Browse Source

refactor: decompose register page into component-driven architecture

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat/registration-page
npmrun 2 weeks ago
parent
commit
c0abdbf88c
  1. 132
      app/pages/register.vue

132
app/pages/register.vue

@ -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>

Loading…
Cancel
Save