Преглед на файлове

feat: 注册逻辑(也太繁琐了吧..)

ChenYangfan преди 6 години
родител
ревизия
b871ecce1b

+ 10 - 1
src/components/Background.vue

@@ -53,7 +53,7 @@ export default {}
     }
   }
 
-  @media (min-width: 600px) and (max-width: 1300px){
+  @media (min-width: 600px) and (max-width: 1400px){
     .mountain {
       transform-origin: bottom;
       transform: scale(.7);
@@ -75,4 +75,13 @@ export default {}
     }
   }
 }
+
+@media (max-height: 700px) {
+  .mountain {
+    display: none;
+  }
+  .sun {
+    display: none;
+  }
+}
 </style>

+ 15 - 2
src/components/CButton.vue

@@ -1,5 +1,5 @@
 <template>
-  <button class="c-button" @click="$emit('click')">
+  <button class="c-button" :class="disabled ? 'disabled' : ''" @click="$emit('click')">
     <span v-if="icon" class="icon">{{icon}}</span>
     <slot></slot>
   </button>
@@ -7,7 +7,7 @@
 
 <script>
 export default {
-  props: ['icon']
+  props: ['icon', 'disabled']
 }
 </script>
 
@@ -77,4 +77,17 @@ button {
     background:  darken($theme-red-transparent, 10);
   }
 }
+
+.disabled {
+  background: #daeafa;
+  color: white;
+  cursor: not-allowed;
+  &:hover {
+    background: #cadaea;
+  }
+
+  &:active {
+    background: #cadaea;
+  }
+}
 </style>

+ 15 - 4
src/components/CInput.vue

@@ -2,8 +2,8 @@
   <div class="c-input">
     <label>
       <component
-        :is="type"
-        type="text"
+        :is="inputType"
+        :type="type ? type : 'text'"
         :placeholder="placeholder"
         :value="value"
         @input="$emit('input', $event.target.value)"
@@ -21,11 +21,12 @@ export default {
   props: {
     value: String,
     placeholder: String,
-    textarea: String
+    textarea: String,
+    type: String
   },
   data () {
     return {
-      type: this.textarea ? 'textarea' : 'input'
+      inputType: this.textarea ? 'textarea' : 'input'
     }
   }
 }
@@ -56,4 +57,14 @@ textarea {
   resize: none;
 }
 
+// 去掉数字输入框中的调整数字按键
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
+
+input[type=number] {
+  -moz-appearance:textfield;
+}
 </style>

+ 8 - 0
src/main.js

@@ -5,10 +5,18 @@ import './registerServiceWorker'
 import router from './router'
 import uuid from 'uuid'
 import store from './store'
+import { SET_MESSAGE } from '@/store/types/message-types'
 
 Vue.config.productionTip = false
 Vue.prototype.$uuid = uuid
 
