注册页面组件(带自动跳转功能)存档文档
注册页面组件(带自动跳转功能)存档文档
一、组件概述
组件名称:RegisterPage.vue
功能定位:企业级用户注册表单,支持手机号/邮箱双因子注册、密码强度检测、GDPR合规同意及注册成功后自动跳转登录功能,基于Vue 3 + TypeScript + Quasar框架实现。
核心价值:
- 表单安全优化:完整的autocomplete属性配置,符合W3C标准
- 智能跳转机制:注册成功后自动导向登录页面,提升用户体验
- 密码安全策略:实时强度检测(弱/中/强/非常强)及多维度规则校验
- 企业级合规:内置GDPR数据保护条例同意机制
- 响应式设计:适配移动端/桌面端的表单布局
- autocomplete属性优化:
二、核心功能模块
1. 智能表单控制
o 手机号:autocomplete="tel"(优化移动端数字键盘唤起)
o 邮箱:autocomplete="email"(自动填充邮箱客户端数据)
o 昵称:autocomplete="nickname"(支持浏览器个人信息管理)
o 密码字段:autocomplete="new-password"(防止密码管理器错误填充)
- 注册成功自动跳转:
o 基于Vue Router的命名路由跳转({ name: 'Login' })
o 成功对话框确认后触发redirectToLogin方法
o 跳转前自动重置表单数据,确保下次注册环境干净
2. 密码安全体系
- 强度实时检测:
const passwordStrength = computed(() => {
let strength = 0
// 长度得分(最高40分)
if (password.length >= 12) strength += 30
if (password.length >= 16) strength += 10
// 字符种类得分(最高60分)
if (/[a-z]/.test(password)) strength += 10
if (/[A-Z]/.test(password)) strength += 10
if (/[0-9]/.test(password)) strength += 10
if (/[^A-Za-z0-9]/.test(password)) strength += 20
// 防常用密码加分(10分)
if (!commonPasswords.some(p => password.toLowerCase().includes(p))) strength += 10
return Math.min(strength, 100)
})
- 多维度验证规则:
const passwordRules = [
(val) => !!val || '密码不能为空',
(val) => val.length >= 12 || '密码长度至少为12位',
(val) => /[a-z]/.test(val) || '必须包含小写字母',
(val) => /[A-Z]/.test(val) || '必须包含大写字母',
(val) => /[0-9]/.test(val) || '必须包含数字',
(val) => /[^A-Za-z0-9]/.test(val) || '必须包含特殊字符',
]
3. 企业级用户体验优化
- 表单交互反馈:
o 手机号掩码:mask="### #### ####"(格式化显示)
o 密码可见性切换:双图标切换(visibility/visibility_off)
o 实时错误提示:字段级验证反馈(如"两次输入的密码不一致")
- 合规性控制:
o GDPR强制同意:未勾选时阻止表单提交
o 数据脱敏处理:注册成功后仅显示用户ID(隐藏敏感信息)
o 操作审计准备:预留用户ID记录字段(registeredUserId)
三、完整代码实现
<template>
<q-card class="enterprise-register-card">
<q-card-section>
<div class="text-h5 text-center q-mb-md">企业用户注册</div>
<q-form @submit.prevent="onSubmit" class="q-gutter-md">
<!-- 手机号输入 -->
<q-input
v-model="registerData.mobile"
label="手机号 *"
outlined
lazy-rules
:rules="mobileRules"
mask="### #### ####"
unmasked-value
hint="请输入11位中国大陆手机号"
autocomplete="tel"
>
<template v-slot:prepend>
<q-icon name="phone" />
</template>
</q-input>
<!-- 邮箱输入 -->
<q-input
v-model="registerData.email"
label="邮箱"
outlined
type="email"
:rules="emailRules"
hint="可选,用于找回密码"
autocomplete="email"
>
<template v-slot:prepend>
<q-icon name="email" />
</template>
</q-input>
<!-- 昵称输入 -->
<q-input
v-model="registerData.nickname"
label="昵称"
outlined
:rules="nicknameRules"
hint="将用于系统显示您的名称"
autocomplete="nickname"
>
<template v-slot:prepend>
<q-icon name="person" />
</template>
</q-input>
<!-- 密码输入 -->
<q-input
v-model="registerData.password"
label="密码 *"
outlined
:type="isPwdVisible ? 'text' : 'password'"
:rules="passwordRules"
hint="至少12位,包含大小写字母、数字和特殊字符"
autocomplete="new-password"
>
<template v-slot:prepend>
<q-icon name="lock" />
</template>
<template v-slot:append>
<q-icon
:name="isPwdVisible ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwdVisible = !isPwdVisible"
/>
</template>
</q-input>
<!-- 密码强度指示器 -->
<div class="row items-center">
<q-linear-progress
:value="passwordStrength / 100"
:color="passwordStrengthColor"
class="col"
/>
<div class="q-ml-sm text-caption">
{{ passwordStrengthLabel }}
</div>
</div>
<!-- 确认密码 -->
<q-input
v-model="registerData.passwordConfirm"
label="确认密码 *"
outlined
:type="isConfirmPwdVisible ? 'text' : 'password'"
:rules="confirmPasswordRules"
autocomplete="new-password"
>
<template v-slot:prepend>
<q-icon name="lock" />
</template>
<template v-slot:append>
<q-icon
:name="isConfirmPwdVisible ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isConfirmPwdVisible = !isConfirmPwdVisible"
/>
</template>
</q-input>
<!-- GDPR 同意 -->
<q-checkbox
v-model="gdprConsent"
label="我同意遵守 GDPR 数据保护条例"
:rules="[(val: boolean) => val || '必须同意GDPR条例才能注册']"
/>
<!-- 提交按钮 -->
<div class="q-mt-lg">
<q-btn
label="注册"
type="submit"
color="primary"
:loading="isSubmitting"
class="full-width"
/>
</div>
</q-form>
</q-card-section>
<!-- 成功注册提示 -->
<q-dialog v-model="showSuccessDialog" persistent>
<q-card>
<q-card-section class="row items-center">
<q-avatar icon="check_circle" color="positive" text-color="white" />
<span class="q-ml-sm">用户注册成功!</span>
</q-card-section>
<q-card-section>
<p>
您的用户ID: <strong>{{ registeredUserId }}</strong>
</p>
<p>请妥善保管您的账号信息</p>
</q-card-section>
<q-card-actions align="right">
<q-btn
flat
label="确定"
color="primary"
v-close-popup
@click="redirectToLogin"
/>
</q-card-actions>
</q-card>
</q-dialog>
</q-card>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useQuasar } from 'quasar'
import { apiClient } from 'src/services/axios'
import type { AxiosError } from 'axios'
import { useRouter } from 'vue-router'
// 使用 Quasar 插件
const $q = useQuasar()
const router = useRouter()
// 注册表单数据类型
interface RegisterFormData {
mobile: string
email: string
nickname: string
password: string
passwordConfirm: string
}
// 注册响应类型
interface RegisterResponse {
status: string
message: string
user_id: string
}
// 表单数据
const registerData = ref<RegisterFormData>({
mobile: '',
email: '',
nickname: '',
password: '',
passwordConfirm: '',
})
// 状态控制
const isSubmitting = ref(false)
const isPwdVisible = ref(false)
const isConfirmPwdVisible = ref(false)
const gdprConsent = ref(false)
const showSuccessDialog = ref(false)
const registeredUserId = ref('')
// 验证规则 - 手机号
const mobileRules = [
(val: string) => !!val || '手机号不能为空',
(val: string) => /^1[3-9]\d{9}$/.test(val) || '请输入有效的手机号',
]
// 验证规则 - 邮箱
const emailRules = [
(val: string) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val) || '请输入有效的邮箱地址',
]
// 验证规则 - 昵称
const nicknameRules = [(val: string) => !val || val.length <= 20 || '昵称不能超过20个字符']
// 验证规则 - 密码
const passwordRules = [
(val: string) => !!val || '密码不能为空',
(val: string) => val.length >= 12 || '密码长度至少为12位',
(val: string) => /[a-z]/.test(val) || '必须包含小写字母',
(val: string) => /[A-Z]/.test(val) || '必须包含大写字母',
(val: string) => /[0-9]/.test(val) || '必须包含数字',
(val: string) => /[^A-Za-z0-9]/.test(val) || '必须包含特殊字符',
]
// 验证规则 - 确认密码
const confirmPasswordRules = [
(val: string) => !!val || '请确认密码',
(val: string) => val === registerData.value.password || '两次输入的密码不一致',
]
// 密码强度计算
const passwordStrength = computed(() => {
const password = registerData.value.password
if (!password) return 0
let strength = 0
// 长度得分
if (password.length >= 12) strength += 30
if (password.length >= 16) strength += 10
// 字符种类得分
if (/[a-z]/.test(password)) strength += 10
if (/[A-Z]/.test(password)) strength += 10
if (/[0-9]/.test(password)) strength += 10
if (/[^A-Za-z0-9]/.test(password)) strength += 20
// 避免常用密码
const commonPasswords = ['password', '123456', 'qwerty', 'admin']
if (!commonPasswords.some((p) => password.toLowerCase().includes(p))) {
strength += 10
}
return Math.min(strength, 100)
})
// 密码强度标签
const passwordStrengthLabel = computed(() => {
const strength = passwordStrength.value
if (strength < 40) return '弱'
if (strength < 70) return '中'
if (strength < 90) return '强'
return '非常强'
})
// 密码强度颜色
const passwordStrengthColor = computed(() => {
const strength = passwordStrength.value
if (strength < 40) return 'negative'
if (strength < 70) return 'warning'
return 'positive'
})
// 跳转到登录页面
const redirectToLogin = () => {
// 使用命名路由跳转到登录页面
router.push({ name: 'Login' })
}
// 提交注册表单
const onSubmit = async () => {
if (!gdprConsent.value) {
$q.notify({
type: 'warning',
message: '请同意GDPR数据保护条例',
})
return
}
isSubmitting.value = true
try {
const response = await apiClient.post<RegisterResponse>('/users/register/', {
mobile: registerData.value.mobile,
email: registerData.value.email,
nickname: registerData.value.nickname,
password: registerData.value.password,
password_confirm: registerData.value.passwordConfirm,
})
if (response.data.status === 'success') {
registeredUserId.value = response.data.user_id
showSuccessDialog.value = true
// 重置表单
registerData.value = {
mobile: '',
email: '',
nickname: '',
password: '',
passwordConfirm: '',
}
gdprConsent.value = false
} else {
$q.notify({
type: 'negative',
message: response.data.message || '注册失败,请重试',
})
}
} catch (error) {
const axiosError = error as AxiosError
if (axiosError.response) {
const errorData = axiosError.response.data as { [key: string]: string[] } | string
if (typeof errorData === 'string') {
$q.notify({
type: 'negative',
message: errorData,
})
} else {
// 处理字段错误
const errorMessages = Object.values(errorData).flat()
$q.notify({
type: 'negative',
message: errorMessages.join(', ') || '注册失败,请检查输入',
})
}
} else {
$q.notify({
type: 'negative',
message: '网络错误,请检查连接后重试',
})
}
} finally {
isSubmitting.value = false
}
}
</script>
<style lang="scss">
.enterprise-register-card {
max-width: 500px;
margin: 0 auto;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
.q-card__section {
padding: 30px;
}
.q-field__label {
font-weight: 500;
}
.q-linear-progress {
height: 8px;
border-radius: 4px;
}
.gdpr-agreement {
font-size: 0.85rem;
margin-top: 10px;
a {
color: var(--q-primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.submit-btn {
height: 48px;
font-size: 1.1rem;
font-weight: 500;
letter-spacing: 0.5px;
border-radius: 6px;
}
}
</style>
四、关键功能说明
1. autocomplete属性优化
所有表单字段均配置符合W3C标准的autocomplete属性,提升浏览器自动填充体验,同时避免敏感信息泄露风险。手机号字段使用tel类型确保移动端唤起数字键盘,密码字段使用new-password提示浏览器禁用旧密码填充。
2. 注册成功跳转机制
通过useRouter实现命名路由跳转,避免硬编码路径带来的维护风险。跳转触发点绑定在成功对话框的确认按钮上,确保用户明确操作意图后再执行跳转,提升体验流畅度。
3. 密码安全增强
实现多维度密码强度评估算法,综合长度、字符多样性、防常用密码等因素,通过颜色编码(红/黄/绿)和文字标签(弱/中/强/非常强)提供直观反馈,引导用户创建高强度密码。
4. 企业级表单验证
采用字段级实时验证策略,结合Quasar的lazy-rules特性优化性能。手机号使用掩码格式化(### #### ####)提升输入体验,同时通过正则表达式确保中国大陆手机号格式正确性。
浙公网安备 33010602011771号