5. 登录与注册
5.1 实现我的页面基础结构
定义页面结构
vue<template> <view> <!-- 个人信息 --> <van-skeleton title avatar :row="3" :loading="loading" class="van-skeleton"> <view class="user-info"> <view class="info"> <block v-if="user.nickName"> <image :src="state.avatar"></image> <view class="user-desc"> <text>昵称: {{ user.nickName }}</text> <text>登录名: {{ user.loginName }}</text> <text class="name">个性签名: {{ user.introduceSign }}</text> </view> </block> <block v-else> <image src="../../static/0.jpg"></image> <view class="user-login" @click="goLogin">登录 / 注册</view> </block> </view> </view> </van-skeleton> <!-- 操作列表 --> <view class="user-list"> <view class="item van-hairline--bottom"> <text>我的订单</text> <van-icon name="arrow" /> </view> <view class="item van-hairline--bottom"> <text>账号管理</text> <van-icon name="arrow" /> </view> <view class="item van-hairline--bottom"> <text>地址管理</text> <van-icon name="arrow" /> </view> <view class="item van-hairline--bottom"> <text>关于我们</text> <van-icon name="arrow" /> </view> </view> </view> </template>
- 用户信息使用骨架屏做加载效果
- 根据登录状态判断 是否显示登录/注册按钮
实现页面样式
scss.van-skeleton { margin-top: 20rpx; } // 用户信息 .user-info { width: 94%; margin: 20rpx; height: 230rpx; background: linear-gradient(90deg, $uni-primary, #51c7c7); box-shadow: 0 4rpx 10rpx #269090; border-radius: 12rpx; .info { position: relative; display: flex; width: 100%; height: 100%; padding: 50rpx 40rpx; box-sizing: border-box; align-items: center; image { width: 120rpx; height: 120rpx; border-radius: 50%; margin-top: 8rpx; } .user-desc { display: flex; flex-direction: column; margin-left: 40rpx; line-height: 40rpx; font-size: 28rpx; color: #fff; text { padding: 4rpx 0; } } .user-login { color: #fff; font-size: 32rpx; margin-left: 30rpx; padding: 14rpx; } } } // 操作列表 .user-list { padding: 0 40rpx; margin-top: 40rpx; .item { height: 80rpx; line-height: 80rpx; display: flex; justify-content: space-between; font-size: 28rpx; } }
5.2 我的页面逻辑
5.2.1 点击登录/注册
给 登录/注册绑定点击事件 用户点击之后请求授权用户信息【用于头像展示】
js
const state = reactive({
avatar: '',
user: {},
loading: true
})
const goLogin = async () => {
try {
const { userInfo } = await wx.getUserProfile({ desc: '用户登录信息' })
uni.navigateTo({ url: '/subpkg/login/login' })
// 将用户信息存储到本地中
uni.setStorageSync('USERINFO', userInfo)
state.avatar = userInfo.avatarUrl
} catch (e) {
//TODO handle the exception
console.log('用户拒绝了')
uni.$Toast('请先同意授权')
}
}
5.2.2 页面加载完成获取信息
在 页面加载完成后 需要获取授权信息 并进行显示
由于vue3 组合式API写法 使用onLoad方法 需要导入
import { onLoad, onShow } from '@dcloudio/uni-app'
js
onLoad(async () => {
// 读取头像信息
const { avatarUrl } = uni.getStorageSync('USERINFO')
state.avatar = avatarUrl
})
5.3 实现登录页面基础结构
创建
login
页面分包/subpkg/login/login
页面结构
vue<template> <view class="login"> <image class="logo" src="../../static/0.jpg"></image> <!-- 登录内容 --> <view class="login-body login" v-if="type === 'login'"> <form @submit="onSubmit"> <van-cell-group> <!-- 手机号 --> <van-field data-name="username" :value="username" required type="number" clearable @change="handleValue" label="手机号" placeholder="请输入手机号" /> <!-- 密码 --> <van-field data-name="password" :value="password" required type="password" clearable @change="handleValue" label="密码" placeholder="请输入密码" /> </van-cell-group> <view style="margin: 32rpx;"> <view class="link-register" @click="toggle('register')">立即注册</view> <van-button round block color="#1baeae" form-type="submit">登录</van-button> </view> </form> </view> <!-- 注册内容 --> <view class="login-body resister" v-else> <form @submit="onSubmit"> <van-cell-group> <!-- 手机号 --> <van-field data-name="username" :value="username" required type="number" clearable @change="handleValue" label="手机号" placeholder="请输入手机号" /> <!-- 密码 --> <van-field data-name="password" :value="password" required type="password" clearable @change="handleValue" label="密码" placeholder="请输入密码" /> </van-cell-group> <view style="margin: 32rpx;"> <view class="link-register" @click="toggle('login')">立即登录</view> <van-button round block color="#1baeae" form-type="submit">注册</van-button> </view> </form> </view> </view> </template>
- 登录与注册逻辑一致 通过点击 去登录/去注册实现登录注册的切换
- 采用vant的输入框 vant输入框不支持v-model 只能自己实现双向绑定
完成UI样式
scss.logo { width: 240rpx; height: 240rpx; display: block; margin: 160rpx auto 40rpx; } .login-body { padding: 0 40rpx; } .login, .register { .link-register, .link-login { font-size: 28rpx; margin-bottom: 40rpx; color: #1989fa; display: inline-block; } }
5.4 用户登录与用户注册
通过 type值 判断用户操作类型是否为 登录/注册
jsimport { reactive, toRefs } from 'vue' import { login } from "@/api/modules/user" import md5 from 'js-md5' const state = reactive({ username: '', password: '', type: 'login' }) // 提交表单 const onSubmit = async () => { // 验证输入框有效性 if (!username.value) return uni.$Toast('手机号不能为空!') if (!password.value) return uni.$Toast('密码不能为空!') // 判断操作类型 if (state.type === 'login') { // 进行登录 const { data } = await login({ loginName: username.value, passwordMd5: md5(password.value) }) // 存储token uni.setStorageSync('TOKEN', data) // 跳转到我的页面 if (getCurrentPages().length > 1) { uni.navigateBack() } else { uni.switchTab({ url: '/pages/my/my' }) } } else { // 进行注册 await register({ loginName: username.value, password: password.value }) uni.$Toast('注册成功') username.value = '' password.value = '' state.type = 'login' } }
- 该方法绑定在form表单的提交事件中 故button按钮需要指定 form-type="submit"
- 使用了md5加密包 需要额外下载
npm i js-md5
注册完成后 自动切换
type
类型为登录 后续操作转为登录操作
5.5 设置请求拦截器添加Token
由于接口限制需要在请求头中携带token 接下来只需要在api/index.js
中自行添加 beforeRequest方法即可
写法如下:
js
import { $http } from '@/utils/request'
// 配置请求根地址
$http.baseUrl = 'http://zhi.zeng.pub/new-bee/api/v1/'
// 配置请求拦截器
$http.beforeRequest = function(options) {
// 获取token 并设置token
options.header.token = wx.getStorageSync('TOKEN') || ''
}
export default $http
5.6 获取用户信息
定义获取用户信息接口
在我的页面显示时获取 若有用户信息则不获取 无头像授权信息也不获取
jsonShow(async () => { state.loading = false // 根据有无头像信息 选择是否重新加载用户信息 if (state.avatar && !user.nickName) { // 读取用户信息 const { data } = await getUserInfo() state.user = data } })