From 1495666ea6e222fb781ed62808ba563514e44463 Mon Sep 17 00:00:00 2001 From: lsy Date: Wed, 25 Feb 2026 18:38:15 +0800 Subject: [PATCH] =?UTF-8?q?add:=E6=96=B0=E5=A2=9E=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ui/src/App.tsx | 41 ++++++++++++++++++++++++++++++ apps/ui/src/core/sites.ts | 37 +++++++++++++++++++++++++++ apps/ui/src/features/SitesView.tsx | 9 +++++++ helloagents/CHANGELOG.md | 3 +++ 4 files changed, 90 insertions(+) diff --git a/apps/ui/src/App.tsx b/apps/ui/src/App.tsx index 25aa474..abd4099 100644 --- a/apps/ui/src/App.tsx +++ b/apps/ui/src/App.tsx @@ -16,7 +16,9 @@ import { import { filterSites, sortSites, + summarizeSiteTests, type SiteSortState, + type SiteTestResult, } from "./core/sites"; import type { AdminData, @@ -547,6 +549,44 @@ const App = () => { [apiFetch, loadSites], ); + const handleSiteTestAll = useCallback(async () => { + if (data.sites.length === 0) { + setNotice("暂无站点可测试"); + return; + } + setNotice("正在执行一键测试..."); + const results: SiteTestResult[] = []; + for (const site of data.sites) { + if (site.status !== "active") { + results.push({ status: "skipped" }); + continue; + } + try { + await apiFetch(`/api/channels/${site.id}/test`, { + method: "POST", + }); + results.push({ status: "success" }); + } catch (_error) { + results.push({ status: "failed" }); + } + } + await loadSites(); + const summary = summarizeSiteTests(results); + const testedTotal = summary.success + summary.failed; + if (testedTotal === 0) { + setNotice(`已跳过 ${summary.skipped} 个禁用站点,暂无可测试站点。`); + return; + } + let message = + summary.failed > 0 + ? `一键测试完成,成功 ${summary.success}/${testedTotal},失败 ${summary.failed}。` + : `一键测试完成,成功 ${summary.success}/${testedTotal}。`; + if (summary.skipped > 0) { + message += ` 已跳过 ${summary.skipped} 个禁用站点。`; + } + setNotice(message); + }, [apiFetch, data.sites, loadSites]); + const handleSiteDelete = useCallback( async (id: string) => { try { @@ -753,6 +793,7 @@ const App = () => { onSortChange={handleSiteSortChange} onFormChange={handleSiteFormChange} onRunAll={handleCheckinRunAll} + onTestAll={handleSiteTestAll} /> ); } diff --git a/apps/ui/src/core/sites.ts b/apps/ui/src/core/sites.ts index 95a0582..c4e30a5 100644 --- a/apps/ui/src/core/sites.ts +++ b/apps/ui/src/core/sites.ts @@ -17,6 +17,19 @@ export type SiteSortState = { direction: SiteSortDirection; }; +export type SiteTestStatus = "success" | "failed" | "skipped"; + +export type SiteTestResult = { + status: SiteTestStatus; +}; + +export type SiteTestSummary = { + total: number; + success: number; + failed: number; + skipped: number; +}; + export const SITE_TYPE_LABELS: Record = { "new-api": "New API", "done-hub": "Done Hub", @@ -32,6 +45,30 @@ export const getSiteTypeLabel = (siteType: Site["site_type"]) => export const getSiteStatusLabel = (status: string) => status === "active" ? "启用" : "禁用"; +export const summarizeSiteTests = ( + results: SiteTestResult[], +): SiteTestSummary => { + return results.reduce( + (acc, item) => { + acc.total += 1; + if (item.status === "success") { + acc.success += 1; + } else if (item.status === "failed") { + acc.failed += 1; + } else { + acc.skipped += 1; + } + return acc; + }, + { + total: 0, + success: 0, + failed: 0, + skipped: 0, + } as SiteTestSummary, + ); +}; + export const getSiteCheckinLabel = (site: Site, today?: string) => { const shouldShow = site.site_type === "new-api" && Boolean(site.checkin_enabled); diff --git a/apps/ui/src/features/SitesView.tsx b/apps/ui/src/features/SitesView.tsx index e848a89..ef6826e 100644 --- a/apps/ui/src/features/SitesView.tsx +++ b/apps/ui/src/features/SitesView.tsx @@ -37,6 +37,7 @@ type SitesViewProps = { onSortChange: (next: SiteSortState) => void; onFormChange: (patch: Partial) => void; onRunAll: () => void; + onTestAll: () => void; }; const pageSizeOptions = [10, 20, 50]; @@ -76,6 +77,7 @@ export const SitesView = ({ onSortChange, onFormChange, onRunAll, + onTestAll, }: SitesViewProps) => { const isEditing = Boolean(editingSite); const pageItems = buildPageItems(sitePage, siteTotalPages); @@ -156,6 +158,13 @@ export const SitesView = ({ > 一键签到 +