Files
tyapi-frontend/src/pages/sub-portal/SubRegister.vue
2026-04-25 20:48:08 +08:00

149 lines
4.6 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-6">
<h2 class="auth-title">注册账号</h2>
<p class="auth-subtitle">填写邀请码与手机号完成账号注册</p>
</div>
<form class="space-y-4" @submit.prevent="onRegister">
<div>
<label class="auth-label">邀请码 <span class="text-red-500">*</span></label>
<el-input v-model="form.inviteToken" placeholder="请输入邀请码" size="large" clearable :disabled="loading"
class="auth-input" />
</div>
<div>
<label class="auth-label">手机号</label>
<el-input v-model="form.phone" maxlength="11" placeholder="手机号" size="large" :disabled="loading" class="auth-input" />
</div>
<div>
<label class="auth-label">验证码</label>
<div class="flex gap-3">
<el-input v-model="form.code" maxlength="6" placeholder="短信验证码" size="large" :disabled="loading" class="auth-input" />
<el-button type="primary" size="large" :disabled="!canSendCode || loading" :loading="sendingCode"
class="auth-button !min-w-[120px]" @click="sendCode">
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
</el-button>
</div>
</div>
<div>
<label class="auth-label">密码</label>
<el-input v-model="form.password" type="password" show-password size="large" :disabled="loading" class="auth-input" />
</div>
<div>
<label class="auth-label">确认密码</label>
<el-input v-model="form.confirmPassword" type="password" show-password size="large" :disabled="loading"
class="auth-input" />
</div>
<div class="text-center">
<router-link to="/sub/auth/login" class="auth-link text-sm">返回登录</router-link>
</div>
<el-button type="primary" size="large" class="auth-button w-full !h-12" native-type="submit" :loading="loading"
:disabled="!canSubmit">
注册
</el-button>
</form>
<p class="mt-4 text-xs text-center text-slate-400">
请确认邀请码来源可信避免账号安全风险
</p>
</div>
</template>
<script setup>
import { subPortalApi } from '@/api'
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
const { runWithCaptcha } = useAliyunCaptcha()
const form = ref({
inviteToken: '',
phone: '',
code: '',
password: '',
confirmPassword: ''
})
const loading = ref(false)
const sendingCode = ref(false)
const countdown = ref(0)
let timer = null
// 与主站同仓同构建:链接 ?invite= 或主账号复制的完整 URL 均会打开本页并预填邀请码
watch(
() => route.query.invite,
(inv) => {
if (typeof inv === 'string' && inv) {
form.value.inviteToken = inv
}
},
{ immediate: true }
)
const canSendCode = computed(
() => form.value.phone?.length === 11 && countdown.value === 0
)
const canSubmit = computed(
() =>
form.value.inviteToken &&
form.value.phone?.length === 11 &&
form.value.code?.length === 6 &&
form.value.password?.length >= 6 &&
form.value.password === form.value.confirmPassword
)
const sendCode = async () => {
if (!canSendCode.value) return
sendingCode.value = true
try {
await runWithCaptcha(
async (captcha) => userStore.sendCode(form.value.phone, 'register', captcha),
(res) => {
if (res.success) {
ElMessage.success('验证码已发送')
countdown.value = 60
timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) clearInterval(timer)
}, 1000)
} else {
ElMessage.error(res?.error?.message || '发送失败')
}
}
)
} finally {
sendingCode.value = false
}
}
const onRegister = async () => {
if (!canSubmit.value) return
loading.value = true
try {
const res = await subPortalApi.register({
phone: form.value.phone,
password: form.value.password,
confirm_password: form.value.confirmPassword,
code: form.value.code,
invite_token: form.value.inviteToken
})
if (res?.success) {
ElMessage.success('注册成功,请登录')
router.push('/sub/auth/login')
}
} catch (e) {
ElMessage.error(e?.response?.data?.message || e?.message || '注册失败')
} finally {
loading.value = false
}
}
onUnmounted(() => {
if (timer) clearInterval(timer)
})
</script>