|
|
@ -1,134 +1,56 @@ |
|
|
<script setup lang="ts"> |
|
|
<script setup lang="ts"> |
|
|
import { clientRegisterSchema } from 'shared/auth-schema' |
|
|
const toast = useToast() |
|
|
|
|
|
|
|
|
const formSchema = clientRegisterSchema |
|
|
|
|
|
|
|
|
|
|
|
const state = reactive({ |
|
|
|
|
|
username: '', |
|
|
|
|
|
password: '', |
|
|
|
|
|
confirmPassword: '', |
|
|
|
|
|
captchaText: '', |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const captchaToken = ref('') |
|
|
const captchaToken = ref('') |
|
|
const captchaSvg = ref('') |
|
|
const captchaSvg = ref('') |
|
|
const loading = ref(false) |
|
|
|
|
|
const toast = useToast() |
|
|
|
|
|
|
|
|
|
|
|
async function fetchCaptcha() { |
|
|
async function fetchCaptcha() { |
|
|
|
|
|
try { |
|
|
const res = await $fetch<{ code: number; data: { token: string; svg: string } }>( |
|
|
const res = await $fetch<{ code: number; data: { token: string; svg: string } }>( |
|
|
'/api/auth/captcha' |
|
|
'/api/auth/captcha' |
|
|
) |
|
|
) |
|
|
captchaToken.value = res.data.token |
|
|
captchaToken.value = res.data.token |
|
|
captchaSvg.value = res.data.svg |
|
|
captchaSvg.value = res.data.svg |
|
|
|
|
|
} catch { |
|
|
|
|
|
captchaSvg.value = '' |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async function onSubmit() { |
|
|
|
|
|
if (loading.value) return |
|
|
|
|
|
loading.value = true |
|
|
|
|
|
try { |
|
|
|
|
|
const res = await $fetch<{ code: number; message: string; data: unknown }>( |
|
|
|
|
|
'/api/auth/register', |
|
|
|
|
|
{ |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
body: { |
|
|
|
|
|
username: state.username, |
|
|
|
|
|
password: state.password, |
|
|
|
|
|
confirmPassword: state.confirmPassword, |
|
|
|
|
|
captchaToken: captchaToken.value, |
|
|
|
|
|
captchaText: state.captchaText, |
|
|
|
|
|
}, |
|
|
|
|
|
} |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if (res.code !== 0) { |
|
|
|
|
|
toast.add({ title: res.message, color: 'error' }) |
|
|
|
|
|
if (res.message.includes('验证码')) { |
|
|
|
|
|
fetchCaptcha() |
|
|
|
|
|
state.captchaText = '' |
|
|
|
|
|
} |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function handleSuccess() { |
|
|
navigateTo('/login?registered=1') |
|
|
navigateTo('/login?registered=1') |
|
|
} catch { |
|
|
|
|
|
toast.add({ title: '注册失败,请稍后重试', color: 'error' }) |
|
|
|
|
|
} finally { |
|
|
|
|
|
loading.value = false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
onMounted(async () => { |
|
|
function handleError(message: string) { |
|
|
try { |
|
|
toast.add({ title: message, color: 'error' }) |
|
|
await fetchCaptcha() |
|
|
|
|
|
} catch { |
|
|
|
|
|
toast.add({ title: '验证码加载失败,请刷新页面', color: 'error' }) |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
|
|
fetchCaptcha() |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
useHead({ title: '注册' }) |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<template> |
|
|
<template> |
|
|
<div class="flex items-center justify-center py-8"> |
|
|
<div class="flex items-center justify-center min-h-[calc(100vh-200px)] py-8"> |
|
|
<UCard class="w-full max-w-[380px]"> |
|
|
<UCard class="w-full max-w-[400px]"> |
|
|
<template #header> |
|
|
<template #header> |
|
|
<h1 class="text-xl font-semibold">Create Account</h1> |
|
|
<h1 class="text-xl font-semibold">创建账号</h1> |
|
|
<p class="text-sm text-muted mt-1">Fill in the details to get started</p> |
|
|
<p class="text-sm text-(--ui-text-muted) mt-1">填写信息开始使用</p> |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<UForm :state="state" :schema="formSchema" @submit="onSubmit"> |
|
|
<RegisterForm |
|
|
<UFormField name="username" label="Username" required class="mb-4"> |
|
|
:captcha-svg="captchaSvg" |
|
|
<UInput v-model="state.username" placeholder="Enter your username" /> |
|
|
:captcha-token="captchaToken" |
|
|
</UFormField> |
|
|
@refresh-captcha="fetchCaptcha" |
|
|
|
|
|
@success="handleSuccess" |
|
|
<UFormField name="password" label="Password" required class="mb-4"> |
|
|
@error="handleError" |
|
|
<UInput |
|
|
|
|
|
v-model="state.password" |
|
|
|
|
|
type="password" |
|
|
|
|
|
placeholder="At least 8 characters" |
|
|
|
|
|
/> |
|
|
/> |
|
|
</UFormField> |
|
|
|
|
|
|
|
|
|
|
|
<UFormField name="confirmPassword" label="Confirm Password" required class="mb-4"> |
|
|
|
|
|
<UInput |
|
|
|
|
|
v-model="state.confirmPassword" |
|
|
|
|
|
type="password" |
|
|
|
|
|
placeholder="Re-enter your password" |
|
|
|
|
|
/> |
|
|
|
|
|
</UFormField> |
|
|
|
|
|
|
|
|
|
|
|
<UFormField name="captchaText" label="Captcha" required class="mb-4"> |
|
|
|
|
|
<div class="flex gap-2 items-start"> |
|
|
|
|
|
<!-- eslint-disable-next-line vue/no-v-html --> |
|
|
|
|
|
<div |
|
|
|
|
|
class="flex-1 h-10 border rounded-md overflow-hidden bg-[#f8f9fa]" |
|
|
|
|
|
role="img" |
|
|
|
|
|
aria-label="CAPTCHA verification code" |
|
|
|
|
|
v-html="captchaSvg" |
|
|
|
|
|
/> |
|
|
|
|
|
<UButton |
|
|
|
|
|
variant="ghost" |
|
|
|
|
|
color="neutral" |
|
|
|
|
|
icon="i-lucide-refresh-cw" |
|
|
|
|
|
square |
|
|
|
|
|
@click="fetchCaptcha" |
|
|
|
|
|
/> |
|
|
|
|
|
</div> |
|
|
|
|
|
<UInput |
|
|
|
|
|
v-model="state.captchaText" |
|
|
|
|
|
placeholder="Enter the code above" |
|
|
|
|
|
class="mt-2" |
|
|
|
|
|
/> |
|
|
|
|
|
</UFormField> |
|
|
|
|
|
|
|
|
|
|
|
<UButton type="submit" block :loading="loading" class="mt-6"> |
|
|
|
|
|
Create Account |
|
|
|
|
|
</UButton> |
|
|
|
|
|
</UForm> |
|
|
|
|
|
|
|
|
|
|
|
<template #footer> |
|
|
<template #footer> |
|
|
<p class="text-sm text-center text-muted"> |
|
|
<p class="text-sm text-center text-(--ui-text-muted)"> |
|
|
Already have an account? |
|
|
已有账号? |
|
|
<NuxtLink to="/login" class="text-primary hover:underline">Log in</NuxtLink> |
|
|
<NuxtLink to="/login" class="text-(--ui-primary) hover:underline">去登录</NuxtLink> |
|
|
</p> |
|
|
</p> |
|
|
</template> |
|
|
</template> |
|
|
</UCard> |
|
|
</UCard> |
|
|
|