uniapp 登录页面封装

1,219 阅读4分钟

注意:JS中对接的接口需要更换为自己业务的接口、返回值也可能不同,此示例只是提供一个思路

微信小程序 登录

功能包括

  • 新用户授权登录
  • 老用户自动登录

效果

image.png

  • 自动登录功能:进入登录页自动执行一次登录的静默操作:如果是新用户则没有任何效果,如果是老用户则自动登录
  • 进行静默登录后可以判断是否是新/老用户,进而让按钮的文本发生变化(登录/注册)
  • 没有勾选协议的时候点击登录/注册按钮会自动弹出询问弹窗

代码

<template>
	<myNavbar type="2" title="登录" bgColor="transparent"></myNavbar>
	<image
		class="bgi-box"
		src="https://douleoss.oss-cn-shenzhen.aliyuncs.com/test/66275d53100d849cce5caae0.png"
		mode=""
	/>
	<div class="login-box">
		<text>{{ isRegister ? '注册' : '登录' }}</text>
		<button
			v-if="isAgree && isRegister"
			class="login-btn"
			open-type="getPhoneNumber"
			@getphonenumber="register"
		></button>
		<button v-if="isAgree && !isRegister" class="login-btn" @click="autoLogin"></button>
		<button v-if="!isAgree" class="login-btn" @click="popupShow = true"></button>
	</div>
	<div class="agreement-box" @click="isAgree = !isAgree">
		<div class="select" :style="{ backgroundColor: isAgree ? '#eed25f' : '#fff' }">
			<u-icon name="checkbox-mark" color="#666666" size="28" v-if="isAgree"></u-icon>
		</div>
		<div class="text">
			我已阅读并同意
			<text @click.stop="goAgreement">用户协议</text><text @click.stop="goAgreement">隐私权条款</text>
		</div>
	</div>
	<u-popup :show="popupShow" @close="popupShow = false" :customStyle="{ padding: '20rpx 40rpx' }">
		<div class="popup-box">
			<div class="popup-title">用户协议和服务条款</div>
			<div class="popup-content"
				>在您注册/登录都乐导乐账号的过程,您需要阅读并同意《用户协议》和《服务条款》</div
			>
			<div class="popup-bottom">
				<div class="cancellation" @click="popupShow = false">取消</div>
				<div class="determine" v-if="isRegister">
					<text>同意</text>
					<button class="login-btn" open-type="getPhoneNumber" @getphonenumber="register"></button>
				</div>
				<div class="determine" v-else @click="autoLogin">
					<text>同意</text>
				</div>
			</div>
		</div>
	</u-popup>
</template>

<script setup>
	import { ref, onMounted, computed, watchEffect, getCurrentInstance } from 'vue';
	import { onLoad, onUnload } from '@dcloudio/uni-app';
	const { proxy } = getCurrentInstance();
	const example = proxy;

	import { $mini_user_phone, $mini_user_login } from '@/api';

	onMounted(() => {
		autoLogin();
	});
	onUnload(() => {
		uni.removeStorageSync('registerToken');
	});

	let isRegister = ref(false); // 是否是注册
	// 自动登录
	function autoLogin() {
		uni.login({
			provider: 'weixin', // 使用微信登录
			success: async function (loginRes) {
				uni.showLoading({ title: '登录中...', mask: true });
				let code = loginRes.code;
				console.log('一键登录code', code);
				try {
					let res = await $mini_user_login({ code });
					uni.hideLoading();
					loginSuccess(res);
				} catch (error) {
					console.log(error);
					uni.hideLoading();
					if (error.statusCode == 407) {
						uni.setStorageSync('registerToken', error.errMsg);
						isRegister.value = true;
					} else {
						uni.showToast({ title: error.errMsg, icon: 'none' });
					}
				}
			},
		});
	}

	let isAgree = ref(false); // 是否同意协议
	// 点击跳转协议
	function goAgreement() {
		console.log('跳转协议');
	}

	let popupShow = ref(false); // 弹窗是否显示

	// 注册(手机号注册)
	async function register(e) {
		uni.showLoading({ title: '注册中...', mask: true });
		let detail = e.detail;
		console.log('手机号code', detail.code);
		if (detail.errMsg !== 'getPhoneNumber:ok') {
			uni.hideLoading();
			return;
		}
		try {
			let res = await $mini_user_phone({ code: detail.code });
			console.log('注册成功', res);
			uni.hideLoading();
			loginSuccess(res, true);
		} catch (error) {
			console.log(error);
			uni.hideLoading();
			uni.showToast({ title: error.errMsg, icon: 'none' });
		}
	}

	// 登录/注册成功
	function loginSuccess(data, isRegister = false) {
		isAgree.value = true;
		popupShow.value = false;
		uni.showToast({
			title: isRegister ? '注册成功' : '登录成功',
			icon: 'none',
			mask: true,
			duration: 1500,
		});
		uni.setStorageSync('token', data.token);
		uni.setStorageSync('userInfo', data);
		setTimeout(() => {
			let path = uni.getStorageSync('path');
			if (isRegister && !path) {
				uni.showToast({ title: '建议先完善个人资料', icon: 'none', mask: true, duration: 1500 });
				setTimeout(() => {
					uni.reLaunch({ url: '/page_user/personalData' });
				}, 1500);
			} else {
				if (path) {
					uni.removeStorageSync('path');
					uni.reLaunch({ url: path });
				} else {
					uni.reLaunch({
						url: '/pages/home/index',
					});
				}
			}
		}, 1500);
	}
