[重构] 前端改造v5

This commit is contained in:
ryan
2026-03-11 17:01:24 +08:00
parent 5d6898b633
commit e1a9cc738e
62 changed files with 212 additions and 5282 deletions
@@ -4,11 +4,12 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { marked } from 'marked';
import { useEffect, useMemo, useState } from 'react';
import { EmptyState } from '@/components/feedback/empty-state';
import { ErrorState } from '@/components/feedback/error-state';
import { InlineMessage } from '@/components/feedback/inline-message';
import { LoadingState } from '@/components/feedback/loading-state';
import { useAuth } from '@/components/providers/auth-provider';
import { EmptyState } from '@/components/feedback/empty-state';
import { ErrorState } from '@/components/feedback/error-state';
import { InlineMessage } from '@/components/feedback/inline-message';
import { LoadingState } from '@/components/feedback/loading-state';
import { TurnstileWidget } from '@/components/forms/turnstile-widget';
import { useAuth } from '@/components/providers/auth-provider';
import { PageHeader } from '@/components/layout/page-header';
import { AppCard } from '@/components/ui/app-card';
import { StatusBadge } from '@/components/ui/status-badge';
@@ -156,10 +157,11 @@ export function SettingsPage() {
const [operationFields, setOperationFields] = useState(defaultOperationFields);
const [otherFields, setOtherFields] = useState(defaultOtherFields);
const [accessToken, setAccessToken] = useState('');
const [wechatCode, setWeChatCode] = useState('');
const [emailAddress, setEmailAddress] = useState('');
const [emailCode, setEmailCode] = useState('');
const [latestRelease, setLatestRelease] = useState<LatestReleaseInfo | null>(null);
const [wechatCode, setWeChatCode] = useState('');
const [emailAddress, setEmailAddress] = useState('');
const [emailCode, setEmailCode] = useState('');
const [emailTurnstileToken, setEmailTurnstileToken] = useState('');
const [latestRelease, setLatestRelease] = useState<LatestReleaseInfo | null>(null);
const isRoot = (user?.role ?? 0) >= 100;
@@ -355,17 +357,22 @@ export function SettingsPage() {
});
};
const handleEmailVerification = () => {
if (!emailAddress.trim()) {
setFeedback({ tone: 'danger', message: '请输入要绑定的邮箱地址。' });
return;
}
void runBusyAction('email-send', async () => {
await sendEmailVerification(emailAddress.trim());
setFeedback({ tone: 'success', message: '验证码已发送,请检查邮箱。' });
});
};
const handleEmailVerification = () => {
if (!emailAddress.trim()) {
setFeedback({ tone: 'danger', message: '请输入要绑定的邮箱地址。' });
return;
}
if (publicStatusQuery.data?.turnstile_check && !emailTurnstileToken) {
setFeedback({ tone: 'info', message: '请先完成人机验证。' });
return;
}
void runBusyAction('email-send', async () => {
await sendEmailVerification(emailAddress.trim(), emailTurnstileToken || undefined);
setFeedback({ tone: 'success', message: '验证码已发送,请检查邮箱。' });
});
};
const handleBindEmail = () => {
if (!emailAddress.trim() || !emailCode.trim()) {
@@ -591,38 +598,46 @@ export function SettingsPage() {
当前状态:{profile.email ? `已绑定 ${profile.email}` : '未绑定'}
</p>
</div>
{publicStatus.turnstile_check ? (
<EmptyState
title='当前已启用 Turnstile'
description='邮箱绑定在旧版前端依赖人机校验组件。若需要绑定邮箱,请暂时使用旧前端作为兼容兜底。'
/>
) : (
<div className='space-y-4'>
<ResourceField label='邮箱地址'>
<ResourceInput
value={emailAddress}
onChange={(event) => setEmailAddress(event.target.value)}
placeholder='请输入邮箱地址'
/>
</ResourceField>
<ResourceField label='验证码'>
<ResourceInput
value={emailCode}
onChange={(event) => setEmailCode(event.target.value)}
placeholder='请输入邮箱验证码'
/>
</ResourceField>
<div className='flex flex-wrap gap-2'>
<SecondaryButton type='button' onClick={handleEmailVerification} disabled={busyKey === 'email-send'}>
{busyKey === 'email-send' ? '发送中...' : '发送验证码'}
</SecondaryButton>
<PrimaryButton type='button' onClick={handleBindEmail} disabled={busyKey === 'email-bind'}>
{busyKey === 'email-bind' ? '绑定中...' : '绑定邮箱'}
</PrimaryButton>
</div>
</div>
)}
</div>
<div className='space-y-4'>
<ResourceField label='邮箱地址'>
<ResourceInput
value={emailAddress}
onChange={(event) => setEmailAddress(event.target.value)}
placeholder='请输入邮箱地址'
/>
</ResourceField>
<ResourceField label='验证码'>
<ResourceInput
value={emailCode}
onChange={(event) => setEmailCode(event.target.value)}
placeholder='请输入邮箱验证码'
/>
</ResourceField>
{publicStatus.turnstile_check ? (
publicStatus.turnstile_site_key ? (
<TurnstileWidget
siteKey={publicStatus.turnstile_site_key}
onVerify={(token) => setEmailTurnstileToken(token)}
onExpire={() => setEmailTurnstileToken('')}
onError={() => setEmailTurnstileToken('')}
/>
) : (
<EmptyState
title='Turnstile 配置不完整'
description='当前系统已启用 Turnstile,但未配置 Site Key,邮箱绑定暂不可用。'
/>
)
) : null}
<div className='flex flex-wrap gap-2'>
<SecondaryButton type='button' onClick={handleEmailVerification} disabled={busyKey === 'email-send'}>
{busyKey === 'email-send' ? '发送中...' : '发送验证码'}
</SecondaryButton>
<PrimaryButton type='button' onClick={handleBindEmail} disabled={busyKey === 'email-bind'}>
{busyKey === 'email-bind' ? '绑定中...' : '绑定邮箱'}
</PrimaryButton>
</div>
</div>
</div>
</div>
</AppCard>
</div>