Browse Source

添加验证码

lex-xin 3 years ago
parent
commit
48e3246ef1

+ 26 - 2
package-lock.json

@@ -2824,6 +2824,16 @@
         }
       }
     },
+    "clean-deep": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npm.taobao.org/clean-deep/download/clean-deep-3.4.0.tgz",
+      "integrity": "sha1-xGXE3hADrhOhqFnmxpNmq5YGn3U=",
+      "requires": {
+        "lodash.isempty": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.transform": "^4.6.0"
+      }
+    },
     "cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npm.taobao.org/cli-cursor/download/cli-cursor-2.1.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcli-cursor%2Fdownload%2Fcli-cursor-2.1.0.tgz",
@@ -6815,6 +6825,11 @@
         "easy-stack": "^1.0.0"
       }
     },
+    "js-storage": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npm.taobao.org/js-storage/download/js-storage-1.1.0.tgz",
+      "integrity": "sha1-NXyTZO453LaoG0UFxZJqyShx7O4="
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npm.taobao.org/js-tokens/download/js-tokens-4.0.0.tgz",
@@ -7136,6 +7151,16 @@
       "integrity": "sha1-US6b1yHSctlOPTpjZT+hdRZ0HKY=",
       "dev": true
     },
+    "lodash.isempty": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npm.taobao.org/lodash.isempty/download/lodash.isempty-4.4.0.tgz",
+      "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4="
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npm.taobao.org/lodash.isplainobject/download/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+    },
     "lodash.kebabcase": {
       "version": "4.1.1",
       "resolved": "https://registry.npm.taobao.org/lodash.kebabcase/download/lodash.kebabcase-4.1.1.tgz",
@@ -7157,8 +7182,7 @@
     "lodash.transform": {
       "version": "4.6.0",
       "resolved": "https://registry.npm.taobao.org/lodash.transform/download/lodash.transform-4.6.0.tgz",
-      "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=",
-      "dev": true
+      "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A="
     },
     "lodash.uniq": {
       "version": "4.5.0",

+ 2 - 0
package.json

@@ -12,10 +12,12 @@
     "browserslist": "^4.9.1",
     "caniuse": "^0.1.3",
     "caniuse-lite": "^1.0.30001035",
+    "clean-deep": "^3.4.0",
     "core-js": "^2.6.5",
     "dayjs": "^1.8.31",
     "es6-promise": "^4.2.8",
     "install": "^0.13.0",
+    "js-storage": "^1.1.0",
     "qs": "^6.8.0",
     "vant": "^2.5.4",
     "vconsole": "^3.3.4",

+ 22 - 0
src/api/app.js

@@ -2,6 +2,26 @@ const axios = require('@/common/axios').default
 import qs from 'qs'
 // import axios from '@/common/axios'
 const api = '/api-teacher'
+import request from '@/helpers/request'
+// 发送登录短信验证码
+const sendSmsRequest = (data) => {
+  return request({
+      url: '/code/sendSms',
+      method: 'post',
+      requestType: 'form',
+      data
+  })
+}
+// 校验登录图形验证码
+const verifyLoginImage = (data) => {
+  return request({
+      url: '/code/verifyLoginImage',
+      method: 'post',
+      requestType: 'form',
+      data
+  })
+}
+
 // 手机号密码方式登录
 const usernameLogin = (data) => {
   return axios({
@@ -191,6 +211,8 @@ const batchAdd = (data) => {
 
 
 export {
+  sendSmsRequest,
+  verifyLoginImage,
   usernameLogin,
   sendSms,
   smsLogin,

+ 116 - 0
src/components/MImgCode.vue

@@ -0,0 +1,116 @@
+<template>
+    <van-popup v-model="showImgCode" class="imgCodePopup" :close-on-click-overlay="false" @close="onClose" closeable close-icon="close">
+        <div class="imgCode">
+            <p class="codeTitle">输入图形验证码</p>
+            <van-row type="flex">
+                <van-col span="14">
+                    <van-field placeholder="请输入验证码" v-model="code" style="background: #F4F4F4; padding: .12rem .16rem;" />
+                </van-col>
+                <van-col span="10" class="img">
+                    <van-image :src="identifyingCode" @click="updateIdentifyingCode(true)">
+                        <template v-slot:loading>
+                            <van-loading type="spinner" size="20" />
+                        </template>
+                    </van-image>
+                </van-col>
+            </van-row>
+            <van-row>
+                <van-col span="14"></van-col>
+                <van-col span="10">
+                    <span class="imgChange" @click="updateIdentifyingCode(true)">看不清?换一换</span>
+                </van-col>
+            </van-row>
+        </div>
+    </van-popup>
+</template>
+
+<script>
+import { verifyLoginImage, sendSmsRequest } from '@/api/app'
+export default {
+    name: 'imgCode',
+    props: {
+        value: Boolean,
+        phone: String || Number,
+    },
+    data() {
+        let origin = window.location.origin
+        return {
+            showImgCode: this.value,
+            identifyingCode: origin + '/api-teacher/code/getLoginImage?phone=' + this.phone,
+            code: null,
+        }
+    },
+    async mounted() {
+        // let res = await getLoginImage({ phone: 15907121013 })
+        // this.img = res.data
+    },
+    methods: {
+        async updateIdentifyingCode() { // 刷新token
+            let origin = window.location.origin
+            this.identifyingCode = `${origin}/api-teacher/code/getLoginImage?phone=${this.phone}&token=${Math.random()}`
+        },
+        async checkVerifyLoginImage() {
+            try {
+                if(this.code.length < 4) {
+                    return
+                }
+                await verifyLoginImage({ phone: this.phone, code: this.code })
+                await sendSmsRequest({ mobile: this.phone })
+                this.$toast('验证码已发送')
+                this.$emit('input', false)
+                this.$emit('onCodeSend', true)
+            } catch {
+                //
+                this.updateIdentifyingCode()
+            }
+        },
+        onClose() {
+            this.$emit('input', false)
+        }
+    },
+    watch: {
+        code(newVal) {
+            // 图形验证码位数大于4位时
+            if(newVal.length >= 4) {
+                this.checkVerifyLoginImage()
+            }
+        }
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.imgCode {
+    padding: .16rem;
+    .codeTitle {
+        text-align: center;
+        font-size: .16rem;
+        color: #4F4F4F;
+        padding-bottom: .16rem;
+    }
+    .img {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+    .imgChange {
+        display: block;
+        color: #AAAAAA;
+        font-size: 0.12rem;
+        text-align: center;
+        padding-top: .05rem;
+    }
+}
+
+.imgCodePopup {
+    width: 90%;
+    border-radius: .05rem;
+    overflow: inherit;
+    /deep/.van-popup__close-icon {
+        top: -37px !important;
+        right: 0 !important;
+        font-size: .25rem;
+        color: #fff;
+    }
+}
+</style>

+ 7 - 0
src/helpers/HELPERS_README.md

@@ -0,0 +1,7 @@
+##### request
+
+1. 默认添加baseURL为`/api-student`
+1. 默认设置有loading,`hideLoading: true`可关闭loading,错误情况会有Toast提示`hint: true`可关闭提示
+1. 默认为除baseURL为`/api-auth`添加登录信息
+1. 仅接口响应code为200是视为成功,其它均失败
+

+ 56 - 0
src/helpers/request.js

@@ -0,0 +1,56 @@
+import qs from 'qs'
+import axios from 'axios'
+import { Toast } from 'vant'
+import cleanDeep from 'clean-deep'
+import { sessionStorage as storage } from 'js-storage'
+import setLoading from '@/utils/loading'
+const instance = axios.create({
+  baseURL: '/api-teacher'
+})
+
+instance.interceptors.request.use(config => {
+  if (config.hideLoading !== true) {
+    setLoading(true)
+  }
+  config.data = cleanDeep(config.data)
+  config.params = cleanDeep(config.params)
+
+  if (config.method.toLocaleUpperCase() === 'POST' && config.requestType === 'form') {
+    config.data = qs.stringify(config.data)
+  }
+  const Authorization = storage.get('token') || localStorage.getItem('Authorization') || localStorage.getItem('userInfo')
+  if (config.baseURL !== '/api-auth' && Authorization) {
+    config.headers = {
+      ...config.headers,
+      Authorization,
+    }
+  }
+  return config
+})
+
+instance.interceptors.response.use(res => {
+  if (res.config.hideLoading !== true) {
+    setLoading(false)
+  }
+  if (!(res.status > 200 || res.status < 200)) {
+    if (res.data.code === 200) {
+      return res.data
+    } else if(res.data.code == 100 || res.data.code == 201) { // 支付时会用到的自定交code
+      return res.data
+    } else {
+      if (res.config.hint !== true) {
+        Toast(res.data.msg || '接口返回错误')
+      }
+      return Promise.reject(res.data)
+    }
+  } else {
+    if (res.config.hint !== true) {
+      Toast(res.data.msg || '接口返回错误')
+    }
+    return Promise.reject('网络错误')
+  }
+}, () => {
+  setLoading(false)
+})
+
+export default instance

+ 34 - 0
src/helpers/rules.js

@@ -0,0 +1,34 @@
+import { Toast } from 'vant'
+
+export const phoneRule = /^1(3|4|5|6|7|8|9)\d{9}$/
+
+export const verifyPhone = data => {
+  let options = {
+    value,
+  }
+  if (typeof data === 'string') {
+    options.value = data
+  } else if (typeof data === 'object') {
+    options = {...options, ...data}
+    return false
+  }
+  const {
+    value = '',
+    hint,
+    message
+  } = options
+  let result = true
+  let msg = ''
+  if (!value) {
+    result = false
+    msg = message || '请输入手机号'
+  }
+  if (value.length !== 11) {
+    result = false
+    msg = message || '请输入正确的手机号'
+  }
+  if (hint !== false && msg) {
+    Toast(msg)
+  }
+  return result
+}

+ 28 - 0
src/helpers/utils.js

@@ -0,0 +1,28 @@
+export const setTongjiTag = event => {
+  if (window._czc) {
+    window._czc.push(event)
+  }
+}
+
+export const sendUploadMessage = data => {
+  return {
+    api: '',
+    payload: {}, // 参数
+    url: '', //提交url, 为空则app不提交数据仅上传视频,并返回视频url
+    callbackUrl: '', // 有就跳转没有就关闭
+    reload: false,  // 是否刷新当前页面,url为空时需要确保为false
+    ...data
+  }
+}
+
+// 身份证号(支持15位|18位)
+export const patternCard = /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/
+
+// 校验身份证号
+export function validateCardNo (value) {
+    let result = true
+    if(!patternCard.test(value)) {
+        result = false
+    }
+    return result
+}

+ 2 - 2
src/views/app/Account.vue

@@ -39,7 +39,7 @@
 <script>
 /* eslint-disable */
 import MHeader from '@/components/MHeader'
-import { browser, calcMinute }  from '@/common/common'
+import { browser }  from '@/common/common'
 import { sysTenantAccountGet } from '@/api/app'
 export default {
     name: 'courseApply',
@@ -57,7 +57,7 @@ export default {
             localStorage.setItem('Authorization', decodeURI(params.Authorization))
             localStorage.setItem('userInfo', decodeURI(params.Authorization))
         }
-        
+
         document.title = '我的账户'
         if(browser().android || browser().iPhone) {
             this.headerStatus = false

+ 25 - 15
src/views/app/AppLogin.vue

@@ -36,14 +36,21 @@
             <van-button round size="large" @click="onCodeLogin" :disabled="codeDisable">登录</van-button>
             <span class="login-change" @click="isTrue = true">密码登录</span>
             <span class="login-change" @click="onRegister" style="float: left;">立即注册</span>
+
+            <!-- 图形验证码 -->
+            <m-img-code v-if="showImgCode" v-model="showImgCode" :phone="phoneNumber" @onCodeSend="onCodeSend" />
         </div>
     </div>
 </template>
 <script>
 /* eslint-disable */
 import { usernameLogin, sendSms, verifySmsCode, smsLogin, queryUserByPhone } from '@/api/app'
+import MImgCode from '@/components/MImgCode'
 export default {
     name: 'login',
+    components: {
+        MImgCode
+    },
     data() {
         return {
             groupId: this.$route.query.groupId,
@@ -56,7 +63,8 @@ export default {
             code: null,
             codeText: '获取验证码',
             countDownTime: 1000 * 120, // 倒计时时间
-            isClick: false
+            isClick: false,
+            showImgCode: false
         }
     },
     mounted() {
@@ -94,22 +102,22 @@ export default {
             if(!this.checkPhone(this.phoneNumber)) {
                 return
             }
-
             queryUserByPhone({ mobile: this.phoneNumber }).then(res => {
                 let result = res.data
                 if(result.code == 200) {
                     if(result.data == 1) {
-                        sendSms({
-                            mobile: this.phoneNumber
-                        }).then(res => {
-                            let result = res.data
-                            if(result.code == 200) {
-                                this.countDownStatus = false
-                                this.$refs.countdown.start() // 倒计时开始
-                            } else {
-                                this.$toast(result.msg)
-                            }
-                        })  
+                        // sendSms({
+                        //     mobile: this.phoneNumber
+                        // }).then(res => {
+                        //     let result = res.data
+                        //     if(result.code == 200) {
+                        //         this.countDownStatus = false
+                        //         this.$refs.countdown.start() // 倒计时开始
+                        //     } else {
+                        //         this.$toast(result.msg)
+                        //     }
+                        // })
+                        this.showImgCode = true
                     } else {
                         this.$dialog.confirm({
                             message: '此手机号尚未注册<br/>是否注册?',
@@ -127,8 +135,10 @@ export default {
                     this.$toast(result.msg)
                 }
             })
-
-                      
+        },
+        onCodeSend() {
+            this.countDownStatus = false
+            this.$refs.countdown.start() // 倒计时开始
         },
         onCodeLogin() { // 短信登录
             if(this.isClick) {

+ 26 - 16
src/views/app/AppRegister.vue

@@ -26,13 +26,20 @@
             </div>
             <van-button round size="large" @click="onCodeLogin" >注册并登录</van-button>
             <span class="login-change" @click="onUrlLogin">登录</span>
+
+            <!-- 图形验证码 -->
+            <m-img-code v-if="showImgCode" v-model="showImgCode" :phone="phoneNumber" @onCodeSend="onCodeSend" />
         </div>
     </div>
 </template>
 <script>
-import {sendSms, smsLogin, queryUserByPhone } from '@/api/app'
+import {smsLogin, queryUserByPhone } from '@/api/app'
+import MImgCode from '@/components/MImgCode'
 export default {
     name: 'login',
+    components: {
+        MImgCode
+    },
     data() {
         let query = localStorage.getItem('getRegister')
         query = query ? JSON.parse(query) : {}
@@ -47,7 +54,8 @@ export default {
             codeText: '获取验证码',
             countDownTime: 1000 * 120, // 倒计时时间
             isClick: false,
-            agreeStatus: query.agreeStatus ? query.agreeStatus : false
+            agreeStatus: query.agreeStatus ? query.agreeStatus : false,
+            showImgCode: false
         }
     },
     mounted() {
@@ -96,21 +104,25 @@ export default {
                         this.$toast('用户已存在')
                         return
                     } else {
-                        sendSms({
-                            mobile: this.phoneNumber
-                        }).then(res => {
-                            let result = res.data
-                            if(result.code == 200) {
-                                this.countDownStatus = false
-                                this.$refs.countdown.start() // 倒计时开始
-                            } else {
-                                this.$toast(result.msg)
-                            }
-                        })
+                        // sendSms({
+                        //     mobile: this.phoneNumber
+                        // }).then(res => {
+                        //     let result = res.data
+                        //     if(result.code == 200) {
+                        //         this.countDownStatus = false
+                        //         this.$refs.countdown.start() // 倒计时开始
+                        //     } else {
+                        //         this.$toast(result.msg)
+                        //     }
+                        // })
+                        this.showImgCode = true
                     }
                 }
             })
-            
+        },
+        onCodeSend() {
+            this.countDownStatus = false
+            this.$refs.countdown.start() // 倒计时开始
         },
         onCodeLogin() { // 短信登录
             if(!this.phoneNumber) {
@@ -146,7 +158,6 @@ export default {
                         this.onLogin()
                     }
                 }
-                
             })
         },
         onLogin() {
@@ -165,7 +176,6 @@ export default {
                 if(s.code == 200) {
                     let auth = s.data.authentication
                     localStorage.setItem('userInfo', auth.token_type + ' ' + auth.access_token)
-                    
                     if(s.password) {
                         this.$router.push({
                             path: '/classDetail',