Files
tyapi-frontend/src/pages/auth/Register.vue
2025-11-24 16:06:44 +08:00

273 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="w-full auth-fade-in">
<!-- 标题 -->
<div class="text-center mb-8">
<h2 class="auth-title">创建账号</h2>
<p class="auth-subtitle">请填写以下信息完成注册</p>
</div>
<form class="space-y-4" @submit.prevent="onRegister">
<!-- 手机号输入 -->
<div>
<label class="auth-label">手机号</label>
<el-input
v-model="form.phone"
name="register-phone"
placeholder="请输入手机号"
size="large"
clearable
maxlength="11"
:disabled="loading"
class="auth-input"
>
<template #prefix>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path
d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"
/>
</svg>
</template>
</el-input>
</div>
<!-- 验证码输入 -->
<div>
<label class="auth-label">验证码</label>
<div class="flex gap-3">
<el-input
v-model="form.code"
name="register-code"
placeholder="请输入验证码"
size="large"
maxlength="6"
:disabled="loading"
class="auth-input"
>
<template #prefix>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M18 8A6 6 0 006 8c0 3.314-4.03 6-6 6s6 2.686 6 6a6 6 0 0012 0c0-3.314 4.03-6 6-6s-6-2.686-6-6z"
clip-rule="evenodd"
/>
</svg>
</template>
</el-input>
<el-button
type="primary"
size="large"
:disabled="!canSendCode || loading"
@click="sendCode"
:loading="sendingCode"
class="auth-button !px-6 !min-w-[120px]"
>
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
</el-button>
</div>
</div>
<!-- 密码输入 -->
<div>
<label class="auth-label">设置密码</label>
<el-input
v-model="form.password"
name="register-password"
type="password"
placeholder="请设置密码至少6位"
size="large"
show-password
:disabled="loading"
class="auth-input"
>
<template #prefix>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
clip-rule="evenodd"
/>
</svg>
</template>
</el-input>
</div>
<!-- 确认密码输入 -->
<div>
<label class="auth-label">确认密码</label>
<el-input
v-model="form.confirmPassword"
name="register-confirm-password"
type="password"
placeholder="请再次输入密码"
size="large"
show-password
:disabled="loading"
class="auth-input"
>
<template #prefix>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
clip-rule="evenodd"
/>
</svg>
</template>
</el-input>
</div>
<!-- 操作链接 -->
<div class="text-center py-2">
<router-link to="/auth/login" class="auth-link text-sm"> 已有账号去登录 </router-link>
</div>
<!-- 注册按钮 -->
<el-button
type="primary"
size="large"
class="auth-button w-full !h-12 !text-base !font-medium"
native-type="submit"
:loading="loading"
:disabled="!canSubmit"
>
{{ loading ? '注册中...' : '注册' }}
</el-button>
</form>
</div>
</template>
<script setup name="UserRegister">
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
const router = useRouter()
const userStore = useUserStore()
// 表单数据
const form = ref({
phone: '',
code: '',
password: '',
confirmPassword: '',
})
// 状态
const loading = ref(false)
const sendingCode = ref(false)
const countdown = ref(0)
let countdownTimer = null
// 计算属性
const canSendCode = computed(() => {
return form.value.phone && form.value.phone.length === 11 && countdown.value === 0
})
const canSubmit = computed(() => {
return (
form.value.phone &&
form.value.phone.length === 11 &&
form.value.code &&
form.value.code.length === 6 &&
form.value.password &&
form.value.password.length >= 6 &&
form.value.confirmPassword &&
form.value.confirmPassword === form.value.password
)
})
// 发送验证码
const sendCode = async () => {
if (!canSendCode.value) return
sendingCode.value = true
try {
const result = await userStore.sendCode(form.value.phone, 'register')
if (result.success) {
ElMessage.success('验证码发送成功')
startCountdown()
}
} catch (error) {
console.error('验证码发送失败:', error)
} finally {
sendingCode.value = false
}
}
// 开始倒计时
const startCountdown = () => {
countdown.value = 60
countdownTimer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
clearInterval(countdownTimer)
}
}, 1000)
}
// 注册
const onRegister = async () => {
if (!canSubmit.value) return
if (form.value.password !== form.value.confirmPassword) {
ElMessage.error('两次密码不一致')
return
}
loading.value = true
try {
const registerData = {
phone: form.value.phone,
password: form.value.password,
confirmPassword: form.value.confirmPassword,
code: form.value.code,
}
const result = await userStore.register(registerData)
if (result.success) {
ElMessage.success('注册成功,请登录')
router.push('/auth/login')
}
} catch (error) {
console.error('注册失败:', error)
} finally {
loading.value = false
}
}
// 组件卸载时清理定时器
onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
}
})
</script>
<style scoped>
/* 输入框样式优化 */
:deep(.el-input__wrapper) {
border-radius: 8px !important;
transition: all 0.3s ease !important;
}
:deep(.el-input__wrapper:hover) {
border-color: #3b82f6 !important;
}
:deep(.el-input__wrapper.is-focus) {
border-color: #3b82f6 !important;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1) !important;
}
/* 按钮样式优化 */
:deep(.el-button--primary) {
border-radius: 8px !important;
font-weight: 500 !important;
transition: all 0.3s ease !important;
}
:deep(.el-button--primary:hover) {
transform: translateY(-1px) !important;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3) !important;
}
</style>