重组前端功能目录并统一代码规范工具链 (#10)

Reviewed-on: #10
This commit was merged in pull request #10.
This commit is contained in:
2026-09-29 18:09:38 +08:00
parent 720ad29f06
commit 0af0256c97
101 changed files with 14644 additions and 10309 deletions
+240
View File
@@ -0,0 +1,240 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { NAlert, NButton, NCard, NForm, NFormItem, NInput, NResult } from 'naive-ui'
import { AuthClientError } from '@/features/auth/authClient'
import { useAuthStore } from '@/features/auth/stores/auth'
const authStore = useAuthStore()
const route = useRoute()
const name = ref('')
const email = ref(typeof route.query.email === 'string' ? route.query.email : '')
const password = ref('')
const confirmPassword = ref('')
const error = ref('')
const loading = ref(false)
const registered = ref(false)
const registerConflict = ref(false)
async function handleRegister() {
error.value = ''
registerConflict.value = false
const trimmedName = name.value.trim()
const trimmedEmail = email.value.trim()
if (!trimmedName) {
error.value = '请输入用户名'
return
}
if (trimmedName.length > 16) {
error.value = '用户名不能超过 16 个字符'
return
}
if (!trimmedEmail || trimmedEmail.length > 64) {
error.value = '请输入不超过 64 个字符的有效邮箱'
return
}
if (password.value.length < 8) {
error.value = '密码至少需要 8 位'
return
}
if (password.value !== confirmPassword.value) {
error.value = '两次输入的密码不一致'
return
}
loading.value = true
try {
await authStore.register(trimmedName, trimmedEmail, password.value)
email.value = trimmedEmail
registered.value = true
} catch (e: unknown) {
if (
e instanceof AuthClientError &&
(e.status === 409 || e.code === 'USER_ALREADY_EXISTS')
) {
registerConflict.value = true
error.value = '邮箱或用户名已被占用;若邮箱尚未验证,可以尝试重发验证邮件。'
} else {
error.value = e instanceof Error ? e.message : '注册失败,请稍后重试'
}
} finally {
loading.value = false
}
}
</script>
<template>
<div class="min-h-[calc(100vh-4rem)] px-4 py-10">
<div
class="mx-auto grid min-w-0 max-w-6xl gap-8 lg:grid-cols-[1.1fr_0.9fr] lg:items-stretch"
>
<section
class="oc-panel-card-xl flex min-w-0 h-full flex-col justify-between overflow-hidden border border-slate-200 bg-[linear-gradient(135deg,#eff6ff_0%,#ffffff_42%,#f2faf6_100%)] p-6 sm:p-8"
>
<div>
<p class="text-sm uppercase tracking-[0.26em] text-sky-700">Observer Join</p>
<h1
class="mt-4 max-w-xl break-words text-4xl font-black leading-[1.05] text-slate-900 sm:text-5xl"
>
加入天空探索者
<span class="block text-sky-700">建立你的云图档案</span>
</h1>
</div>
<div class="mt-8 grid min-w-0 grid-cols-2 gap-4">
<div class="min-w-0 border border-slate-200 bg-white px-4 py-3">
<div class="oc-stat-label">Step 1</div>
<div class="oc-stat-value">Join</div>
<div class="oc-field-help">填写账号资料</div>
</div>
<div class="min-w-0 border border-slate-200 bg-white px-4 py-3">
<div class="oc-stat-label">Step 2</div>
<div class="oc-stat-value">Verify</div>
<div class="oc-field-help">通过邮件确认邮箱</div>
</div>
</div>
</section>
<NCard class="oc-panel-card-xl min-w-0 h-full">
<NResult
v-if="registered"
status="success"
title="确认你的邮箱"
description="如该邮箱可以注册,请查收验证邮件并点击链接。验证后请重新登录。"
>
<template #footer>
<div class="space-y-4">
<p class="text-sm text-slate-500">
目标邮箱:<span class="font-semibold text-slate-700">{{
email
}}</span>
</p>
<div class="flex flex-wrap justify-center gap-3">
<RouterLink
:to="{ path: '/resend-confirmation', query: { email } }"
class="no-underline"
>
<NButton
type="default"
class="oc-panel-button oc-panel-button--sky"
>重发验证邮件</NButton
>
</RouterLink>
<RouterLink
:to="{ path: '/login', query: { email } }"
class="no-underline"
>
<NButton
type="default"
class="oc-panel-button oc-panel-button--teal"
>去登录</NButton
>
</RouterLink>
</div>
</div>
</template>
</NResult>
<template v-else>
<div class="mb-8">
<div class="text-sm uppercase tracking-[0.22em] text-slate-500">
Create Account
</div>
<h2 class="mt-3 text-3xl font-bold text-slate-900">注册 OpenCloud</h2>
<p class="mt-2 text-sm text-slate-500">
注册后,请先通过邮件确认邮箱再登录。
</p>
</div>
<NForm @submit.prevent="handleRegister">
<NFormItem label="用户名">
<NInput
v-model:value="name"
maxlength="16"
show-count
placeholder="最多 16 个字符"
:input-props="{
autocomplete: 'username',
name: 'username',
required: true,
}"
/>
</NFormItem>
<NFormItem label="邮箱">
<NInput
v-model:value="email"
maxlength="64"
type="text"
placeholder="your@email.com"
:input-props="{
autocomplete: 'email',
inputmode: 'email',
name: 'email',
required: true,
}"
/>
</NFormItem>
<NFormItem label="密码">
<NInput
v-model:value="password"
type="password"
show-password-on="click"
placeholder="至少 8 位"
:input-props="{
autocomplete: 'new-password',
name: 'new-password',
required: true,
}"
/>
</NFormItem>
<NFormItem label="确认密码">
<NInput
v-model:value="confirmPassword"
type="password"
show-password-on="click"
placeholder="再次输入密码"
:input-props="{
autocomplete: 'new-password',
name: 'confirm-new-password',
required: true,
}"
/>
</NFormItem>
<NAlert v-if="error" type="error" class="mb-4">
{{ error }}
<div v-if="registerConflict" class="mt-2">
<RouterLink
:to="{ path: '/resend-confirmation', query: { email } }"
class="font-semibold text-rose-700 underline underline-offset-2"
>重发验证邮件</RouterLink
>
</div>
</NAlert>
<NButton
attr-type="submit"
type="primary"
block
size="large"
class="oc-primary-button oc-primary-button--sky"
:loading="loading"
>
{{ loading ? '注册中...' : '注册' }}
</NButton>
</NForm>
<p class="mt-6 text-sm text-slate-500">
已有账号?
<RouterLink
:to="{ path: '/login', query: { email } }"
class="font-semibold text-sky-700 hover:text-sky-800"
>去登录</RouterLink
>
</p>
</template>
</NCard>
</div>
</div>
</template>