</script>

<style lang="scss" scoped>
	.bgi-box {
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		z-index: -1;
	}
	.login-box {
		width: 80%;
		height: 100rpx;
		background: #fdd100;
		border-radius: 60rpx;
		margin: 23rpx auto;
		margin-top: 800rpx;
		@extend %c-box-center;
		font-weight: bold;
		font-size: 32rpx;
		color: #ffffff;
		position: relative;
	}
	.login-btn {
		position: absolute;
		width: 100%;
		height: 100%;
		left: 0;
		top: 0;
		opacity: 0;
	}
	.agreement-box {
		width: 80%;
		height: 100rpx;
		margin: 0 auto;
		background: #666666;
		border-radius: 48rpx;
		@extend %c-box-center;
		.select {
			width: 37rpx;
			height: 37rpx;
			border-radius: 50%;
			margin-right: 25rpx;
			@extend %c-box-center;
		}
		.text {
			font-weight: 500;
			font-size: 24rpx;
			color: #ffffff;
			text {
				position: relative;
				::before {
					content: '';
					width: 100%;
					height: 2rpx;
					background: #d2d2d2;
					position: absolute;
					bottom: -10rpx;
				}
			}
		}
	}
	.popup-box {
		.popup-title {
			margin: 20rpx 0;
			display: flex;
			justify-content: center;
			align-items: center;
			font-size: 34rpx;
			font-weight: 700;
		}
		.popup-content {
			margin-bottom: 20rpx;
			font-size: 24rpx;
			color: #666666;
		}
		.popup-bottom {
			display: flex;
			justify-content: space-between;
			align-items: center;
			.determine,
			.cancellation {
				width: 48%;
				height: 80rpx;
				border-radius: 48rpx;
				display: flex;
				justify-content: center;
				align-items: center;
				font-size: 34rpx;
				font-weight: 700;
				color: #ffffff;
			}
			.determine {
				background: #fdd100;
				position: relative;
			}
			.cancellation {
				background: #666666;
			}
		}
	}
</style>

微信小程序登录(简便版)

  • 没有自动登录功能
  • 没有勾选协议的时候点击登录/注册按钮会自动弹出询问弹窗
  • 只是简简单单的点击登录按钮进行手机号登录

代码

<template>
	<div class="uni-page-box" :style="{ paddingBottom: $safeAreaBottomHeight + 'px' }">
		<div class="title"> 配料查查 </div>
		<div class="logo">
			<image src="/static/images/logo.png" mode="scaleToFill" />
		</div>
		<div class="btn">
			<text>登录</text>
			<button open-type="getPhoneNumber" @getphonenumber="login" class="login-btn"></button>
		</div>
	</div>
</template>

