- 新增 monitor.lbl 翻译子命名空间 89 键(HostMonitor/Middleware/Site/SNMP/Shared)
- 26 .tsx: import i18n → useTranslation() hook + i18n.t('中文') → t('monitor.lbl.semanticKey')
- 模块级代码(TABS/METRIC_CATEGORIES)保留 i18n.t() + 语义化键
- 1 .ts helpers 文件 phantom 键替换
- zh/en 6886→6975 +89 键对齐
- tsc -b=0, npm build✓ (17.16s), ESLint=0/53/0, go build=0, Docker healthy
84 lines
3.1 KiB
TypeScript
84 lines
3.1 KiB
TypeScript
// 拨测协议数量分布饼图 // Logan.Li
|
|
import { useMemo } from 'react';
|
|
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts';
|
|
import { MONITOR_PIE_STROKE, MonitorPieTooltip } from '../shared/monitorChartTheme';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { PieChart as PieChartIcon } from 'lucide-react';
|
|
import { PROBE_TYPE_OPTIONS, normalizeProbeType } from './helpers';
|
|
import type { SiteTask } from './helpers';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
interface Props {
|
|
tasks: SiteTask[];
|
|
}
|
|
|
|
const PROTOCOL_COLORS: Record<string, string> = {
|
|
HTTP: 'var(--dg-blue)',
|
|
SSL: '#a855f7',
|
|
TCP: '#06b6d4',
|
|
DNS: 'var(--dg-green)',
|
|
PING: '#f59e0b',
|
|
};
|
|
|
|
export function SiteProtocolPieChart({ tasks }: Props) {
|
|
const { t } = useTranslation();
|
|
const protocolData = useMemo(() => {
|
|
const counts: Record<string, number> = {};
|
|
for (const t of PROBE_TYPE_OPTIONS) counts[t] = 0;
|
|
for (const t of tasks) {
|
|
const key = normalizeProbeType(t.type);
|
|
counts[key] = (counts[key] || 0) + 1;
|
|
}
|
|
return PROBE_TYPE_OPTIONS
|
|
.map((name) => ({ name, value: counts[name] || 0, fill: PROTOCOL_COLORS[name] || 'var(--dg-text-muted)' }))
|
|
.filter((d) => d.value > 0);
|
|
}, [tasks]);
|
|
|
|
if (tasks.length === 0) {
|
|
return (
|
|
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
|
<CardContent className="p-6 text-center text-sm text-[var(--dg-text-muted)]"> {t('monitor.lbl.availability24h')} </CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<PieChartIcon className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
|
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.lbl.availability24h')} </h3>
|
|
<span className="text-[10px] text-[var(--dg-text-muted)]">共 {tasks.length} 个任务</span>
|
|
</div>
|
|
<ResponsiveContainer width="100%" height={200}>
|
|
<PieChart>
|
|
<Pie
|
|
data={protocolData}
|
|
dataKey="value"
|
|
nameKey="name"
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius={48}
|
|
outerRadius={76}
|
|
paddingAngle={2}
|
|
isAnimationActive={false}
|
|
>
|
|
{protocolData.map((entry) => (
|
|
<Cell key={entry.name} fill={entry.fill} stroke={MONITOR_PIE_STROKE} strokeWidth={2} />
|
|
))}
|
|
</Pie>
|
|
<Tooltip content={<MonitorPieTooltip />} />
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
<div className="flex flex-wrap justify-center gap-x-4 gap-y-1 mt-1">
|
|
{protocolData.map((d) => (
|
|
<div key={d.name} className="flex items-center gap-1.5 text-xs text-[var(--dg-text-muted)]">
|
|
<span className="w-2 h-2 rounded-full shrink-0" style={{ backgroundColor: d.fill }} />
|
|
{d.name} {d.value}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
} |