Files
tyapi-frontend/src/pages/auth/Register.vue
2026-04-25 11:59:14 +08:00

259 lines
7.1 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-1">
<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>
<p class="mt-4 text-xs text-center text-slate-400">
注册即表示您同意平台相关服务条款与隐私政策
</p>
</div>
</template>
<script setup name="UserRegister">
import { useUserStore } from '@/stores/user'
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
import { ElMessage } from 'element-plus'
const router = useRouter()
const userStore = useUserStore()
const { runWithCaptcha } = useAliyunCaptcha()
// 表单数据
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 {
await runWithCaptcha(
async (captchaVerifyParam) => {
return await userStore.sendCode(form.value.phone, 'register', captchaVerifyParam)
},
(res) => {
if (res.success) {
ElMessage.success('验证码发送成功')
startCountdown()
} else {
ElMessage.error(res?.error?.message || '验证码发送失败')
}
}
)
} 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>