<script setup>
	import { ref, onMounted, computed, watchEffect, getCurrentInstance } from 'vue';
	import { onLoad } from '@dcloudio/uni-app';
	const { proxy } = getCurrentInstance();

	let code = '';
	onMounted(() => {});
	onLoad((res) => {
		uni.login({
			provider: 'weixin',
			success: async function (loginRes) {
				console.log('uni.login API调用成功', loginRes);
				code = loginRes.code;
			},
			fail: function (err) {
				console.log('uni.login API调用失败', err);
				uni.hideLoading();
			},
		});
	});

	async function login(e) {
		console.log('获取手机号信息', e);
		if (e.detail.errMsg != 'getPhoneNumber:ok') {
			uni.showToast({
				title: '获取手机号失败',
				icon: 'none',
			});
			return;
		}
		uni.showLoading({
			title: '登录中...',
			mask: true,
		});
		let iv = e.detail.iv;
		let encryptedData = e.detail.encryptedData;
		try {
			let params = {
				code: code,
				iv: iv,
				encryptedData: encryptedData,
			};
			console.log('登录参数', params);
			// return;
			// const res = await $login(params);
			// console.log('登录成功', res);
			// uni.setStorageSync('token', res.token);
			// uni.setStorageSync('userInfo', res);
			uni.showToast({
				title: '登录成功',
				icon: 'success',
				duration: 1500,
			});
			await new Promise((resolve) => setTimeout(resolve, 1500));
			let path = uni.getStorageSync('path');
			if (path) {
				uni.removeStorageSync('path');
				uni.reLaunch({
					url: path,
				});
			} else {
				uni.reLaunch({
					url: '/pages/home/index',
				});
			}
		} catch (error) {
			console.log('登录失败', error);
			uni.hideLoading();
		}
	}
	function devLogin() {
		uni.showModal({
			title: '提示',
			content: '',
			placeholderText: '请输入手机号',
			editable: true,
			showCancel: true,
			success: async ({ confirm, cancel, content }) => {
				if (confirm) {
					try {
						// const res = await $devLogin({
						// 	mobile: content,
						// });
						// console.log('登录成功', res);
						// uni.setStorageSync('token', res.token);
						// uni.setStorageSync('userInfo', res);
						uni.showToast({
							title: '登录成功',
							icon: 'success',
							duration: 1500,
						});
						await new Promise((resolve) => setTimeout(resolve, 1500));
						let path = uni.getStorageSync('path');
						if (path) {
							uni.removeStorageSync('path');
							uni.reLaunch({
								url: path,
							});
						} else {
							uni.reLaunch({
								url: '/pages/home/index',
							});
						}
					} catch (error) {
						console.log('登录失败', error);
					}
				}
			},
		});
	}
</script>

<style lang="scss" scoped>
	.title {
		font-size: 40rpx;
		font-weight: bold;
		text-align: center;
		margin-top: 160rpx;
	}
	.logo {
		margin-top: 60rpx;
		display: flex;
		justify-content: center;
		image {
			width: 100rpx;
			height: 100rpx;
		}
	}
	.btn {
		margin: 0 auto;
		margin-top: 60rpx;
		width: 80%;
		height: 90rpx;
		background: #e94747;
		border-radius: 12rpx 12rpx 12rpx 12rpx;
		display: flex;
		justify-content: center;
		align-items: center;
		font-size: 32rpx;
		color: #ffffff;
		font-weight: bold;
		position: relative;
		.login-btn {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			opacity: 0;
		}
	}
</style>

微信公众号H5 登录

注意事项以及开发参考:developers.weixin.qq.com/doc/offiacc…

代码

<template>
	<div class="login-container">
		<div v-if="loading" class="loading"> 登录中... </div>
		<div v-else class="loading"> 授权中... </div>
	</div>
</template>

<script setup>
	import { ref, onMounted } from 'vue';
	import { onLoad } from '@dcloudio/uni-app';

	const loading = ref(false);
	const appid = 'your_appid'; // 需要替换成你的公众号appid

	// 获取当前页面完整URL
	const getCurrentPageUrl = () => {
		const pages = getCurrentPages();
		const currentPage = pages[pages.length - 1];
		const url = currentPage.$page.fullPath;
		return `${window.location.origin}${url}`;
	};

	// 微信授权登录
	const wxLogin = () => {
		const redirectUri = encodeURIComponent(getCurrentPageUrl());
		console.log('授权后重定向的回调链接地址', redirectUri);
		const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`;
		window.location.href = authUrl;
	};

	// 使用code获取用户信息并登录
	const loginWithCode = async (code) => {
		try {
			loading.value = true;
			// 调用后端登录接口
			// let res = await $test({ code });
			// uni.setStorageSync('token', '1234567890');
			// uni.setStorageSync('userInfo', {
			// 	nickName: '张三',
			// 	avatarUrl: 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
			// });
			uni.showToast({
				title: '登录成功',
				icon: 'none',
			});
			setTimeout(() => {
				uni.reLaunch({
					url: '/pages/home/index',
				});
			}, 1500);
		} catch (error) {
			uni.showToast({
				title: '登录失败',
				icon: 'none',
			});
		} finally {
			loading.value = false;
		}
	};

	// 检查登录状态
	const checkLogin = () => {
		const token = uni.getStorageSync('token');
		if (token) {
			// 已登录,跳转到首页
			uni.reLaunch({
				url: '/pages/home/index',
			});
			return true;
		}
		return false;
	};

	onLoad((options) => {
		// 如果已登录,直接跳转
		if (checkLogin()) return;

		// 获取url中的code参数
		const code = options.code;

		if (code) {
			// 有code,进行登录
			loginWithCode(code);
		} else {
			// 没有code,进行微信授权
			wxLogin();
		}
	});
</script>

<style lang="scss" scoped>
	.login-container {
		width: 100%;
		height: 100vh;
		display: flex;
		justify-content: center;
		align-items: center;
		.loading {
			font-size: 28rpx;
			color: #666;
		}
	}
</style>