马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
UniApp 实现精美的用户登录和注册页面
媒介
在开辟鸿蒙APP时,登录注册页面作为用户与应用交互的第一道门槛,其体验与质量往往决定了用户的第一印象。客岁我接手了一个电商小程序项目,产物司理特别强调要做一个既雅观又实用的登录注册页面。经过一番摸索和实践,我用UniApp实现了一套完整的登录注册流程,今天就把这个过程分享给各人。
设计思路
一个好的登录注册页面应该具备以下特点:
- 简洁雅观 - 视觉上吸引用户
- 交互流畅 - 包括动画过渡、表单验证反馈等
- 功能完整 - 常规登录、手机验证码登录、第三方登录等
- 兼容适配 - 适应不同设备尺寸
基于这些思量,我采用了卡片式设计、渐变色背景,并加入适当的动效,让整个页面既现代又不失实用性。
页面结构
我们的登录注册体系包罗三个主要页面:
- 登录页面 - 支持账号密码登录和手机验证码登录
- 注册页面 - 新用户注册表单
- 忘记密码页面 - 用于密码找回
下面我们逐一实现这些页面。
登录页面实现
首先来看登录页面的结构代码:
上面的模板看起来代码挺多,但结构非常清晰。接下来实现逻辑部分:
- <script>
- export default {
- data() {
- return {
- loginType: 'password', // 登录类型:password-密码登录,sms-短信登录
- account: '',
- password: '',
- phone: '',
- smsCode: '',
- showPassword: false,
- counting: false, // 是否正在倒计时
- countDown: 60, // 倒计时秒数
- codeText: '获取验证码'
- };
- },
- computed: {
- tabLineStyle() {
- return {
- transform: this.loginType === 'password' ? 'translateX(0)' : 'translateX(100%)'
- };
- },
- isFormValid() {
- if (this.loginType === 'password') {
- return this.account.trim() && this.password.trim();
- } else {
- return this.phone.trim().length === 11 && this.smsCode.trim().length === 6;
- }
- }
- },
- methods: {
- // 切换登录方式
- switchLoginType(type) {
- this.loginType = type;
- },
-
- // 切换密码可见性
- togglePasswordVisibility() {
- this.showPassword = !this.showPassword;
- },
-
- // 发送短信验证码
- sendSmsCode() {
- if (this.counting) return;
-
- // 验证手机号
- if (!this.validatePhone()) {
- return;
- }
-
- // 开始倒计时
- this.counting = true;
- this.countDown = 60;
- this.codeText = `${this.countDown}秒后重发`;
-
- const timer = setInterval(() => {
- this.countDown--;
- this.codeText = `${this.countDown}秒后重发`;
-
- if (this.countDown <= 0) {
- clearInterval(timer);
- this.counting = false;
- this.codeText = '获取验证码';
- }
- }, 1000);
-
- // 发送验证码请求
- this.requestSmsCode();
- },
-
- // 验证手机号
- validatePhone() {
- if (!/^1[3-9]\d{9}$/.test(this.phone)) {
- uni.showToast({
- title: '请输入正确的手机号',
- icon: 'none'
- });
- return false;
- }
- return true;
- },
-
- // 请求发送验证码
- requestSmsCode() {
- // 实际项目中这里应该调用API发送验证码
- uni.showLoading({ title: '发送中...' });
-
- setTimeout(() => {
- uni.hideLoading();
- uni.showToast({
- title: '验证码已发送',
- icon: 'success'
- });
- }, 1500);
- },
-
- // 处理登录
- handleLogin() {
- if (!this.isFormValid) return;
-
- uni.showLoading({ title: '登录中...' });
-
- // 登录验证逻辑
- setTimeout(() => {
- uni.hideLoading();
-
- // 模拟登录成功
- uni.setStorageSync('token', 'demo_token_' + Date.now());
- uni.setStorageSync('userInfo', {
- id: 1,
- nickname: '测试用户',
- avatar: '/static/images/avatar.png'
- });
-
- uni.showToast({
- title: '登录成功',
- icon: 'success',
- duration: 1500,
- success: () => {
- // 延迟跳转,让用户看到成功提示
- setTimeout(() => {
- uni.switchTab({
- url: '/pages/home/home'
- });
- }, 1500);
- }
- });
- }, 2000);
- },
-
- // 第三方登录
- thirdPartyLogin(type) {
- uni.showToast({
- title: `正在尝试${type === 'wechat' ? '微信' : '苹果'}登录`,
- icon: 'none'
- });
-
- // 这里应该调用对应的第三方登录API
- // 微信登录示例
- if (type === 'wechat' && uni.getSystemInfoSync().platform !== 'devtools') {
- uni.login({
- provider: 'weixin',
- success: (loginRes) => {
- console.log('微信登录成功', loginRes);
- // 获取用户信息
- uni.getUserInfo({
- provider: 'weixin',
- success: (infoRes) => {
- console.log('获取用户信息成功', infoRes);
- // 将用户信息传给后端进行登录验证
- // this.wxLoginToServer(loginRes.code, infoRes.userInfo);
- }
- });
- },
- fail: (err) => {
- console.error('微信登录失败', err);
- }
- });
- }
- },
-
- // 打开隐私政策
- openPrivacyPolicy() {
- uni.navigateTo({
- url: '/pages/common/privacy-policy'
- });
- }
- }
- };
- </script>
复制代码 末了是样式部分,一个漂亮的UI很大程度上取决于CSS:
- <style lang="scss">
- .login-container {
- position: relative;
- width: 100%;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- overflow: hidden;
- }
- // 背景样式
- .bg-wrapper {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: -1;
-
- .bg-image {
- width: 100%;
- height: 100%;
- }
-
- .bg-mask {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: linear-gradient(to bottom, rgba(29, 36, 52, 0.5), rgba(29, 36, 52, 0.8));
- }
- }
- // Logo区域
- .logo-box {
- margin-top: 80rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .logo {
- width: 180rpx;
- margin-bottom: 20rpx;
- }
-
- .slogan {
- font-size: 28rpx;
- color: #ffffff;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
- }
- }
- // 登录表单
- .login-form {
- width: 85%;
- margin-top: 80rpx;
- background-color: rgba(255, 255, 255, 0.95);
- border-radius: 16rpx;
- padding: 40rpx 30rpx;
- box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.15);
-
- // 标签头部
- .tab-header {
- display: flex;
- position: relative;
- border-bottom: 1px solid #f2f2f2;
- margin-bottom: 50rpx;
-
- .tab-item {
- flex: 1;
- text-align: center;
- font-size: 32rpx;
- color: #666;
- padding-bottom: 20rpx;
- transition: all 0.3s;
-
- &.active {
- color: #3b7aff;
- font-weight: 500;
- }
- }
-
- .tab-line {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 50%;
- height: 4rpx;
- background-color: #3b7aff;
- border-radius: 4rpx;
- transition: all 0.3s ease;
- }
- }
-
- // 表单内容
- .form-content {
- margin-bottom: 50rpx;
-
- .input-item {
- display: flex;
- align-items: center;
- height: 100rpx;
- border-bottom: 1rpx solid #eee;
- margin-bottom: 30rpx;
-
- .input {
- flex: 1;
- height: 100%;
- font-size: 30rpx;
- padding-left: 20rpx;
- }
-
- .eye-icon {
- padding: 0 10rpx;
- }
-
- .code-btn {
- width: 200rpx;
- height: 70rpx;
- line-height: 70rpx;
- background: #3b7aff;
- color: #fff;
- font-size: 26rpx;
- text-align: center;
- border-radius: 35rpx;
-
- &.disabled {
- background: #cccccc;
- }
- }
- }
- }
-
- // 操作区域
- .action-area {
- .login-btn {
- width: 100%;
- height: 90rpx;
- line-height: 90rpx;
- background: linear-gradient(135deg, #4b8eff, #3b7aff);
- color: #fff;
- font-size: 32rpx;
- border-radius: 45rpx;
- margin-bottom: 30rpx;
-
- &.disabled {
- background: linear-gradient(135deg, #cccccc, #aaaaaa);
- }
- }
-
- .additional-links {
- display: flex;
- justify-content: space-between;
- font-size: 26rpx;
- color: #666;
- margin-bottom: 40rpx;
-
- .link-item {
- padding: 10rpx;
- }
- }
- }
-
- // 第三方登录
- .third-party-login {
- margin-top: 30rpx;
-
- .divider {
- display: flex;
- align-items: center;
- margin-bottom: 40rpx;
-
- .line {
- flex: 1;
- height: 1rpx;
- background-color: #eee;
- }
-
- .text {
- padding: 0 20rpx;
- font-size: 26rpx;
- color: #999;
- }
- }
-
- .icons-row {
- display: flex;
- justify-content: center;
-
- .icon-item {
- width: 80rpx;
- height: 80rpx;
- margin: 0 40rpx;
-
- image {
- width: 100%;
- height: 100%;
- }
- }
- }
- }
- }
- // 底部隐私政策
- .privacy-policy {
- position: absolute;
- bottom: 40rpx;
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.8);
- text-align: center;
-
- .policy-link {
- color: #4b8eff;
- }
- }
- </style>
复制代码 注册页面实现
注册页面与登录页面类似,但表单内容有所不同。这里我们简化一下代码,只展示关键部分:
- <template>
- <view class="register-container">
- <!-- 背景和顶部与登录页类似 -->
-
- <view class="register-form">
- <view class="form-header">
- <text class="title">新用户注册</text>
- <text class="subtitle">加入我们,体验更多精彩</text>
- </view>
-
- <view class="form-content">
- <view class="input-item">
- <uni-icons type="phone" size="20" color="#999"></uni-icons>
- <input
- class="input"
- type="number"
- maxlength="11"
- placeholder="请输入手机号"
- v-model="form.phone"
- />
- </view>
-
- <view class="input-item">
- <uni-icons type="chat" size="20" color="#999"></uni-icons>
- <input
- class="input"
- type="number"
- maxlength="6"
- placeholder="请输入验证码"
- v-model="form.smsCode"
- />
- <view
- class="code-btn"
- :class="{ disabled: counting }"
- @tap="sendSmsCode"
- >
- {{ codeText }}
- </view>
- </view>
-
- <view class="input-item">
- <uni-icons type="locked" size="20" color="#999"></uni-icons>
- <input
- class="input"
- :type="showPassword ? 'text' : 'password'"
- placeholder="请设置6-20位密码"
- v-model="form.password"
- />
- <view class="eye-icon" @tap="togglePasswordVisibility">
- <uni-icons :type="showPassword ? 'eye' : 'eye-slash'" size="20" color="#999"></uni-icons>
- </view>
- </view>
-
- <view class="input-item">
- <uni-icons type="locked" size="20" color="#999"></uni-icons>
- <input
- class="input"
- :type="showPassword ? 'text' : 'password'"
- placeholder="请确认密码"
- v-model="form.confirmPassword"
- />
- </view>
- </view>
-
- <view class="action-area">
- <button
- class="register-btn"
- :disabled="!isFormValid"
- :class="{ disabled: !isFormValid }"
- @tap="handleRegister"
- >
- 注册
- </button>
-
- <view class="login-link">
- 已有账号?<text class="link" @tap="goToLogin">去登录</text>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- export default {
- data() {
- return {
- form: {
- phone: '',
- smsCode: '',
- password: '',
- confirmPassword: ''
- },
- showPassword: false,
- counting: false,
- countDown: 60,
- codeText: '获取验证码'
- };
- },
- computed: {
- isFormValid() {
- return this.form.phone.length === 11 &&
- this.form.smsCode.length === 6 &&
- this.form.password.length >= 6 &&
- this.form.password === this.form.confirmPassword;
- }
- },
- methods: {
- // 密码可见性切换
- togglePasswordVisibility() {
- this.showPassword = !this.showPassword;
- },
-
- // 发送验证码(与登录页类似)
- sendSmsCode() {
- // 实现与登录页类似
- },
-
- // 处理注册
- handleRegister() {
- if (!this.isFormValid) return;
-
- // 表单验证
- if (this.form.password !== this.form.confirmPassword) {
- uni.showToast({
- title: '两次密码不一致',
- icon: 'none'
- });
- return;
- }
-
- uni.showLoading({ title: '注册中...' });
-
- // 模拟注册请求
- setTimeout(() => {
- uni.hideLoading();
- uni.showToast({
- title: '注册成功',
- icon: 'success',
- duration: 1500,
- success: () => {
- // 延迟跳转到登录页
- setTimeout(() => {
- uni.navigateTo({
- url: '/pages/auth/login'
- });
- }, 1500);
- }
- });
- }, 2000);
- },
-
- // 跳转到登录页
- goToLogin() {
- uni.navigateBack();
- }
- }
- };
- </script>
复制代码 注册页面的样式可以参考登录页面,只需对一些细节进行调整即可。
实际效果与优化方案
在实际项目中,我们实现的登录注册页面已经投入利用,用户反馈非常好。不过在利用过程中也发现了一些题目和优化点:
1. 动画效果优化
为了让登录体验更流畅,我们加入了一些过渡动画:
- // 切换标签时添加动画
- switchLoginType(type) {
- // 添加动画类
- this.animating = true;
- setTimeout(() => {
- this.loginType = type;
- setTimeout(() => {
- this.animating = false;
- }, 300);
- }, 150);
- }
复制代码 对应的CSS:
- .form-content {
- transition: opacity 0.3s ease;
- opacity: 1;
-
- &.animating {
- opacity: 0;
- }
- }
复制代码 2. 表单验证优化
在用户输入过程中实时验证并给出友爱提示:
- <view class="input-item" :class="{ error: phoneError }">
- <uni-icons type="phone" size="20" color="#999"></uni-icons>
- <input
- class="input"
- type="number"
- maxlength="11"
- placeholder="请输入手机号"
- v-model="phone"
- @input="validatePhoneInput"
- />
- <text v-if="phoneError" class="error-tip">{{ phoneErrorMsg }}</text>
- </view>
复制代码- validatePhoneInput() {
- if (this.phone && !/^1[3-9]\d{9}$/.test(this.phone)) {
- this.phoneError = true;
- this.phoneErrorMsg = '请输入正确的手机号';
- } else {
- this.phoneError = false;
- this.phoneErrorMsg = '';
- }
- }
复制代码 3. 记着登录状态
用户体验优化,记着登录状态避免频仍登录:
- // 登录成功后保存状态
- handleLoginSuccess(userInfo, token) {
- // 保存用户信息和token
- uni.setStorageSync('token', token);
- uni.setStorageSync('userInfo', userInfo);
- uni.setStorageSync('loginTime', Date.now());
-
- // 如果用户勾选了"记住登录状态"
- if (this.rememberMe) {
- uni.setStorageSync('rememberMe', true);
- } else {
- uni.removeStorageSync('rememberMe');
- }
- }
复制代码 在App启动时查抄登录状态:
- // 在App.vue的onLaunch中
- checkLoginStatus() {
- const token = uni.getStorageSync('token');
- const rememberMe = uni.getStorageSync('rememberMe');
- const loginTime = uni.getStorageSync('loginTime');
- const currentTime = Date.now();
-
- // 如果有token且选择了记住登录,或者登录时间在7天内
- if (token && (rememberMe || (currentTime - loginTime < 7 * 24 * 60 * 60 * 1000))) {
- // token验证
- this.verifyToken(token);
- } else {
- // 清除登录信息,跳转到登录页
- this.clearLoginInfo();
- uni.reLaunch({
- url: '/pages/auth/login'
- });
- }
- }
复制代码 总结与心得
通过这次实践,我总结了几点关于实现好的登录注册页面的经验:
- 设计先行 - 在编码前先做好设计稿,确保视觉效果和交互流程
- 拆分组件 - 将复杂页面拆分为多个可复用组件,便于维护
- 验证健壮 - 表单验证要全面且给出友爱提示
- 安全思量 - 密码加密传输,防止中间人攻击
- 适配兼容 - 适配不同尺寸的设备,兼容不同平台
UniApp提供了很多实用组件和API,极大简化了我们的开辟过程。通过合理利用这些功能,我们可以或许快速实现一个既雅观又实用的登录注册体系。
希望这篇文章对你在UniApp中实现登录注册页面有所帮助。假如有任何题目或建议,欢迎在评论区交流!
参考资料
- UniApp官方文档:https://uniapp.dcloud.io/
- uni-ui组件库:https://ext.dcloud.net.cn/plugin?id=55
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|