uni-app黑马优购项目学习记录(十)(上篇)

415 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

10. 登录与支付

请添加图片描述

==注意:因接口问题,支付功能无法实现,就不贴出相应接口的信息了,想了解的可以在文章顶部网盘里获取==

10.0 创建 settle 分支

运行如下的命令,基于 master 分支在本地创建 settle 子分支,用来开发登录与支付相关的功能:

git checkout -b settle

10.1 点击结算按钮进行条件判断

说明:用户点击了结算按钮之后,需要先后判断是否勾选了要结算的商品、是否选择了收货地址、是否登录。

  1. my-settle 组件中,为结算按钮绑定点击事件处理函数:

    <!-- 结算按钮 -->
    <view class="btn-settle" @click="settlement">结算({{checkedCount}})</view>
    
  2. my-settle 组件的 methods 节点中声明 settlement 事件处理函数如下:

    // 点击了结算按钮
    settlement() {
      // 1. 先判断是否勾选了要结算的商品
      if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')
    
      // 2. 再判断用户是否选择了收货地址
      if (!this.addstr) return uni.$showMsg('请选择收货地址!')
    
      // 3. 最后判断用户是否登录了
      if (!this.token) return uni.$showMsg('请先登录!')
    }
    
  3. my-settle 组件中,使用 mapGetters 辅助函数,从 m_user 模块中将 addstr 映射到当前组件中使用:

    export default {
      computed: {
        ...mapGetters('m_cart', ['total', 'checkedCount', 'checkedGoodsAmount']),
        // addstr 是详细的收货地址
        ...mapGetters('m_user', ['addstr']),
        isFullCheck() {
          return this.total === this.checkedCount
        },
      },
    }
    
  4. store/user.js 模块的 state 节点中,声明 token 字符串:

    export default {
      // 开启命名空间
      namespaced: true,
    
      // state 数据
      state: () => ({
        // 收货地址
        address: JSON.parse(uni.getStorageSync('address') || '{}'),
        // 登录成功之后的 token 字符串
        token: '',
      }),
    
      // 省略其它代码
    }
    
  5. my-settle 组件中,使用 mapState 辅助函数,从 m_user 模块中将 token 映射到当前组件中使用:

    // 按需从 vuex 中导入 mapState 辅助函数
    import { mapGetters, mapMutations, mapState } from 'vuex'
    
    export default {
      computed: {
        ...mapGetters('m_cart', ['total', 'checkedCount', 'checkedGoodsAmount']),
        ...mapGetters('m_user', ['addstr']),
        // token 是用户登录成功之后的 token 字符串
        ...mapState('m_user', ['token']),
        isFullCheck() {
          return this.total === this.checkedCount
        },
      },
    }
    

10.2 登录

10.2.1 实现登录和用户信息组件的按需展示

  1. components 目录中新建登录组件

    在这里插入图片描述

  2. components 目录中新建用户信息组件(my-userinfo)

  3. my.vue 页面中,通过 mapState 辅助函数,导入需要的 token 字符串:

    import badgeMix from '@/mixins/tabbar-badge.js'
    // 1. 从 vuex 中按需导入 mapState 辅助函数
    import { mapState } from 'vuex'
    
    export default {
      mixins: [badgeMix],
      computed: {
        // 2. 从 m_user 模块中导入需要的 token 字符串
        ...mapState('m_user', ['token']),
      },
      data() {
        return {}
      },
    }
    
  4. my.vue 页面中,实现登录组件用户信息组件的按需展示:

    <template>
      <view>
    
        <!-- 用户未登录时,显示登录组件 -->
        <my-login v-if="!token"></my-login>
    
        <!-- 用户登录后,显示用户信息组件 -->
        <my-userinfo v-else></my-userinfo>
    
      </view>
    </template>
    

