644 lines
15 KiB
Vue
644 lines
15 KiB
Vue
<template>
|
|
<div class="register-container">
|
|
<div class="background">
|
|
<div class="shape"></div>
|
|
<div class="shape"></div>
|
|
</div>
|
|
<el-card class="register-card" :body-style="{ padding: '30px' }">
|
|
<div class="register-content">
|
|
<div class="basic-info">
|
|
<div class="register-header">
|
|
<div class="logo">
|
|
<el-icon :size="40" color="#409EFF"><Platform /></el-icon>
|
|
</div>
|
|
<h2>智慧养老平台</h2>
|
|
<p class="subtitle">创建新账号</p>
|
|
</div>
|
|
|
|
<el-form
|
|
ref="registerFormRef"
|
|
:model="registerForm"
|
|
:rules="rules"
|
|
label-position="top"
|
|
class="animated-form"
|
|
>
|
|
<el-form-item label="姓名" prop="username">
|
|
<el-input
|
|
v-model="registerForm.username"
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><User /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="密码" prop="password">
|
|
<el-input
|
|
v-model="registerForm.password"
|
|
type="password"
|
|
show-password
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><Lock /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="确认密码" prop="confirmPassword">
|
|
<el-input
|
|
v-model="registerForm.confirmPassword"
|
|
type="password"
|
|
show-password
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><Lock /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
|
|
<div class="verify-info">
|
|
<div class="divider">联系方式验证</div>
|
|
|
|
<div class="verify-section">
|
|
<div class="verify-title">
|
|
<el-icon><Phone /></el-icon>
|
|
<span>手机验证</span>
|
|
</div>
|
|
|
|
<el-form
|
|
:model="registerForm"
|
|
:rules="rules"
|
|
label-position="top"
|
|
>
|
|
<el-form-item label="手机号" prop="phone">
|
|
<div class="verify-code-input">
|
|
<el-input
|
|
v-model="registerForm.phone"
|
|
placeholder="请输入手机号"
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><Phone /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
<el-button
|
|
type="primary"
|
|
:disabled="phoneCooldown > 0 || !registerForm.phone"
|
|
@click="sendPhoneCode"
|
|
>
|
|
{{ phoneCooldown > 0 ? `${phoneCooldown}s` : '获取验证码' }}
|
|
</el-button>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="验证码" prop="phoneCode">
|
|
<el-input
|
|
v-model="registerForm.phoneCode"
|
|
placeholder="请输入手机验证码"
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><Key /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
|
|
<div class="verify-section">
|
|
<div class="verify-title">
|
|
<el-icon><Message /></el-icon>
|
|
<span>邮箱验证</span>
|
|
</div>
|
|
|
|
<el-form
|
|
:model="registerForm"
|
|
:rules="rules"
|
|
label-position="top"
|
|
>
|
|
<el-form-item label="邮箱号" prop="email">
|
|
<div class="verify-code-input">
|
|
<el-input
|
|
v-model="registerForm.email"
|
|
placeholder="请输入邮箱"
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><Message /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
<el-button
|
|
type="primary"
|
|
:disabled="emailCooldown > 0 || !registerForm.email"
|
|
@click="sendEmailCode"
|
|
>
|
|
{{ emailCooldown > 0 ? `${emailCooldown}s` : '获取验证码' }}
|
|
</el-button>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="验证码" prop="emailCode">
|
|
<el-input
|
|
v-model="registerForm.emailCode"
|
|
placeholder="请输入邮箱验证码"
|
|
class="custom-input"
|
|
>
|
|
<template #prefix>
|
|
<div class="input-icon">
|
|
<el-icon><Key /></el-icon>
|
|
</div>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<el-button
|
|
type="primary"
|
|
class="register-button"
|
|
:loading="loading"
|
|
@click="handleRegister"
|
|
>
|
|
<span class="button-text">注 册</span>
|
|
<el-icon class="button-icon"><Right /></el-icon>
|
|
</el-button>
|
|
|
|
<div class="login-link">
|
|
已有账号?
|
|
<router-link to="/login" class="login-btn">立即登录</router-link>
|
|
</div>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { User, Lock, Phone, Message, Platform, Right, Key } from '@element-plus/icons-vue'
|
|
import type { FormInstance } from 'element-plus'
|
|
import { ElMessage } from 'element-plus'
|
|
import { userApi } from '@/api/user'
|
|
|
|
const router = useRouter()
|
|
const registerFormRef = ref<FormInstance>()
|
|
const loading = ref(false)
|
|
|
|
const registerForm = reactive({
|
|
username: '',
|
|
phone: '',
|
|
password: '',
|
|
confirmPassword: '',
|
|
phoneCode: '',
|
|
email: '',
|
|
emailCode: ''
|
|
})
|
|
|
|
const validatePass = (rule: any, value: string, callback: any) => {
|
|
if (value === '') {
|
|
callback(new Error('请再次输入密码'))
|
|
} else if (value !== registerForm.password) {
|
|
callback(new Error('两次输入密码不一致!'))
|
|
} else {
|
|
callback()
|
|
}
|
|
}
|
|
|
|
const validateEmail = (rule: any, value: string, callback: any) => {
|
|
if (value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
callback(new Error('请输入正确的邮箱地址'))
|
|
} else {
|
|
callback()
|
|
}
|
|
}
|
|
|
|
const rules = {
|
|
username: [
|
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
|
{ min: 3, message: '用户名长度不能小于3个字符', trigger: 'blur' }
|
|
],
|
|
phone: [
|
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
|
],
|
|
password: [
|
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
|
{ min: 6, message: '密码长度不能小于6个字符', trigger: 'blur' }
|
|
],
|
|
confirmPassword: [
|
|
{ required: true, message: '请再次输入密码', trigger: 'blur' },
|
|
{ validator: validatePass, trigger: 'blur' }
|
|
],
|
|
phoneCode: [
|
|
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
|
],
|
|
email: [
|
|
{ validator: validateEmail, trigger: 'blur' }
|
|
],
|
|
emailCode: [
|
|
{ required: true, message: '请输入邮箱验证码', trigger: 'blur' }
|
|
]
|
|
}
|
|
|
|
const handleRegister = async () => {
|
|
if (!registerFormRef.value) return
|
|
|
|
await registerFormRef.value.validate(async (valid) => {
|
|
if (valid) {
|
|
loading.value = true
|
|
try {
|
|
// 这里添加实的注册逻辑
|
|
// const response = await register(registerForm)
|
|
|
|
// 模拟注册成功
|
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
|
|
ElMessage.success('注册成功')
|
|
router.push('/login')
|
|
} catch (error) {
|
|
ElMessage.error('注册失败,请稍后重试')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 添加验证码相关逻辑
|
|
const phoneCooldown = ref(0)
|
|
const emailCooldown = ref(0)
|
|
|
|
// 手机验证码倒计时
|
|
const startPhoneCooldown = () => {
|
|
phoneCooldown.value = 60
|
|
const timer = setInterval(() => {
|
|
phoneCooldown.value--
|
|
if (phoneCooldown.value <= 0) {
|
|
clearInterval(timer)
|
|
}
|
|
}, 1000)
|
|
}
|
|
|
|
// 邮箱验证码倒计时
|
|
const startEmailCooldown = () => {
|
|
emailCooldown.value = 60
|
|
const timer = setInterval(() => {
|
|
emailCooldown.value--
|
|
if (emailCooldown.value <= 0) {
|
|
clearInterval(timer)
|
|
}
|
|
}, 1000)
|
|
}
|
|
|
|
// 发送手机验证码
|
|
const sendPhoneCode = () => {
|
|
if (phoneCooldown.value > 0 || !registerForm.phone) {
|
|
ElMessage.warning('请先输入手机号')
|
|
return
|
|
}
|
|
|
|
// 验证手机号格式
|
|
if (!/^1[3-9]\d{9}$/.test(registerForm.phone)) {
|
|
ElMessage.warning('请输入正确的手机号')
|
|
return
|
|
}
|
|
|
|
// 这里添加发送验证码的逻辑
|
|
ElMessage.success('手机验证码已发送')
|
|
startPhoneCooldown()
|
|
}
|
|
|
|
// 发送邮箱验证码
|
|
const sendEmailCode = async () => {
|
|
if (emailCooldown.value > 0 || !registerForm.email) return
|
|
const result = await userApi.sendVerifyEmailCode(registerForm.email)
|
|
console.log(result)
|
|
console.log(result.code)
|
|
ElMessage.success('邮箱验证码已发送')
|
|
startEmailCooldown()
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.register-container {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background: linear-gradient(45deg, #1a1a1a, #434343);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.background {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 0;
|
|
}
|
|
|
|
.shape {
|
|
position: absolute;
|
|
border-radius: 50%;
|
|
background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
|
|
animation: float 6s ease-in-out infinite;
|
|
}
|
|
|
|
.shape:nth-child(1) {
|
|
width: 500px;
|
|
height: 500px;
|
|
top: -250px;
|
|
right: -100px;
|
|
background: linear-gradient(45deg, #4481eb, #04befe);
|
|
}
|
|
|
|
.shape:nth-child(2) {
|
|
width: 400px;
|
|
height: 400px;
|
|
bottom: -200px;
|
|
left: -100px;
|
|
background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
|
|
animation-delay: -3s;
|
|
}
|
|
|
|
@keyframes float {
|
|
0% {
|
|
transform: translateY(0) rotate(0deg);
|
|
}
|
|
50% {
|
|
transform: translateY(-20px) rotate(5deg);
|
|
}
|
|
100% {
|
|
transform: translateY(0) rotate(0deg);
|
|
}
|
|
}
|
|
|
|
.register-card {
|
|
width: 1100px;
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 20px;
|
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
|
position: relative;
|
|
z-index: 1;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.register-card:hover {
|
|
transform: translateY(-5px);
|
|
}
|
|
|
|
.register-header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.logo {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.subtitle {
|
|
color: #606266;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.custom-input {
|
|
height: 50px;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.custom-input :deep(.el-input__wrapper) {
|
|
padding-left: 45px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
left: 15px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: #909399;
|
|
}
|
|
|
|
.verify-code-input {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.verify-code-input .el-input {
|
|
flex: 1;
|
|
}
|
|
|
|
.verify-code-input .el-button {
|
|
width: 120px;
|
|
height: 50px;
|
|
margin-top: 0;
|
|
}
|
|
|
|
.verify-section .verify-code-input + .el-form-item .el-input {
|
|
margin-left: calc(100% - 100%);
|
|
}
|
|
|
|
.register-button {
|
|
width: 100%;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.login-link {
|
|
text-align: center;
|
|
font-size: 14px;
|
|
color: #606266;
|
|
}
|
|
|
|
.login-link a {
|
|
color: #409EFF;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.login-link a:hover {
|
|
color: #66b1ff;
|
|
}
|
|
|
|
.divider {
|
|
text-align: center;
|
|
color: #909399;
|
|
font-size: 14px;
|
|
margin: 20px 0;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.divider::before,
|
|
.divider::after {
|
|
content: '';
|
|
flex: 1;
|
|
height: 1px;
|
|
background: #dcdfe6;
|
|
margin: 0 16px;
|
|
}
|
|
|
|
.verify-section {
|
|
background: #f8f9fa;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.verify-section .el-form-item {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.verify-section :deep(.el-form-item__label) {
|
|
float: none;
|
|
display: block;
|
|
text-align: left;
|
|
padding: 0 0 8px;
|
|
line-height: 1.5;
|
|
white-space: normal;
|
|
}
|
|
|
|
.verify-section :deep(.el-form-item__content) {
|
|
margin-left: 0 !important;
|
|
}
|
|
|
|
.verify-code-input {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: flex-start;
|
|
width: 100%;
|
|
}
|
|
|
|
.verify-code-input .el-input {
|
|
flex: 1;
|
|
}
|
|
|
|
.verify-code-input .el-button {
|
|
width: 120px;
|
|
height: 50px;
|
|
margin-top: 0;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.verify-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
color: #409EFF;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.verify-title .el-icon {
|
|
font-size: 20px;
|
|
}
|
|
|
|
/* 优化输入框样式 */
|
|
.custom-input {
|
|
height: 50px;
|
|
}
|
|
|
|
.custom-input :deep(.el-input__wrapper) {
|
|
background: white;
|
|
border: 1px solid #e4e7ed;
|
|
box-shadow: none !important;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.custom-input :deep(.el-input__wrapper:hover) {
|
|
border-color: #c0c4cc;
|
|
}
|
|
|
|
.custom-input :deep(.el-input__wrapper.is-focus) {
|
|
border-color: #409EFF;
|
|
}
|
|
|
|
/* 优化按钮样式 */
|
|
.el-button {
|
|
border-radius: 8px;
|
|
font-weight: 500;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.el-button:not(:disabled):hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
|
|
}
|
|
|
|
.register-content {
|
|
display: flex;
|
|
gap: 30px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.basic-info {
|
|
flex: 0.8;
|
|
padding-right: 30px;
|
|
border-right: 1px solid #e4e7ed;
|
|
}
|
|
|
|
.verify-info {
|
|
flex: 1.2;
|
|
}
|
|
|
|
/* 响应式布局 */
|
|
@media (max-width: 992px) {
|
|
.register-card {
|
|
width: 95%;
|
|
max-width: 500px;
|
|
}
|
|
|
|
.register-content {
|
|
flex-direction: column;
|
|
gap: 20px;
|
|
}
|
|
|
|
.basic-info {
|
|
padding-right: 0;
|
|
border-right: none;
|
|
border-bottom: 1px solid #e4e7ed;
|
|
padding-bottom: 20px;
|
|
flex: none;
|
|
}
|
|
|
|
.verify-info {
|
|
flex: none;
|
|
}
|
|
|
|
.verify-code-input {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.verify-section .el-form-item:not(:first-child) .el-input {
|
|
width: 100%;
|
|
margin-left: 0;
|
|
}
|
|
|
|
.verify-code-input .el-button {
|
|
width: 100%;
|
|
margin-top: 10px;
|
|
}
|
|
}
|
|
</style>
|