refactor: 前端架构重构 — hook拆分、组件拆分、类型筛选器动态化、Meta API
- 后端新增 GET /api/meta 端点(checkerRegistry.supportedTypes)及 MetaResponse 类型 - 前端 hook 拆分为 use-queries.ts(全局查询+useMeta)和 use-target-detail.ts(Drawer状态) - TargetDetailDrawer 拆分为 OverviewTab + HistoryTab + history-table-columns + stats.ts - 类型筛选器由 meta API 动态驱动,删除 target-type-display 静态映射 - 列定义改为工厂函数 createTargetTableColumns(checkerTypes),TargetGroup 新增 columns prop - 修复 StatusDonut key、StatusBar maxSlots prop、TrendChart 移除 loading prop - 补充 utils/time、utils/stats、动态列工厂测试,删除旧 mapping 测试 - 同步 delta specs 到主 specs,归档 frontend-architecture-refactor change
This commit is contained in:
@@ -7,86 +7,91 @@ import type { TargetStatus } from "../../shared/api";
|
||||
import { StatusBar } from "../components/StatusBar";
|
||||
import { StatusDot } from "../components/StatusDot";
|
||||
import { getAvailabilityProgressColor } from "./color-threshold";
|
||||
import { statusFilter, typeFilter } from "./target-table-filters";
|
||||
import { statusFilter } from "./target-table-filters";
|
||||
import { availabilitySorter, latencySorter, nameSorter } from "./target-table-sorters";
|
||||
import { getTargetTypeDisplay } from "./target-type-display";
|
||||
|
||||
export const TARGET_TABLE_COLUMNS: Array<PrimaryTableCol<TargetStatus>> = [
|
||||
{
|
||||
align: "center",
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => <StatusDot up={!!row.latestCheck?.matched} />,
|
||||
colKey: "latestCheck.matched",
|
||||
filter: statusFilter,
|
||||
fixed: "left",
|
||||
title: "#",
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
colKey: "name",
|
||||
ellipsis: true,
|
||||
sorter: nameSorter,
|
||||
sortType: "all",
|
||||
title: "名称",
|
||||
},
|
||||
{
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => (
|
||||
<Tag size="small" theme="primary" variant="light-outline">
|
||||
{getTargetTypeDisplay(row.type)}
|
||||
</Tag>
|
||||
),
|
||||
colKey: "type",
|
||||
filter: typeFilter,
|
||||
title: "类型",
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => {
|
||||
const availability = row.stats?.availability;
|
||||
if (availability === undefined || availability === null) return "-";
|
||||
const color = getAvailabilityProgressColor(availability);
|
||||
return (
|
||||
<Progress
|
||||
color={color}
|
||||
label={`${availability.toFixed(1)}%`}
|
||||
percentage={availability}
|
||||
size="small"
|
||||
theme="line"
|
||||
/>
|
||||
);
|
||||
export function createTargetTableColumns(checkerTypes: string[]): Array<PrimaryTableCol<TargetStatus>> {
|
||||
return [
|
||||
{
|
||||
align: "center",
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => <StatusDot up={!!row.latestCheck?.matched} />,
|
||||
colKey: "latestCheck.matched",
|
||||
filter: statusFilter,
|
||||
fixed: "left",
|
||||
title: "#",
|
||||
width: 60,
|
||||
},
|
||||
colKey: "stats.availability",
|
||||
sorter: availabilitySorter,
|
||||
sortType: "all",
|
||||
title: "可用率",
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => <StatusBar samples={row.recentSamples} />,
|
||||
colKey: "recentSamples",
|
||||
title: "最近状态",
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
align: "right",
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => {
|
||||
const ms = row.latestCheck?.durationMs;
|
||||
if (ms === null || ms === undefined) return <span className="text-disabled">-</span>;
|
||||
const colorClass = ms <= 100 ? "latency-ok" : ms <= 500 ? "latency-warn" : "latency-error";
|
||||
return <span className={`${colorClass} tabular-nums`}>{Math.round(ms)}ms</span>;
|
||||
{
|
||||
colKey: "name",
|
||||
ellipsis: true,
|
||||
sorter: nameSorter,
|
||||
sortType: "all",
|
||||
title: "名称",
|
||||
},
|
||||
colKey: "latestCheck.durationMs",
|
||||
sorter: latencySorter,
|
||||
sortType: "all",
|
||||
title: "延迟",
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
colKey: "interval",
|
||||
title: "间隔",
|
||||
width: 72,
|
||||
},
|
||||
];
|
||||
{
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => (
|
||||
<Tag size="small" theme="primary" variant="light-outline">
|
||||
{row.type}
|
||||
</Tag>
|
||||
),
|
||||
colKey: "type",
|
||||
filter: createTypeFilter(checkerTypes),
|
||||
title: "类型",
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => {
|
||||
const availability = row.stats?.availability;
|
||||
if (availability === undefined || availability === null) return "-";
|
||||
const color = getAvailabilityProgressColor(availability);
|
||||
return (
|
||||
<Progress
|
||||
color={color}
|
||||
label={`${availability.toFixed(1)}%`}
|
||||
percentage={availability}
|
||||
size="small"
|
||||
theme="line"
|
||||
/>
|
||||
);
|
||||
},
|
||||
colKey: "stats.availability",
|
||||
sorter: availabilitySorter,
|
||||
sortType: "all",
|
||||
title: "可用率",
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => <StatusBar samples={row.recentSamples} />,
|
||||
colKey: "recentSamples",
|
||||
title: "最近状态",
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
align: "right",
|
||||
cell: ({ row }: PrimaryTableCellParams<TargetStatus>) => {
|
||||
const ms = row.latestCheck?.durationMs;
|
||||
if (ms === null || ms === undefined) return <span className="text-disabled">-</span>;
|
||||
const colorClass = ms <= 100 ? "latency-ok" : ms <= 500 ? "latency-warn" : "latency-error";
|
||||
return <span className={`${colorClass} tabular-nums`}>{Math.round(ms)}ms</span>;
|
||||
},
|
||||
colKey: "latestCheck.durationMs",
|
||||
sorter: latencySorter,
|
||||
sortType: "all",
|
||||
title: "延迟",
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
colKey: "interval",
|
||||
title: "间隔",
|
||||
width: 72,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export { statusFilter, typeFilter } from "./target-table-filters";
|
||||
export { availabilitySorter, latencySorter, nameSorter, statusSorter } from "./target-table-sorters";
|
||||
function createTypeFilter(checkerTypes: string[]): PrimaryTableCol["filter"] {
|
||||
return {
|
||||
list: [{ label: "全部", value: "" }, ...checkerTypes.map((type) => ({ label: type, value: type }))],
|
||||
type: "single",
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user