Skip to content
本页目录

5. 登录与注册

5.1 实现我的页面基础结构

  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>
    
    1. 用户信息使用骨架屏做加载效果
    2. 根据登录状态判断 是否显示登录/注册按钮
  2. 实现页面样式

    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 实现登录页面基础结构

  1. 创建 login页面分包 /subpkg/login/login

  2. 页面结构

    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>
    
    1. 登录与注册逻辑一致 通过点击 去登录/去注册实现登录注册的切换
    2. 采用vant的输入框 vant输入框不支持v-model 只能自己实现双向绑定
  3. 完成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 用户登录与用户注册

  1. 通过 type值 判断用户操作类型是否为 登录/注册

    js
    import { 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'
    	}
    }
    
    1. 该方法绑定在form表单的提交事件中 故button按钮需要指定 form-type="submit"
    2. 使用了md5加密包 需要额外下载 npm i js-md5
  2. 注册完成后 自动切换 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 获取用户信息

  1. 定义获取用户信息接口

  2. 在我的页面显示时获取 若有用户信息则不获取 无头像授权信息也不获取

    js
    onShow(async () => {
    	state.loading = false
    	// 根据有无头像信息 选择是否重新加载用户信息
    	if (state.avatar && !user.nickName) {
    		// 读取用户信息
    		const { data } = await getUserInfo()
    		state.user = data
    	}
    })