Uni-app 手机号+验证码登录 & 用户名密码登录登出

  • 用户名密码登录,手机号登录
    • 用户名密码登录:用了uniapp+uview的$u.debounce防抖方法,再发送请求
  • 判断正则以及同意条款勾选后,发起登录请求(参数是用户名和密码),如果请求成功——setStorage存储aes加密了的token,同时跳转页面回到主页mine.vue
  • 在mine.vue主页中,onShow中判断:如果现在有存储的token了,但是vuex中没有用户信息,就调用getUserInfo方法——发起获取用户信息的请求(请求头中带着解密的token),如果请求成功——将请求到的用户信息存储到vuex中,并在页面中渲染及使用
  • 在App.vue中, onLaunch判断(重新打开app时,vuex已经销毁了):如果现在有存储的token(永久的,必然有),就调用getUserInfo方法,存到vuex中。目的:只要曾经登录过,重启App时,不进入mine.vue,也方便用户的学习、购买等操作

mine.vue > mine (tab页) 在此页面点击登陆跳转到login.vue

<template>
  <view class="mine">
    <u-navbar :is-back="false" :background="background" :border-bottom="false">
      <view slot="right">
        <view class="right-content">
          <view class="right-setup iconfont icon-shezhi"></view>
          <view class="right-sweep iconfont icon-saoyisao"></view>
        </view>
      </view>
    </u-navbar>
    <!-- 登录状态 -->
    <view class="mine-header" v-if="isLogin">
      <view class="mine-img" @tap="goProfile">
        <!-- /static/me/avatar.jpeg -->
        <image mode=""></image>
      </view>
      <view class="mine-right">
        <view class="login">
          {{userInfo.nickName}}
          <image :src="userInfo.avatar" mode=""></image>
        </view>
        <view class="tip" @tap="goProfile">编辑资料</view>
      </view>
    </view>
    <!-- 未登录状态 -->
    <view class="mine-header" v-else>
      <view class="mine-img">
        <image src="/static/me/avatar.png" mode=""></image>
      </view>
      <view class="mine-right" @tap="goLogin">
        <view class="login">点击登陆</view>
        <view class="tip">登陆后同步数据,学习更放心</view>
      </view>
    </view>
    <view class="mine-content">
      <Member></Member>
      <!-- <MeList></MeList> -->
      <u-cell-group :border-top="false" class="list-top">
        <u-cell-item title="购物车" @tap="goCart()" :title-style="title">
          <view slot="icon" class="slot-icon iconfont icon-gouwuche"></view>
        </u-cell-item>
        <u-cell-item title="我的订单" @tap="goOrder()" :title-style="title">
          <view slot="icon" class="slot-icon iconfont icon-dingdan"></view>
        </u-cell-item>
        <u-cell-item title="电子兑换码" @tap="goChange()" :title-style="title">
          <view slot="icon" class="slot-icon iconfont icon-liwu"></view>
        </u-cell-item>
      </u-cell-group>

      <u-cell-group class="list-top">
        <u-cell-item title="关于我们" @tap="goAbout()" :title-style="title">
          <image slot="icon" src="/static/me/small.png" mode="" class="slot-image"></image>
        </u-cell-item>
      </u-cell-group>
      <!-- 退出登录 -->
      <view class="logout" @tap="loginOut">退出登录</view>
    </view>
    <u-toast ref="uToast" />
    <tabbar currentPath="mine"></tabbar>
  </view>
</template>

