diff --git a/frontend/src/api/admin.ts b/frontend/src/api/admin.ts index 41d6141..d19a5fb 100644 --- a/frontend/src/api/admin.ts +++ b/frontend/src/api/admin.ts @@ -124,6 +124,37 @@ export interface ModelExport { config?: any } +// 邮件模板接口 +export interface EmailTemplateInfo { + type: string + name: string + variables: string[] + subject: string + html: string + is_custom: boolean + default_subject?: string + default_html?: string +} + +export interface EmailTemplatesResponse { + templates: EmailTemplateInfo[] +} + +export interface EmailTemplatePreviewResponse { + html: string + variables: Record +} + +export interface EmailTemplateResetResponse { + message: string + template: { + type: string + name: string + subject: string + html: string + } +} + // Provider 模型查询响应 export interface ProviderModelsQueryResponse { success: boolean @@ -395,5 +426,52 @@ export const adminApi = { config ) return response.data + }, + + // 邮件模板相关 + // 获取所有邮件模板 + async getEmailTemplates(): Promise { + const response = await apiClient.get('/api/admin/system/email/templates') + return response.data + }, + + // 获取指定类型的邮件模板 + async getEmailTemplate(templateType: string): Promise { + const response = await apiClient.get( + `/api/admin/system/email/templates/${templateType}` + ) + return response.data + }, + + // 更新邮件模板 + async updateEmailTemplate( + templateType: string, + data: { subject?: string; html?: string } + ): Promise<{ message: string }> { + const response = await apiClient.put<{ message: string }>( + `/api/admin/system/email/templates/${templateType}`, + data + ) + return response.data + }, + + // 预览邮件模板 + async previewEmailTemplate( + templateType: string, + data?: { html?: string } & Record + ): Promise { + const response = await apiClient.post( + `/api/admin/system/email/templates/${templateType}/preview`, + data || {} + ) + return response.data + }, + + // 重置邮件模板为默认值 + async resetEmailTemplate(templateType: string): Promise { + const response = await apiClient.post( + `/api/admin/system/email/templates/${templateType}/reset` + ) + return response.data } } diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index e0079d5..17355f0 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -51,6 +51,18 @@ export interface VerifyEmailResponse { success: boolean } +export interface VerificationStatusRequest { + email: string +} + +export interface VerificationStatusResponse { + email: string + has_pending_code: boolean + is_verified: boolean + cooldown_remaining: number | null + code_expires_in: number | null +} + export interface RegisterRequest { email: string username: string @@ -67,7 +79,6 @@ export interface RegisterResponse { export interface RegistrationSettingsResponse { enable_registration: boolean require_email_verification: boolean - verification_code_expire_minutes?: number } export interface User { @@ -154,5 +165,13 @@ export const authApi = { '/api/auth/registration-settings' ) return response.data + }, + + async getVerificationStatus(email: string): Promise { + const response = await apiClient.post( + '/api/auth/verification-status', + { email } + ) + return response.data } } diff --git a/frontend/src/components/ui/dialog/Dialog.vue b/frontend/src/components/ui/dialog/Dialog.vue index c0cba50..a77759a 100644 --- a/frontend/src/components/ui/dialog/Dialog.vue +++ b/frontend/src/components/ui/dialog/Dialog.vue @@ -71,8 +71,8 @@ - -
+ +
@@ -105,6 +105,7 @@ const props = defineProps<{ icon?: Component // Lucide icon component iconClass?: string // Custom icon color class zIndex?: number // Custom z-index for nested dialogs (default: 60) + noPadding?: boolean // Disable default content padding }>() // Emits 定义 diff --git a/frontend/src/components/ui/input.vue b/frontend/src/components/ui/input.vue index 5f25280..4dea378 100644 --- a/frontend/src/components/ui/input.vue +++ b/frontend/src/components/ui/input.vue @@ -3,6 +3,9 @@ :class="inputClass" :value="modelValue" :autocomplete="autocompleteAttr" + :data-lpignore="disableAutofill ? 'true' : undefined" + :data-1p-ignore="disableAutofill ? 'true' : undefined" + :data-form-type="disableAutofill ? 'other' : undefined" v-bind="$attrs" @input="handleInput" > @@ -16,6 +19,7 @@ interface Props { modelValue?: string | number class?: string autocomplete?: string + disableAutofill?: boolean } const props = defineProps() @@ -23,7 +27,12 @@ const emit = defineEmits<{ 'update:modelValue': [value: string] }>() -const autocompleteAttr = computed(() => props.autocomplete ?? 'off') +const autocompleteAttr = computed(() => { + if (props.disableAutofill) { + return 'one-time-code' + } + return props.autocomplete ?? 'off' +}) const inputClass = computed(() => cn( diff --git a/frontend/src/features/auth/components/RegisterDialog.vue b/frontend/src/features/auth/components/RegisterDialog.vue index 9cc3632..0db304e 100644 --- a/frontend/src/features/auth/components/RegisterDialog.vue +++ b/frontend/src/features/auth/components/RegisterDialog.vue @@ -3,42 +3,41 @@ v-model:open="isOpen" size="lg" > - - -
-
+
+ +
+
Logo
+

+ 注册新账户 +

+

+ 请填写您的邮箱和个人信息完成注册 +

- - - 注册新账户 - - - 请填写您的邮箱和个人信息完成注册 - - - +
- +
@@ -46,110 +45,127 @@
- +
- -

- 验证码错误,请重新输入 -

-

- ✓ 邮箱验证成功 -

+
+ +
+ + + + + 正在发送验证码... +
+ + +
- +
- + -

- 密码长度至少 8 位 -

- +
- - - - -
-
+ +
已有账户?
- +
+ + diff --git a/frontend/src/views/admin/SystemSettings.vue b/frontend/src/views/admin/SystemSettings.vue index a95fad3..ae5a01b 100644 --- a/frontend/src/views/admin/SystemSettings.vue +++ b/frontend/src/views/admin/SystemSettings.vue @@ -185,218 +185,6 @@
- - -
-
- - -

- 邮件服务器地址 -

-
- -
- - -

- 常用端口: 587 (TLS), 465 (SSL), 25 (非加密) -

-
- -
- - -

- 通常是您的邮箱地址 -

-
- -
- - -

- 邮箱密码或应用专用密码 -

-
- -
- - -

- 显示为发件人的邮箱地址 -

-
- -
- - -

- 显示为发件人的名称 -

-
- -
- - -

- 验证码的有效时间 -

-
- -
-
- -
- -

- 推荐开启以提高安全性 -

-
-
-
- -
-
- -
- -

- 部分服务需要隐式 SSL,一般使用端口 465 -

-
-
-
-
- -
- -
- -
-

- {{ smtpTestResult.success ? '✓ SMTP 连接测试成功' : '✗ SMTP 连接测试失败' }} -

-

- {{ smtpTestResult.message }} -

-
-
-