149 lines
4.6 KiB
Vue
149 lines
4.6 KiB
Vue
|
|
<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>
|
|||
|
|
|