10.2.2 实现登录组件的基本布局

  1. my-login 组件定义如下的 UI 结构:

    <template>
      <view class="login-container">
        <!-- 提示登录的图标 -->
        <uni-icons type="contact-filled" size="100" color="#AFAFAF"></uni-icons>
        <!-- 登录按钮 -->
        <button type="primary" class="btn-login">一键登录</button>
        <!-- 登录提示 -->
        <view class="tips-text">登录后尽享更多权益</view>
      </view>
    </template>
    
  2. 美化登录组件的样式:

    .login-container {
      // 登录盒子的样式
      height: 750rpx;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      background-color: #f8f8f8;
      position: relative;
      overflow: hidden;
    
      // 绘制登录盒子底部的半椭圆造型
      &::after {
        content: ' ';
        display: block;
        position: absolute;
        width: 100%;
        height: 40px;
        left: 0;
        bottom: 0;
        background-color: white;
        border-radius: 100%;
        transform: translateY(50%);
      }
    
      // 登录按钮的样式
      .btn-login {
        width: 90%;
        border-radius: 100px;
        margin: 15px 0;
        background-color: #c00000;
      }
    
      // 按钮下方提示消息的样式
      .tips-text {
        font-size: 12px;
        color: gray;
      }
    }
    

10.2.3 点击登录按钮获取微信用户的基本信息

需求描述:需要获取微信用户的头像昵称等基本信息。

  1. 为登录的 button 按钮绑定 getUserInfo 方法,表示点击按钮时,希望获取用户的基本信息:

    <!-- 登录按钮 -->
    <!-- 可以从 @getuserinfo 事件处理函数的形参中,获取到用户的基本信息 -->
    <button type="primary" class="btn-login" @click="getUserInfo">一键登录</button>
    
  2. methods 节点中声明 getUserInfo 事件处理函数如下:

    methods: {
      // 获取微信用户的基本信息
      getUserInfo() {
       
        const [err, res] = await uni.getUserProfile({
              desc: '用于会员登录'
            }).catch(err => err)
          // 判断是否获取用户信息成功
        if (err?.errMsg === 'getUserProfile:fail auth deny') return uni.$showMsg('您取消了登录授权!')
        // 获取用户信息成功,res.userInfo就是用户的基本信息
        console.log(res.userInfo)
      }
    }
    

    Promise.catch()错误捕获机制 - 这里使用[err, res]对返回的信息进行解构,注意err, res解构的顺序问题 - getUserProfile的返回信息会被赋值给res,catch捕捉的错误会被赋值给err - [err, res]结构赋值里err,res的顺序不能变

    if (err?.errMsg === 'getUserProfile:fail auth deny') 等价于 if(err&&err.Msg&&err.errMsg === 'getUserProfile:fail auth deny')

10.2.4 将用户的基本信息存储到 vuex

  1. store/user.js 模块的 state 节点中,声明 userinfo 的信息对象如下:

    // state 数据
    state: () => ({
      // 收货地址
      // address: {}
      address: JSON.parse(uni.getStorageSync('address') || '{}'),
      // 登录成功之后的 token 字符串
      token: '',
      // 用户的基本信息
      userinfo: JSON.parse(uni.getStorageSync('userinfo') || '{}')
    }),
    
  2. store/user.js 模块的 mutations 节点中,声明如下的两个方法:

    // 方法
    mutations: {
      // 省略其它代码...
    
      // 更新用户的基本信息
      updateUserInfo(state, userinfo) {
        state.userinfo = userinfo
        // 通过 this.commit() 方法,调用 m_user 模块下的 saveUserInfoToStorage 方法,将 userinfo 对象持久化存储到本地
        this.commit('m_user/saveUserInfoToStorage')
      },
    
      // 将 userinfo 持久化存储到本地
      saveUserInfoToStorage(state) {
        uni.setStorageSync('userinfo', JSON.stringify(state.userinfo))
      }
    }
    
  3. 使用 mapMutations 辅助函数,将需要的方法映射到 my-login 组件中使用:

    // 1. 按需导入 mapMutations 辅助函数
    import { mapMutations } from 'vuex'
    
    export default {
      data() {
        return {}
      },
      methods: {
        // 2. 调用 mapMutations 辅助方法,把 m_user 模块中的 updateUserInfo 映射到当前组件中使用
        ...mapMutations('m_user', ['updateUserInfo']),
        // 获取微信用户的基本信息
        getUserInfo() {
           const [err, res] = await uni.getUserProfile({
              desc: '用于会员登录'
           }).catch(err => err)
          // 判断是否获取用户信息成功
    	   if (err?.errMsg === 'getUserProfile:fail auth deny') return uni.$showMsg('您取消了登录授权!')
    	    // 获取用户信息成功,res.userInfo就是用户的基本信息
    	    //console.log(res.userInfo)
    
          // 3. 将用户的基本信息存储到 vuex 中
          this.updateUserInfo(res.userInfo)
        },
      },
    }
    

