优化仪表板页面,移除冗余组件,简化代码结构

This commit is contained in:
ryan
2026-03-14 16:17:43 +08:00
parent ee104f0ad8
commit 590e1d7a8a
11 changed files with 150 additions and 188 deletions
-5
View File
@@ -1,13 +1,8 @@
import { DashboardOverview } from '@/features/dashboard/components/dashboard-overview'; import { DashboardOverview } from '@/features/dashboard/components/dashboard-overview';
import { PageHeader } from '@/components/layout/page-header';
export default function DashboardPage() { export default function DashboardPage() {
return ( return (
<div className='space-y-6'> <div className='space-y-6'>
<PageHeader
title='ATSFlare 管理端'
description='集中展示系统核心数据概览,快速导航至各功能模块进行管理和配置。'
/>
<DashboardOverview /> <DashboardOverview />
</div> </div>
); );
@@ -235,7 +235,6 @@ export function DashboardOverview() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<WorldStage <WorldStage
generatedAt={overview.generated_at}
summary={overview.summary} summary={overview.summary}
traffic={overview.traffic} traffic={overview.traffic}
capacity={overview.capacity} capacity={overview.capacity}
@@ -13,7 +13,6 @@ import type {
DistributionItem, DistributionItem,
} from '@/features/dashboard/types'; } from '@/features/dashboard/types';
import { cn } from '@/lib/utils/cn'; import { cn } from '@/lib/utils/cn';
import { formatDateTime } from '@/lib/utils/date';
type Tone = 'healthy' | 'warning' | 'danger'; type Tone = 'healthy' | 'warning' | 'danger';
@@ -157,14 +156,12 @@ function CountrySignal({
} }
export function WorldStage({ export function WorldStage({
generatedAt,
summary, summary,
traffic, traffic,
capacity, capacity,
nodes, nodes,
sourceCountries, sourceCountries,
}: { }: {
generatedAt: string;
summary: DashboardSummary; summary: DashboardSummary;
traffic: DashboardTraffic; traffic: DashboardTraffic;
capacity: DashboardCapacity; capacity: DashboardCapacity;
@@ -236,43 +233,14 @@ export function WorldStage({
)} )}
> >
<div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between"> <div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between">
<div className="space-y-2"> <p
<p
className={cn( className={cn(
'text-[11px] tracking-[0.34em] uppercase', 'text-[11px] tracking-[0.34em] uppercase',
isDark ? 'text-sky-200/80' : 'text-sky-700/80', isDark ? 'text-sky-200/80' : 'text-sky-700/80',
)} )}
>
Global Stage
</p>
<h2
className={cn(
'text-2xl font-semibold',
isDark ? 'text-white' : 'text-slate-950',
)}
>
全球态势板
</h2>
<p
className={cn(
'max-w-3xl text-sm leading-6',
isDark ? 'text-slate-300' : 'text-slate-600',
)}
>
在同一视图汇总节点在线率、运行状态、资源负载与全球流量来源,
让首屏可以直接承担值守与研判入口。
</p>
</div>
<div
className={cn(
'rounded-full border px-4 py-2 text-sm backdrop-blur',
isDark
? 'border-white/10 bg-white/6 text-slate-200'
: 'border-slate-200/80 bg-white/80 text-slate-700',
)}
> >
数据生成于 {formatDateTime(generatedAt)} Global Stage
</div> </p>
</div> </div>
</div> </div>
@@ -320,7 +320,7 @@ export function NodeDetailPage({ nodeId }: { nodeId: string }) {
const saveMutation = useMutation({ const saveMutation = useMutation({
mutationFn: async (payload: Parameters<typeof updateNode>[1]) => mutationFn: async (payload: Parameters<typeof updateNode>[1]) =>
updateNode(Number(nodeId), payload), updateNode(Number(nodeId), payload),
onSuccess: async (updatedNode) => { onSuccess: async () => {
setFeedback({ tone: 'success', message: '节点已更新。' }); setFeedback({ tone: 'success', message: '节点已更新。' });
setIsEditorOpen(false); setIsEditorOpen(false);
await queryClient.invalidateQueries({ queryKey: nodesQueryKey }); await queryClient.invalidateQueries({ queryKey: nodesQueryKey });
@@ -161,13 +161,6 @@ function isCacheLevelsValue(value: string) {
return /^\d{1,2}(?::\d{1,2}){0,2}$/.test(value.trim()); return /^\d{1,2}(?::\d{1,2}){0,2}$/.test(value.trim());
} }
function extractMainConfigLines(content: string, matcher: RegExp) {
return content
.split('\n')
.map((line) => line.trim())
.filter((line) => line && matcher.test(line));
}
export function PerformancePage() { export function PerformancePage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const {user} = useAuth(); const {user} = useAuth();
@@ -595,15 +588,6 @@ export function PerformancePage() {
); );
} }
const cachePreviewLines = extractMainConfigLines(
preview.main_config,
/proxy_cache_path|proxy_cache_key|proxy_cache_lock(?:_timeout)?|proxy_cache_use_stale/,
);
const gzipPreviewLines = extractMainConfigLines(
preview.main_config,
/gzip(?:_| )/,
);
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<PageHeader <PageHeader
@@ -196,48 +196,54 @@ export function TlsCertificatesPage() {
</AppCard> </AppCard>
</div> </div>
<CertificateImportModal {isImportOpen ? (
isOpen={isImportOpen} <CertificateImportModal
onClose={() => setIsImportOpen(false)} isOpen={isImportOpen}
onImported={(certificate) => { onClose={() => setIsImportOpen(false)}
setFeedback({ onImported={(certificate) => {
tone: 'success', setFeedback({
message: `证书 ${certificate.name} 已导入。`, tone: 'success',
}); message: `证书 ${certificate.name} 已导入。`,
}} });
/> }}
/>
) : null}
<CertificateDetailModal {isDetailOpen ? (
certificateId={selectedCertificateId} <CertificateDetailModal
isOpen={isDetailOpen} certificateId={selectedCertificateId}
onClose={() => setIsDetailOpen(false)} isOpen={isDetailOpen}
onEdit={() => { onClose={() => setIsDetailOpen(false)}
setIsDetailOpen(false); onEdit={() => {
setIsEditorOpen(true);
}}
onDelete={() => {
const certificate = certificates.find(
(item) => item.id === selectedCertificateId,
);
if (certificate) {
setIsDetailOpen(false); setIsDetailOpen(false);
handleDeleteCertificate(certificate); setIsEditorOpen(true);
} }}
}} onDelete={() => {
deleting={deleteCertificateMutation.isPending} const certificate = certificates.find(
/> (item) => item.id === selectedCertificateId,
);
if (certificate) {
setIsDetailOpen(false);
handleDeleteCertificate(certificate);
}
}}
deleting={deleteCertificateMutation.isPending}
/>
) : null}
<CertificateEditorModal {isEditorOpen ? (
certificateId={selectedCertificateId} <CertificateEditorModal
isOpen={isEditorOpen} certificateId={selectedCertificateId}
onClose={() => setIsEditorOpen(false)} isOpen={isEditorOpen}
onSaved={(certificate) => { onClose={() => setIsEditorOpen(false)}
setFeedback({ onSaved={(certificate) => {
tone: 'success', setFeedback({
message: `证书 ${certificate.name} 已更新。`, tone: 'success',
}); message: `证书 ${certificate.name} 已更新。`,
}} });
/> }}
/>
) : null}
</> </>
); );
} }
@@ -434,62 +434,70 @@ export function WebsiteDetailPage({websiteId}: { websiteId: string }) {
</AppCard> </AppCard>
</div> </div>
<WebsiteEditorModal {isEditorOpen ? (
isOpen={isEditorOpen} <WebsiteEditorModal
onClose={() => { isOpen={isEditorOpen}
setIsEditorOpen(false); onClose={() => {
setPreferredCertificateId(null); setIsEditorOpen(false);
}} setPreferredCertificateId(null);
initialDomain={website} }}
certificates={certificates} initialDomain={website}
certificatesLoading={certificatesQuery.isLoading} certificates={certificates}
preferredCertificateId={preferredCertificateId} certificatesLoading={certificatesQuery.isLoading}
onRequestImportCertificate={() => setIsCertificateImportOpen(true)} preferredCertificateId={preferredCertificateId}
onSaved={() => { onRequestImportCertificate={() => setIsCertificateImportOpen(true)}
setPreferredCertificateId(null); onSaved={() => {
setFeedback({tone: 'success', message: '网站已更新。'}); setPreferredCertificateId(null);
}} setFeedback({tone: 'success', message: '网站已更新。'});
/> }}
/>
) : null}
<CertificateImportModal {isCertificateImportOpen ? (
isOpen={isCertificateImportOpen} <CertificateImportModal
onClose={() => setIsCertificateImportOpen(false)} isOpen={isCertificateImportOpen}
onImported={(importedCertificate) => { onClose={() => setIsCertificateImportOpen(false)}
setPreferredCertificateId(importedCertificate.id); onImported={(importedCertificate) => {
setFeedback({ setPreferredCertificateId(importedCertificate.id);
tone: 'success', setFeedback({
message: `证书 ${importedCertificate.name} 已导入,可在编辑网站时直接应用。`, tone: 'success',
}); message: `证书 ${importedCertificate.name} 已导入,可在编辑网站时直接应用。`,
setIsEditorOpen(true); });
}} setIsEditorOpen(true);
/> }}
/>
) : null}
<CertificateDetailModal {isCertificateDetailOpen ? (
certificateId={certificate?.id ?? null} <CertificateDetailModal
isOpen={isCertificateDetailOpen} certificateId={certificate?.id ?? null}
onClose={() => setIsCertificateDetailOpen(false)} isOpen={isCertificateDetailOpen}
onEdit={() => { onClose={() => setIsCertificateDetailOpen(false)}
setIsCertificateDetailOpen(false); onEdit={() => {
setIsCertificateEditorOpen(true); setIsCertificateDetailOpen(false);
}} setIsCertificateEditorOpen(true);
onDelete={() => { }}
setIsCertificateDetailOpen(false); onDelete={() => {
handleDeleteCertificate(); setIsCertificateDetailOpen(false);
}} handleDeleteCertificate();
deleting={deleteCertificateMutation.isPending} }}
/> deleting={deleteCertificateMutation.isPending}
/>
) : null}
<CertificateEditorModal {isCertificateEditorOpen ? (
certificateId={certificate?.id ?? null} <CertificateEditorModal
isOpen={isCertificateEditorOpen} certificateId={certificate?.id ?? null}
onClose={() => setIsCertificateEditorOpen(false)} isOpen={isCertificateEditorOpen}
onSaved={(updatedCertificate) => { onClose={() => setIsCertificateEditorOpen(false)}
setFeedback({ onSaved={(updatedCertificate) => {
tone: 'success', setFeedback({
message: `证书 ${updatedCertificate.name} 已更新。`, tone: 'success',
}); message: `证书 ${updatedCertificate.name} 已更新。`,
}} });
/> }}
/>
) : null}
</> </>
); );
} }
@@ -114,7 +114,7 @@ export function WebsiteEditorModal({
? toManagedDomainFormValues(initialDomain) ? toManagedDomainFormValues(initialDomain)
: defaultManagedDomainValues, : defaultManagedDomainValues,
); );
}, [form, initialDomain, isOpen]); }, [form, initialDomain, isOpen, saveMutation]);
useEffect(() => { useEffect(() => {
if (!isOpen || !preferredCertificateId) { if (!isOpen || !preferredCertificateId) {
@@ -28,7 +28,6 @@ import {
DangerButton, DangerButton,
PrimaryButton, PrimaryButton,
} from '@/features/shared/components/resource-primitives'; } from '@/features/shared/components/resource-primitives';
import { formatDateTime } from '@/lib/utils/date';
type FeedbackState = { type FeedbackState = {
tone: 'info' | 'success' | 'danger'; tone: 'info' | 'success' | 'danger';
@@ -205,36 +204,40 @@ export function WebsitesPage() {
</AppCard> </AppCard>
</div> </div>
<WebsiteEditorModal {isWebsiteModalOpen ? (
isOpen={isWebsiteModalOpen} <WebsiteEditorModal
onClose={() => { isOpen={isWebsiteModalOpen}
setIsWebsiteModalOpen(false); onClose={() => {
setPreferredCertificateId(null); setIsWebsiteModalOpen(false);
}} setPreferredCertificateId(null);
certificates={certificates} }}
certificatesLoading={certificatesQuery.isLoading} certificates={certificates}
preferredCertificateId={preferredCertificateId} certificatesLoading={certificatesQuery.isLoading}
onRequestImportCertificate={() => setIsCertificateImportOpen(true)} preferredCertificateId={preferredCertificateId}
onSaved={(_, mode) => { onRequestImportCertificate={() => setIsCertificateImportOpen(true)}
setPreferredCertificateId(null); onSaved={(_, mode) => {
setFeedback({ setPreferredCertificateId(null);
tone: 'success', setFeedback({
message: mode === 'create' ? '网站已创建。' : '网站已更新。', tone: 'success',
}); message: mode === 'create' ? '网站已创建。' : '网站已更新。',
}} });
/> }}
/>
) : null}
<CertificateImportModal {isCertificateImportOpen ? (
isOpen={isCertificateImportOpen} <CertificateImportModal
onClose={() => setIsCertificateImportOpen(false)} isOpen={isCertificateImportOpen}
onImported={(certificate) => { onClose={() => setIsCertificateImportOpen(false)}
setPreferredCertificateId(certificate.id); onImported={(certificate) => {
setFeedback({ setPreferredCertificateId(certificate.id);
tone: 'success', setFeedback({
message: `证书 ${certificate.name} 已导入,可直接用于当前网站。`, tone: 'success',
}); message: `证书 ${certificate.name} 已导入,可直接用于当前网站。`,
}} });
/> }}
/>
) : null}
</> </>
); );
} }
@@ -2,7 +2,6 @@ import type {
ManagedDomainItem, ManagedDomainItem,
ManagedDomainMutationPayload, ManagedDomainMutationPayload,
} from '@/features/managed-domains/types'; } from '@/features/managed-domains/types';
import type { ProxyRouteItem } from '@/features/proxy-routes/types';
import type { import type {
TlsCertificateFileImportPayload, TlsCertificateFileImportPayload,
TlsCertificateItem, TlsCertificateItem,