【vue+pc端】实现微信扫码登录pc端,公众号开发,前端生成二维码+短信验证+按钮置灰60秒+处理token,全流程详细解说

如何用开发者账号开通权限以及配置路径等都是后端实现的,我这里主要讲前端要做的工作

首先是登录页,原先已经有输入账号和密码的常规登录模式,现在加一个微信的图标,点击后切换为登录页,效果如下

在这里插入图片描述
一、因为是在一个页面上切换,所以原登录的模块和微信扫码页用变量控制切换,重点看微信扫码,

<template>
  <div class="mainWrapper">
    <div v-show="passwordLoginFlag" class="main">
     (正常登录的逻辑,此处省略。。。。。)
       <a-row class="user-layout-login wxlogin">
        <a-col :span="10">社交账号登录</a-col>
        <a-col :span="8" @click="getqrcode()">
          <img src="@/assets/icons/icon_wx.png" alt="" /><span class="wxpay">微信</span>
        </a-col>
      </a-row>
    </div>


    <div v-show="wxLoginFlag">
      <div class="wx_dialog_toiast">
        <div class="wx_dialog_dl">微信登录</div>
        <div id="qrcode" class="qr-code"></div>
        <div class="wx_dialog_sm">请使用微信扫描二维码登录"xxx"</div>
        <div class="wx_dialog_success" v-if="successFlag">登录成功</div>
      </div>
    </div>
    <wx-login-modules ref="wxLoginModules" />
  </div>
</template>

(备注:新建这篇文章的时候是早上,一直到下午四点多我才解决了一个问题然后重新回来编辑,这个问题会在文章中体现,只想说真的是气到无语的一个小错,耽误了我四五个小时,也是提醒大家一定要注意细节!!)

1、安装

控制台--npm install qrcodejs2 --save
页面引入--import QRCode from 'qrcodejs2

2、先有个容器承载二维码,其他的都是样式,

 <div id="qrcode" class="qr-code"></div>

3、点击微信调用方法,调接口res.data是后端设置好的二维码地址,其实就是微信文档上要求的格式,如下图
在这里插入图片描述

import { getWxappid, getWeixinlogin } from '@/api/login.js'
import { mapState } from 'vuex'

  data() {
    return {
      form: this.$form.createForm(this),
      psdVisible: false,
      state: {
        time: 60,
        loginBtn: false,
      },
      passwordLoginFlag: true,
      wxLoginFlag: false,
      successFlag: false,
      settimer: 150,
    }
  },
  computed: {
    ...mapState({
      token: (state) => state.user.token,
    }),
  },
 getqrcode() {
      this.passwordLoginFlag= false//控制密码登录页面隐藏
      this.wxLoginFlag= true//控制微信扫码页面显示
      getWxappid().then((res) => {//获取二维码的接口
         let qrcode = new QRCode('qrcode', {
          width: 190,
          height: 190,
          text: res.data, // 二维码地址
          colorDark: '#000',
          colorLight: '#fff',
        })
        getWeixinlogin().then((res) => {//扫码后登录的接口
        =>authorization_flag字段有三个状态,-1未响应要轮循调用接口不断刷新二维码,0登录成功直接跳转,1登录失败跳到短信验证页去绑定手机号
          if (res && res.code == 0) {
            if (res.authorization_flag == -1) {
              const vm = this
              let timer = setInterval(function () {//设定时器开始循环
                vm.settimer--   
                vm.recirleLogin(timer)//重复调用接口
                if (vm.settimer == 0) {
                  clearInterval(timer)
                  vm.settimer = 150
                }
              }, 2000)
            } else if (res.authorization_flag == 0) {//字段为0的时候,后端会返回token,代表登录成功,可以直接跳到登录后的首页,所以一定要有token
              this.successFlag = true
              this.$ls.set('Access-Token', res.data.token, 12 * 60 * 60 * 1000)//保存token第一步,存到localstroage中,项目里使用了vue.ls插件封装了一下localstorage,我在最下面延申了一下,可以看看
              this.$store.commit('SET_TOKEN', res.data.token)//保存token第二步,保存到vuex中,保存token一般都是本地存储+vuex双存
              this.$router.replace({ name: 'portal' })//跳到登录后的首页
            } else {//字段为1代表未绑定,跳到绑定手机页
              this.successFlag = true
              this.$router.push({ name: 'wx-bingingtel' })
            }
          } else {//如果状态码不是0,代表出现了错误,提示一下错误,重新调一下接口
            this.$message.error(res.msg)
            this.qrcode()
          }
        })
      })
    },
    recirleLogin(timer) {
    这里是当状态为0的时候,定时器调用接口走这里,代表用户一直没有扫码。如果用户扫码了就会走1或者0里面的代码,逻辑和上面是一样的,我有点懒,应该封装一下,重复调用的。
      getWeixinlogin().then((res) => {
        if (res.authorization_flag == 1) {
          this.successFlag = true
          this.wxLoginFlag= false
          clearInterval(timer)
          this.$router.push({ name: 'wx-bingingtel' })
        } else if (res.authorization_flag == 0) {
          this.successFlag = true
          clearInterval(timer)
           this.$ls.set('Access-Token', res.data.token, 12 * 60 * 60 * 1000)//今天花四五个小时才解决的问题就是这个token,明明获取到了,但是登录后一直没有用户信息。原因在于我写的是Access_Token或ACCESS_TOKEN,这就是要注意的细节之处,为了这个花这么久,真不值啊。。。。
          this.$store.commit('SET_TOKEN', res.data.token)
          this.$router.replace({ name: 'portal' })
        }
      })
    },