<script>
  import Tabbar from '@/common/tabbar/index.vue'
  import Member from '@/components/me/Memebar-nav.vue';
  import api from '@/service/mine.js'
  import {
    mapState,
    mapActions
  } from 'vuex'
  export default {
    data() {
      return {
        background: {
          backgroundColor: '#f5f5f5'
        },
        isLogin: false,
        title: {
          color: '#333'
        },
      }
    },
    onShow() {
      uni.hideTabBar();
      //每次进入页面判断是否已有用户信息
      if(uni.getStorageSync('token')){//前提存了token
        if (!this.userInfo) {//如果vuex没有用户信息 就发起请求获取用户信息存进vuex
          this.getUserInfo();
        }else{//如果vuex有用户信息 就显示登录状态
          this.isLogin = true
        }
      }else{//前提没存token
        this.isLogin = false
      }
      
    },
    components: {
      Tabbar,
      Member
    },
    computed: {
      ...mapState({
        userInfo: state => state.user.userInfo
      })
    },
    methods: {
      ...mapActions(['changeUserInfoActions']),
      //如果现在有token(token是登录成功后存储的)了,就发起获取用户信息的请求,并将用户信息存储到vuex中
      getUserInfo() {      
          api.getUserInfo().then(res => {
            //console.log(res)
            if (res.meta.code === '200') {
              this.isLogin = true //变成已登录状态
              let info = res.data.data
              //若有昵称和头像,就走你的昵称和头像;若没有,就走默认的
              info.nickName = info.nickName ? info.nickName : '默认昵称'
              info.avatar = info.avatar ? info.avatar : '/static/me/avatar.jpeg'
              //将用户信息存储到vuex中
              this.changeUserInfoActions(info)
            } else {
              this.$refs.uToast.show({
                title: res.meta.msg,
                type: 'warning',
                icon: false
              })
            }
          }).catch(err => {
            console.log(err)
            this.$refs.uToast.show({
              title: err.meta.msg,
              type: 'error',
              icon: false
            })
          })
      },
      goCart() {
        console.log('我的购物车被点击了')
      },
      goOrder() {
        console.log('我的订单被点击了')
      },
      goChange() {
        console.log('电子兑换码被点击了')
      },
      goAbout() {
        console.log('关于我们被点击了')
      },
      goLogin() {
        uni.redirectTo({
          url: '../login/login'
        })
      },
      //退出登录
      loginOut() {
        api.getlogout().then(res => {
          if (res.meta.code === '200') {
            this.$refs.uToast.show({
              title: '退出登录成功',
              type: 'success'
            })
            //清空vuex中用户信息数据
            this.changeUserInfoActions()
            //移除token
            uni.removeStorageSync('token')
            this.isLogin = false
          }
        })
      },
      //个人资料
      goProfile() {
        uni.navigateTo({
          url:'../profile/profile'
        })
      }
    }
  }
</script>

mine.js > 接口

import $http from './request.js'
import {Decrypt} from'@/utils/aes/aes.js'
 
export default {
  //获取用户信息
  getUserInfo() {//拿到对象中mobile对应的值
    return $http.request({
      url: '/member/getInfo',
      method: 'GET',
      header:{
      	"Content-Type": "application/json",
      	"Authorization": Decrypt(uni.getStorageSync('token'))
      }
    })
  }
}

login.vue > 登录页 此页面点击手机登陆跳转到code.vue

<template>
  <view class="wrap">
    <view class="top"></view>
    <view class="content">
      <view class="content-header">
        <image src="/static/login/logo.png"></image>
        <view class="title">欢迎登录</view>
      </view>
      <block v-if="isPhone">
        <input class="u-border-bottom" type="number" v-model="tel" placeholder="请输入手机号" />
      </block>
      <block v-else>
        <input class="u-border-bottom" v-model="username" placeholder="请输入用户名" type="username" />
        <input class="u-border-bottom" v-model="password" placeholder="请输入密码" type="password" />
      </block>

      <view class="alternative">
        <u-checkbox-group>
          <u-checkbox v-model="checked" shape="circle">
            <view class="">
              我已阅读并同意<text class="link">用户服务条款</text>和<text class="link">隐私政策</text>
            </view>
          </u-checkbox>
        </u-checkbox-group>
        <u-toast ref="uToast" />
      </view>
      <button @tap="phoneSubmit" :style="[inputStyle]" class="getCaptcha" v-if="isPhone">获取短信验证码</button>
      <button @tap="userSubtmit" :style="[inputUserStyle]" class="getCaptcha" v-else>登录</button>
      <view class="alternative">
        <view class="password"></view>
        <view class="issue" @tap="goChange">{{tips}}</view>
      </view>
    </view>
  </view>