+Vue.prototype.$showMessage = function ({ message, type = 'info' }) {
+  this.$store.commit(SET_MESSAGE, {
+    message: message,
+    type: type
+  })
+}
+
 new Vue({
   router,
   store,

+ 1 - 1
src/plugins/axios.js

@@ -61,4 +61,4 @@ Plugin.install = function (Vue, options) {
 
 Vue.use(Plugin)
 
-export default axios
+export default _axios

+ 19 - 6
src/server/code.js

@@ -3,8 +3,11 @@ import {
 } from '@/config/server-info'
 import httpCode from '@/server/enums/http-code'
 import axios from '@/plugins/axios'
+import showMessage from '@/server/utils/show-message'
+import catchHandler from '@/server/utils/catch-handler'
+import showErrorMessage from '@/server/utils/show-error-message'
 
-export function sendEmailCode(email) {
+export function sendEmailCode (email) {
   return axios
     .post(useCodeServer('/email'), null, {
       params: {
@@ -14,14 +17,19 @@ export function sendEmailCode(email) {
     .then(res => {
       const emptyResponse = res.data
       if (emptyResponse.code === httpCode.success) {
-
+        showMessage({
+          message: '邮箱验证码发送成功',
+          type: 'success'
+        })
       } else {
-        // TODO
+        showErrorMessage(emptyResponse.msg)
       }
+    }).catch(() => {
+      catchHandler()
     })
 }
 
-export function sendPhoneCode(phone) {
+export function sendPhoneCode (phone) {
   return axios
     .post(useCodeServer('/phone'), null, {
       params: {
@@ -31,9 +39,14 @@ export function sendPhoneCode(phone) {
     .then(res => {
       const emptyResponse = res.data
       if (emptyResponse.code === httpCode.success) {
-
+        showMessage({
+          message: '手机验证码发送成功',
+          type: 'success'
+        })
       } else {
-        // TODO
+        showErrorMessage(emptyResponse.msg)
       }
+    }).catch(() => {
+      catchHandler()
     })
 }

+ 10 - 1
src/server/user.js

@@ -3,6 +3,9 @@ import {
 } from '@/config/server-info'
 import httpCode from '@/server/enums/http-code'
 import axios from '@/plugins/axios'
+import showMessage from '@/server/utils/show-message'
+import showErrorMessage from '@/server/utils/show-error-message'
+import catchHandler from '@/server/utils/catch-handler'
 
 export function register({ name, email, emailCode, phone, phoneCode, password }) {
   const data = {
@@ -18,10 +21,16 @@ export function register({ name, email, emailCode, phone, phoneCode, password })
     .then(res => {
       const resourceResponse = res.data
       if (resourceResponse.code === httpCode.success) {
+        showMessage({
+          message: '注册成功',
+          type: 'success'
+        })
         return resourceResponse.data
       } else {
-        // TODO
+        showErrorMessage(resourceResponse.msg)
       }
+    }).catch(() => {
+      catchHandler()
     })
 }
 

+ 8 - 0
src/server/utils/catch-handler.js

@@ -0,0 +1,8 @@
+import showMessage from '@/server/utils/show-message'
+
+export default function catchHandler () {
+  showMessage({
+    message: '请检查网络情况,若网络正常请联系管理员:chenyangfan1998@icloud.com',
+    type: 'error'
+  })
+}

+ 8 - 0
src/server/utils/show-error-message.js

@@ -0,0 +1,8 @@
+import showMessage from '@/server/utils/show-message'
+
+export default function showErrorMessage (msg) {
+  showMessage({
+    message: msg,
+    type: 'error'
+  })
+}

+ 8 - 0
src/server/utils/show-message.js

@@ -0,0 +1,8 @@
+import store from '@/store/index'
+import { SET_MESSAGE } from '@/store/types/message-types'
+
+const showMessage = function ({ message, type = 'info' }) {
+  store.commit(SET_MESSAGE, { message, type })
+}
+
+export default showMessage

+ 6 - 11
src/views/Home.vue

@@ -4,8 +4,8 @@
       <h1 class="title">
         SEEC 课堂助手
       </h1>
-      <c-button @click="handleLoginButtonClick" class="full-width mb-12">去登录</c-button>
-      <c-button class="secondary full-width" @click="showMessage">去注册</c-button>
+      <c-button @click="handleLogin" class="full-width mb-12">去登录</c-button>
+      <c-button class="secondary full-width" @click="handleRegister">去注册</c-button>
     </div>
     <background></background>
   </div>
@@ -14,20 +14,15 @@
 <script>
 import CButton from '@/components/CButton'
 import Background from '@/components/Background'
-import { SET_MESSAGE } from '@/store/types/message-types'
 
 export default {
   components: { CButton, Background },
   methods: {
-    handleLoginButtonClick () {
-      this.$router.push('/login')
+    handleLogin () {
+      this.$router.push({ name: 'login' })
     },
-    showMessage () {
-      // todo
-      this.$store.commit(SET_MESSAGE, {
-        message: '消息提醒测试',
-        type: 'info'
-      })
+    handleRegister () {
+      this.$router.push({ name: 'register' })
     }
   }
 }

+ 19 - 17
src/views/login/Login.vue

@@ -1,12 +1,12 @@
 <template>
   <div class="login-view-container">
     <div>
-      <h1><span>SEEC</span> 用户登录</h1>
-      <c-input placeholder="用户名"></c-input>
+      <h1 class="title"><span>SEEC</span> 用户登录</h1>
+      <c-input placeholder="邮箱"></c-input>
       <c-input placeholder="密码"></c-input>
       <div class="button-area">
         <c-button @click="handleSubmit">确认</c-button>
-        <c-button class="secondary">帮助</c-button>
+        <c-button class="secondary" @click="handleToRegister">去注册</c-button>
       </div>
       <background></background>
     </div>
@@ -17,41 +17,41 @@
 import CInput from '@/components/CInput'
 import CButton from '@/components/CButton'
 import Background from '@/components/Background'
+
 export default {
   components: { CButton, CInput, Background },
   methods: {
     handleSubmit () {
       this.$router.push('/teacher')
+    },
+    handleToRegister () {
+      this.$router.push({ name: 'register' })
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
-h1 {
-  color: #3a4a5a;
-  font-size: 28px;
-  font-weight: bold;
-  margin-bottom: 32px;
-
-  & > span {
-    font-family: 'Courier New', 'Courier', monospace;
-    font-size: 1.15em;
-  }
-}
-
 .login-view-container {
-  height: 95vh;
+  min-height: 100vh;
   display: flex;
   align-items: center;
   justify-content: center;
 
   & > div {
-    max-width: 260px;
+    max-width: 360px;
+    padding: 60px 24px;
     width: 100%;
   }
 }
 
+.title {
+  span {
+    font-family: 'Courier New', 'Courier', monospace;
+    font-size: 1.15em;
+  }
+}
+
 .c-input {
   margin-bottom: 24px;
   background: #f2f6fa;
@@ -60,7 +60,9 @@ h1 {
 .button-area {
   display: flex;
   margin: 0 -8px;
+
   & > button {
+    flex: 1;
     margin: 0 8px;
   }
 }

+ 155 - 33
src/views/register/Register.vue

@@ -7,41 +7,53 @@
           登录信息
           <span>1/2</span>
         </div>
-        <c-input placeholder="邮箱" v-model="email"></c-input>
+        <c-input placeholder="邮箱" type="email" v-model="email"></c-input>
         <div class="verified-code-wrapper">
-          <c-input placeholder="邮箱验证码" v-model="emailCode"></c-input>
-          <c-button class="secondary" @click="handleSendEmailCode">发送验证码</c-button>
+          <c-input placeholder="邮箱验证码" type="number" v-model="emailCode"></c-input>
+          <c-button class="secondary" :disabled="typeof this.emailButtonContent === typeof 0"
+                    @click="handleSendEmailCode">{{ emailButtonContent }}
+          </c-button>
         </div>
         <section class="tip mb-24">
           <div>关于邮箱的说明:</div>
-          <div>1. 邮箱需为南京大学校邮</div>
+          <div>1. 邮箱需为南京大学校邮(@nju.edu.cn | @smail.nju.edu.cn)</div>
           <div>2. 系统根据邮箱判定您的身份为教师还是学生</div>
         </section>
-        <c-input placeholder="密码" v-model="password"></c-input>
-        <c-input placeholder="确认密码" v-model="passwordEnsure"></c-input>
-        <c-button class="secondary full-width mb-16" @click="step = 2">下一步</c-button>
+        <c-input placeholder="密码" type="password" v-model="password"></c-input>
+        <c-input placeholder="确认密码" type="password" v-model="passwordEnsure"></c-input>
+        <section class="tip absolute password-warning" v-if="passwordWarning">
+          <div>{{passwordWarning}}</div>
+        </section>
+        <section class="tip absolute password-checked" v-if="password && passwordEnsure && !passwordWarning">
+          <div>{{iconMap['success']}} 密码验证通过</div>
+        </section>
+        <c-button class="secondary full-width mb-16 next-step" @click="step = 2">下一步</c-button>
       </section>
-      <section class="step=2"  v-show="step === 2">
-        <section class="tip">
-          <div>已填写的邮箱:{{}}</div>
+      <section class="step=2" v-show="step === 2">
+        <section class="tip mb-8">
+          <div>已填写的邮箱:{{email}}</div>
         </section>
         <c-button @click="step = 1" class="warning full-width mb-16">上一步</c-button>
         <div class="sub-title">
           其他信息
           <span>2/2</span>
         </div>
-        <c-input placeholder="姓名"></c-input>
-        <section class="tip">
+        <c-input placeholder="姓名" v-model="name"></c-input>
+        <section class="tip mb-16">
           <div>请使用真实姓名,便于学生、老师之间辨认</div>
         </section>
-        <c-input placeholder="手机"></c-input>
+        <c-input placeholder="手机" type="number" v-model="phone"></c-input>
         <div class="verified-code-wrapper">
-          <c-input placeholder="手机验证码"></c-input>
-          <c-button class="secondary">发送验证码</c-button>
+          <c-input placeholder="手机验证码" type="number" v-model="phoneCode"></c-input>
+          <c-button class="secondary" @click="handleSendPhoneCode"
+                    :disabled="typeof this.phoneButtonContent === typeof 0">{{phoneButtonContent}}
+          </c-button>
         </div>
+        <section class="tip mb-16">
+          <div>重要提示:测试期间手机号验证暂未开通,请填写任意6位数字验证码即可通过验证</div>
+        </section>
         <div class="button-area">
-          <c-button @click="handleSubmit">确认</c-button>
-          <c-button class="secondary">帮助</c-button>
+          <c-button class="full-width" :disabled="!formChecked()" @click="handleSubmit">确认</c-button>
         </div>
       </section>
       <background></background>
@@ -53,30 +65,136 @@
 import CInput from '@/components/CInput'
 import CButton from '@/components/CButton'
 import Background from '@/components/Background'
+import { sendEmailCode, sendPhoneCode } from '@/server/code'
+import { register } from '@/server/user'
+import iconMap from '@/utils/icon-map'
 
 export default {
   components: { CButton, CInput, Background },
   data () {
     return {
+      iconMap: iconMap,
       step: 1,
       email: '',
       emailCode: '',
+      emailButtonContent: '发送验证码',
+      phoneButtonContent: '发送验证码',
       password: '',
       passwordEnsure: '',
+      passwordWarning: '',
       name: '',
       phone: '',
-      phoneCode: ''
+      phoneCode: '',
+      message: ''
+    }
+  },
+  watch: {
+    password (newValue) {
+      this.passwordWarning = newValue && this.passwordEnsure && this.password !== this.passwordEnsure ? '两次输入的密码不一致' : ''
+    },
+    passwordEnsure (newValue) {
+      this.passwordWarning = newValue && this.password && this.password !== this.passwordEnsure ? '两次输入的密码不一致' : ''
     }
   },
   methods: {
+    formChecked () {
+      if (!(this.email && this.emailCode && this.password && this.passwordEnsure)) {
+        this.message = '上一页信息尚未完全填写'
+        return false
+      }
+
+      if (!(this.name && this.phone && this.phoneCode)) {
+        this.message = '本页信息尚未完全填写'
+        return false
+      }
+
+      if (this.password !== this.passwordEnsure) {
+        this.message = '两次输入的密码不一致'
+        return false
+      }
+
+      if (this.password !== this.passwordEnsure) {
+        this.message = '两次输入的密码不一致'
+        return false
+      }
+      return true
+    },
     handleSubmit () {
-      this.$router.push('/teacher')
+      if (!this.formChecked()) {
+        this.$showMessage({ message: this.message, type: 'error' })
+      } else {
+        register({
+          name: this.name,
+          email: this.email,
+          emailCode: this.emailCode,
+          phone: this.phone,
+          phoneCode: this.phoneCode,
+          password: this.password
+        }).then(() => {
+          this.$router.push({ name: 'login' })
+        })
+      }
+    },
+    handleSendEmailCode () {
+      this.$showMessage({ message: '正在尝试发送验证码', type: 'info' })
+      if (this.email.match(/.+@(nju.edu.cn|smail.nju.edu.cn)/)) {
+        this.setEmailCodeCd()
+        sendEmailCode(this.email)
+      } else {
+        this.$showMessage({ message: '本邮箱不符合南京大学校邮格式', type: 'error' })
+      }
+    },
+    setEmailCodeCd () {
+      this._emailCodeCdInterval && clearInterval(this._emailCodeCdInterval)
+      this.emailButtonContent = 60
+      this._emailCodeCdInterval = setInterval(() => {
+        this.emailButtonContent--
+        if (typeof this.emailButtonContent !== typeof 0 || this.emailButtonContent <= 0) {
+          clearInterval(this._emailCodeCdInterval)
+          this.emailButtonContent = '发送验证码'
+        }
+      }, 1000)
+    },
+    handleSendPhoneCode () {
+      this.$showMessage({ message: '正在尝试发送验证码', type: 'info' })
+      if (this.phone.match(/[0-9]{11}/)) {
+        this.setPhoneCodeCd()
+        sendPhoneCode(this.phone)
+      } else {
+        this.$showMessage({ message: '手机号码应为大陆11位手机号码', type: 'error' })
+      }
+    },
+    setPhoneCodeCd () {
+      this._phoneCodeCdInterval && clearInterval(this._phoneCodeCdInterval)
+      this.phoneButtonContent = 60
+      this._phoneCodeCdInterval = setInterval(() => {
+        this.phoneButtonContent--
+        if (typeof this.phoneButtonContent !== typeof 0 || this.phoneButtonContent <= 0) {
+          clearInterval(this._phoneCodeCdInterval)
+          this._phoneCodeCdInterval = '发送验证码'
+        }
+      }, 1000)
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
+@import "~@/style/theme.scss";
+
+.login-view-container {
+  min-height: 100vh;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  & > div {
+    max-width: 360px;
+    padding: 60px 24px;
+    width: 100%;
+  }
+}
+
 .title {
   span {
     font-family: 'Courier New', 'Courier', monospace;
@@ -99,22 +217,8 @@ export default {
   font-size: 12px;
 }
 
-.login-view-container {
-  height: 95vh;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  & > div {
-    max-width: 360px;
-    padding: 0 24px;
-    width: 100%;
-  }
-}
-
 .c-input {
   margin-bottom: 16px;
-  background: #f2f6fa;
 }
 
 .button-area {
@@ -142,4 +246,22 @@ export default {
     margin-left: 12px;
   }
 }
+
+.next-step {
+  margin-top: 56px;
+}
+
+.absolute {
+  position: absolute;
+}
+
+.password-warning {
+  color: $theme-orange;
+  font-family: 'iconfont', sans-serif;
+}
+
+.password-checked {
+  color: $theme-green;
+  font-family: 'iconfont', sans-serif;
+}
 </style>