处理token
在store文件夹下新建a.js文件

export const ACCESS_TOKEN = 'Access-Token'   //还有其他导出,和本文无关,所以只重点突出token的过程

在vuex文件夹的modules文件夹中新建b.js

import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/a.js'
const user = {
  state: {
    token: '',   
  },
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },  
  },
  actions: {}
export default user

二、当扫码后没有微信绑定的要跳转到这个页面
在这里插入图片描述

form表单验证的代码就不贴了,太多了。主要贴一下获取验证码的方法和登录的方法

           <a-button
            class="getCaptcha"
            type="primary"
            :disabled="state.smsSendBtnDisable || state.smsSendBtn"
            @click.stop.prevent="getCaptcha"
            v-text="(!state.smsSendBtn && '获取验证码') || state.time + ' s'"
          />
           <a-button
          size="large"
          type="primary"
          htmlType="submit"
          class="login-button"
          :loading="submitDisabel"
          :disabled="submitDisabel"
        >
          登录
        </a-button>
import { mapActions } from 'vuex'

 data() {
    return {
      isLoginError: false,

      formLayout: 'horizontal',
      form: this.$form.createForm(this),
      state: {
        time: 60,
        smsSendBtnDisable: true,
        smsSendBtn: false,
        passwordIsVisible: false,
        passwordLevel: 0,
        passwordLevelChecked: false,
        percent: 10,
        progressColor: '#FF0000',
      },
      submitDisabel: false,
    }
 // 获取验证码
    getCaptcha(e) {
      e.preventDefault()
      const {
        form: { validateFields },
        state,
        $notification,
      } = this
      validateFields(['mobile'], { force: true }, (err, values) => {
        if (!err) {
          state.smsSendBtn = true
          const interval = window.setInterval(() => {//设置定时器,点击获取验证码开始倒计时60秒并置灰按钮
            if (state.time-- <= 0) {
              state.time = 60
              state.smsSendBtn = false
              window.clearInterval(interval)
            }
          }, 1000)

          getSmsCaptchaTop({ phoneNumber: values.mobile })
            .then((res) => {
              if (res && res.code == 0) {
                $notification['success']({
                  message: '提示',
                  description: res.msg,
                  duration: 5,
                })
              } else {
                $notification['warning']({
                  message: '提示',
                  description: res.msg,
                  duration: 5,
                })
              }
            })
            .catch((err) => {
              clearInterval(interval)
              state.time = 60
              state.smsSendBtn = false
              this.requestFailed(err)
            })
        }
      })
    },
    requestFailed(err) {
      this.$notification['error']({
        message: '错误',
        description: ((err.response || {}).data || {}).message || err.msg || '请求出现错误,请稍后再试',
        duration: 4,
      })
    },
 ...mapActions(['wxLogin']),

    handleSubmit(e) {
      e.preventDefault()
      const {
        form: { validateFields },
        state,
        wxLogin,
        $message,
        $router,
      } = this
      validateFields({ force: true }, (err, values) => {
        if (!err) {
          state.passwordLevelChecked = false
          this.submitDisabel = true

          wxLogin({
            loginName: values.mobile,
            agreementReadFlag: values.agree ? 1 : 0,
            verifyCode: values.captcha,
          })
            .then((res) => this.loginSuccess(res) )
            .catch((err) => {
              this.submitDisabel = false
              this.requestFailed(err)
            }).finally(()=>{
              setTimeout(()=>{
                state.loginBtn = false
              },600)
            })
        }
      })
    },
       loginSuccess(res) {
      if (res.code === 0) {
        this.$router.replace({ name: 'portal' }, () => {
          this.$notification.success({
            message: '欢迎',            
          })
        })
        // this.isLoginError = false
      } else {
        this.requestFailed(res)
      }
    },

注意:wxLogin方法是封装在vuex的b.js文件中的,正常引入接口,然后在antion中定义方法

import { getWxbind } from '@/api/login'

 actions: {
 // 微信登录
    wxLogin({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        getWxbind(userInfo)
          .then(response => {
            const result = response
            Vue.ls.set(ACCESS_TOKEN, result.data.token, 12 * 60 * 60 * 1000)//注意这里是ACCESS_TOKEN
            commit('SET_TOKEN', result.data.token)
            resolve(response)
          })
          .catch(error => {
            reject(error)
          })
      })
    },

}

vue-ls插件延申点:

npm install vue-ls --save

main.js中:
import VueStorage from 'vue-ls'

新建new.js中写入以下代码,然后引入到vuex的js文件中
export default {
  primaryColor: '#48B8FF', // primary color of ant design
  navTheme: 'dark', // theme for nav menu
  layout: 'sidemenu', // nav menu position: sidemenu or topmenu
  contentWidth: 'Fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu
  fixedHeader: false, // sticky header
  fixSiderbar: true, // sticky siderbar
  autoHideHeader: false, //  auto hide header
  colorWeak: false,
  multiTab: false,
  storageOptions: {
    namespace: 'pro__', // key prefix
    name: 'ls', // name variable Vue.[ls] or this.[$ls],
    storage: 'local' // storage name session, local, memory
  }
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>