</template>

<script>
  import {
    Encrypt
  } from '../../utils/aes/aes.js'
  import api from '@/service/login.js'
  export default {
    data() {
      return {
        isPhone: true, //手机登录
        tel: '',
        checked: false, //同意条款
        username: '',
        password: '',
        tips: '用户名密码登录'
      }
    },
    computed: {
      inputStyle() {
        let style = {};
        if (this.tel) {
          style.color = "#fff";
          style.backgroundColor = 'rgba(51,122,255,1)'
        }
        return style;
      },
      inputUserStyle() {
        let style = {};
        if (this.username && this.password) {
          style.color = "#fff";
          style.backgroundColor = 'rgba(51,122,255,1)'
        }
        return style;
      }
    },
    methods: {
      //提交手机号登录
      phoneSubmit() {
        if (this.checked) { //如果勾选同意
          let reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
          if (this.$u.test.mobile(this.tel) && reg.test(this.tel)) { //输入的是手机号
            this.$u.route({
              url: 'pages/login/code',
              params: {
                tel: Encrypt(this.tel)
              }
            })
          } else { //输入不是手机号
            this.$refs.uToast.show({
              title: '手机号填写有误',
              type: 'warning'
            })
          }
        } else { //没有勾选同意
          this.$refs.uToast.show({
            title: '请先阅读用户服务条款与隐私政策',
            type: 'warning'
          })
        }

      },
      goChange() {
        if (this.isPhone) { //现在为手机登录
          this.isPhone = false;
          this.tips = '手机号登录'
        } else { //现在为密码登录
          this.isPhone = true;
          this.tips = '用户名密码登录'
        }
      },
      //提交用户名登录 防抖
      userSubtmit() {
        if (this.username && this.password) {
          this.$u.debounce(this.userLogin, 500)
        } else {
          this.$refs.uToast.show({
            title: '用户名或密码不能为空',
            type: 'warning',
            icon: false
          })
        }

      },
      userLogin() {
        if (this.checked) { //如果已勾选
          api.loginByJson({
            username: this.username,
            password: this.password
          }).then(res => {
            console.log(res)
            if (res.meta.code === '10006') {
              //存储token
              uni.setStorageSync('token', Encrypt(res.data.accessToken))
              //跳转页面
              uni.switchTab({
                url: '../mine/mine'
              })
            } else {
              this.$refs.uToast.show({
                title: '登录失败',
                type: 'warning',
                icon: false
              })
            }
          }).catch(err => {
            this.$refs.uToast.show({
              title: err.meta.msg,
              type: 'error',
              icon: false
            })
          })
        }
      }
    }
  };
</script>

code.vue > 输入验证码页面

<template>
  <view class="wrap">
    <view class="key-input">
      <view class="title">输入验证码</view>
      <view class="tips">验证码已发送至 +{{showTel}}</view>
      <u-message-input :focus="true" :value="value" @finish="finish" mode="bottomLine"
        :maxlength="maxlength"></u-message-input>
      <text :class="{ error: error }">验证码错误,请重新输入</text>
      <view class="captcha">
        <!--  -->
        <!-- <text :class="{ noCaptcha: show }" @tap="noCaptcha">收不到验证码点这里</text> -->
        <text :class="{ regain: !show }">{{ second }}秒后重新获取验证码</text>
      </view>
    </view>
    <u-toast ref="uToast" />
  </view>
</template>