10.2.5 登录获取 Token 字符串

需求说明:当获取到了微信用户的基本信息之后,还需要进一步调用登录相关的接口,从而换取登录成功之后的 Token 字符串

  1. getUserInfo 方法中,预调用 this.getToken() 方法,同时把获取到的用户信息传递进去:
// 获取微信用户的基本信息
getUserInfo() {
	  const [err, res] = await uni.getUserProfile({
	          desc: '用于会员登录'
	  }).catch(err => err)
	  if (err?.errMsg === 'getUserProfile:fail auth deny') return uni.$showMsg('您取消了登录授权!')
	  this.updateUserInfo(res.userInfo)
      // 获取登录成功后的 Token 字符串
      this.getToken(res)	  
},
  1. methods 中定义 getToken 方法,调用登录相关的 API,实现登录的功能:

    // 调用登录接口,换取永久的 token
     async getToken(info) {
       // 调用微信登录接口
       const [err, res] = await uni.login().catch(err => err)
       // 判断是否 uni.login() 调用失败
       if (err || res.errMsg !== 'login:ok') return uni.$showError('登录失败!')
       // 准备参数对象
       const query = {
          code: res.code,
          encryptedData: info.encryptedData,
          iv: info.iv,
          rawData: info.rawData,
          signature: info.signature
       }
    
        // 换取 token
        const { data: loginResult } = await uni.$http.post('/api/public/v1/users/wxlogin', query)
        if (loginResult.meta.status !== 200) return uni.$showMsg('登录失败!')
        uni.$showMsg('登录成功')
    

10.2.6 将 Token 存储到 vuex

  1. store/user.js 模块的 mutations 节点中,声明如下的两个方法:

    mutations: {
      // 省略其它代码...
    
      // 更新 token 字符串
      updateToken(state, token) {
        state.token = token
        // 通过 this.commit() 方法,调用 m_user 模块下的 saveTokenToStorage 方法,将 token 字符串持久化存储到本地
        this.commit('m_user/saveTokenToStorage')
      },
    
      // 将 token 字符串持久化存储到本地
      saveTokenToStorage(state) {
        uni.setStorageSync('token', state.token)
      }
    }
    
  2. 修改 store/user.js 模块的 state 节点如下:

    // state 数据
    state: () => ({
      // 收货地址
      address: JSON.parse(uni.getStorageSync('address') || '{}'),
      // 登录成功之后的 token 字符串
      token: uni.getStorageSync('token') || '',
      // 用户的基本信息
      userinfo: JSON.parse(uni.getStorageSync('userinfo') || '{}')
    }),
    
  3. my-login 组件中,把 vuex 中的 updateToken 方法映射到当前组件中使用:

    	methods: {
    	  // 1. 使用 mapMutations 辅助方法,把 m_user 模块中的 updateToken 方法映射到当前组件中使用
    	  ...mapMutations('m_user', ['updateUserInfo', 'updateToken'])
    	
    	  // 省略其它代码...
    	
    	  // 调用登录接口,换取永久的 token
    	  async getToken(info) {
    	    // 调用微信登录接口
    	    const [err, res] = await uni.login().catch(err => err)
    	    // 判断是否 uni.login() 调用失败
    	    if (err || res.errMsg !== 'login:ok') return uni.$showError('登录失败!')
    	
    	    // 准备参数对象
    	    const query = {
    	      code: res.code,
    	      encryptedData: info.encryptedData,
    	      iv: info.iv,
    	      rawData: info.rawData,
    	      signature: info.signature
    	    }
    	
    	    // 换取 token
    	    const { data: loginResult } = await uni.$http.post('/api/public/v1/users/wxlogin', query)
    	    if (loginResult.meta.status !== 200) return uni.$showMsg('登录失败!')
    	
    	    // 2. 更新 vuex 中的 token
    	    this.updateToken(loginResult.message.token)
    	  }
    	}
    

    因为获取token的接口无法使用了,为了做测试,可以将this.updateToken(loginResult.message.token)写成this.updateToken('xunitokentokentokentoken')用一个假的虚拟token代替,并把换取token的请求和判断登录失败的两行代码注释掉