<script>
  import {
    Decrypt,
    Encrypt
  } from '@/utils/aes/aes.js'
  import api from '@/service/login.js'
  export default {
    data() {
      return {
        maxlength: 6,
        value: '',
        second: 60,
        show: false,
        error: false,
        mobile: null
      };
    },
    computed: {
      showTel() {
        let reg = /^(\d{3})\d{4}(\d{4})$/;
        return this.mobile.replace(reg, "$1****$2");
      }
    },
    onLoad(params) {
      this.mobile = Decrypt(params.tel) //将手机号解密
      this.loginCaptcha() //调用发送短信验证码功能
      let interval = setInterval(() => {
        this.second--;
        if (this.second <= 0) {
          this.show = true;
          if (this.value.lenth != 6) {
            this.error = true;
          }
          clearInterval(interval);
        }
      }, 1000);
    },
    methods: {
      //发送短信验证码
      loginCaptcha() {
        api.sendRegisterOrLoginCaptcha({
          mobile: this.mobile
        }).then(res => {
          if (res.meta.code !== 200) {
            this.$refs.uToast.show({
              title: res.meta.msg,
              type: 'error',
              icon: false
            })
          } else {
            this.$refs.uToast.show({
              title: '发送成功,留意短信',
              type: 'success',
              icon: false
            })
          }
        }).catch(err => {
          this.$refs.uToast.show({
            title: err.meta.msg,
            type: 'error',
            icon: false
          })
        })
      },
      //输入完验证码最后一位执行
      finish(value) {
        api.loginByMobile({
          mobile: Encrypt(this.mobile), //传加密的手机号
          captcha: value
        }).then(res => {
          if (res.meta.code !== '10006') {
            console.log(res)
            this.$refs.uToast.show({
              title: res.meta.msg,
              type: 'warning',
              icon: false
            })
          } else {
            //登录成功
            //存储token
            uni.setStorageSync('token', Encrypt(res.data.accessToken))
            //跳转页面
            uni.switchTab({
              url: '../mine/mine'
            })
          }
        }).catch(err => {
          this.$refs.uToast.show({
            title: err.meta.msg,
            type: 'error',
            icon: false
          })
        })
      }
    }
  };
</script>

login.js 接口

import $http from './request.js'
import {
  Decrypt
} from '@/utils/aes/aes.js'

export default {
  //发送短信验证码
  sendRegisterOrLoginCaptcha({mobile}) {//拿到对象中mobile对应的值
    return $http.request({
      url: '/sms/sendRegisterOrLoginCaptcha?mobile='+mobile,
      method: 'GET'
    })
  },
  //手机验证码登录
  loginByMobile(params) {//
    return $http.request({
      url: '/u/loginByMobile',
      method: 'POST',
      data:params
    })
  },
  //用户名密码登录
  loginByJson(params) {//
    return $http.request({
      url: '/u/loginByJson',
      method: 'POST',
      data:params
    })
  },
}

App.vue

<script>
   import api from '@/service/mine.js'
   import {
     mapActions
   } from 'vuex'
   export default {
     onLaunch: function() {
       console.log('App Launch')
       //前提:曾经登录过 目的:重启时,不进入mine.vue,也能获取用户信息,存储到vuex中,方便该用户的学习、购买等操作 
       if (uni.getStorageSync('token')) {
         this.getUserInfo()
       }
     },
     onShow: function() {
       console.log('App Show')

     },
     onHide: function() {
       console.log('App Hide')
     },
     methods: {
       ...mapActions(['changeUserInfoActions']),
       //如果现在有存储的token(代表登录成功过),就发起获取用户信息的请求,并将用户信息存储到vuex中
       getUserInfo() {
         api.getUserInfo().then(res => {
           //console.log(res)
           if (res.meta.code === '200') {
             this.isLogin = true //变成已登录状态
             let info = res.data.data
             //若有昵称和头像,就走你的昵称和头像;若没有,就走默认的
             info.nickName = info.nickName ? info.nickName : '默认昵称'
             info.avatar = info.avatar ? info.avatar : '/static/me/avatar.jpeg'
             //将用户信息存储到vuex中
             this.changeUserInfoActions(info)
           } else {
             this.$refs.uToast.show({
               title: res.meta.msg,
               type: 'warning',
               icon: false
             })
           }
         }).catch(err => {
           console.log(err)
           this.$refs.uToast.show({
             title: err.meta.msg,
             type: 'error',
             icon: false
           })
         })
       },
     }
   }
 </script>

 <style lang="scss">
   @import "uview-ui/index.scss";
   /*每个页面公共css */
   @import "@/utils/iconfonts/iconfont.css";
   @import "@/utils/font-awesome/css/font-awesome.css"
 </style>

  • 7
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值