refactor(i18n): U21 Zabbix 组件 i18n 治理 — ZabbixDatasourceManager + ZabbixDashboard 从 i18n.t() 全局改为 useTranslation() hook

- ZabbixDatasourceManager.tsx (415L): 30 处 i18n.t()/硬编码中文清零, 删除 5 个 monitor.zabbixdatasourc.* hash 键, 新增 monitor.zabbixDs.* 语义化命名空间 (23 键)
- ZabbixDashboard.tsx (162L): 12 处 i18n.t()/硬编码中文清零, 删除 monitor.zabbixdashboard.* + monitor.prometheusdashb.s5e8eb212 hash 键, 修复 3 个中文幻影键 (i18n.t('磁盘') → t('monitor.zabbixDs.disk') 等)
- 新增 common.days/common.seconds 通用单位键
- 删除 monitor.zabbixdatasourc (5 hash 键) + monitor.zabbixdashboard (1 hash 键) + monitor.prometheusdashb.s5e8eb212 hash 键
- zh/en 6767=6767 对齐, tsc -b=0, npm build✓ (17.41s), ESLint=0/43/0/0, go build=0, 17/17 测试通过
This commit is contained in:
attacker committed 2026-07-11 12:57:46 +08:00
1 parent f953ba3248
commit fba5fc46b6
4 files changed
+142 -67

No files matched your search

@@ -1,7 +1,7 @@
// Zabbix 数据源监控面板 — 接受外部配置 // Logan.Li
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
Server,
Loader2,
AlertTriangle,
@@ -17,7 +17,6 @@ import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { zabbixProxyApi } from '@/api';
import type { JsonData } from '@/types/common';
import i18n from '@/i18n';
interface ZabbixHost {
hostid: string; hostname: string; name: string; ip: string; available: string;
@@ -31,6 +30,7 @@ type SortField = 'hostname' | 'cpu_usage' | 'memory_usage' | 'disk_usage' | 'loa
interface Props { dsUrl?: string; dsUsername?: string; dsPassword?: string; }
export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword }) => {
const { t } = useTranslation();
const [connected, setConnected] = useState(false);
const [loading, setLoading] = useState(false);
const [hosts, setHosts] = useState<ZabbixHost[]>([]);
@@ -43,6 +43,7 @@ export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword
useEffect(() => {
if (dsUrl && dsUsername && dsPassword) doConnect(dsUrl, dsUsername, dsPassword);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dsUrl, dsUsername, dsPassword]);
const doConnect = async (u: string, un: string, pw: string) => {
@@ -52,7 +53,7 @@ export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword
setHosts(data.hosts || []);
setVersion(data.version || '');
setConnected(true);
} catch (e) { setConnected(false); setError((e as JsonData).message || i18n.t('aiops.llmTestFailed')); }
} catch (e) { setConnected(false); setError((e as JsonData).message || t('aiops.llmTestFailed')); }
finally { setLoading(false); }
};
@@ -99,24 +100,24 @@ export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword
<CardHeader className="py-3 px-4 pb-2">
<div className="flex items-center gap-2">
<Activity className="h-4 w-4 dg-text-blue" />
<CardTitle className="text-sm font-medium">Zabbix 主机监控</CardTitle>
<CardTitle className="text-sm font-medium">{t('monitor.zabbixDs.hostMonitor')}</CardTitle>
{version && <Badge className="text-[10px] bg-blue-500/10 dg-text-blue">v{version}</Badge>}
{connected && <span className="text-[10px] text-[var(--dg-text-muted)] ml-2">{hosts.length} 台</span>}
{connected && <div className="ml-auto flex gap-1"><Button variant="ghost" size="sm" className="h-6 px-2 text-[10px]" onClick={refresh}><RefreshCw className="w-3 h-3 mr-1" /> {i18n.t('monitor.dashboard.refresh')} </Button></div>}
{connected && <span className="text-[10px] text-[var(--dg-text-muted)] ml-2">{hosts.length} {t('monitor.zabbixDs.unit')}</span>}
{connected && <div className="ml-auto flex gap-1"><Button variant="ghost" size="sm" className="h-6 px-2 text-[10px]" onClick={refresh}><RefreshCw className="w-3 h-3 mr-1" /> {t('monitor.dashboard.refresh')} </Button></div>}
</div>
</CardHeader>
<CardContent className="px-4 pb-3 pt-0">
{loading ? (<div className="text-center py-12"><Loader2 className="h-6 w-6 mx-auto mb-2 animate-spin dg-text-blue" /><p className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.prometheusdashb.s5e8eb212')} </p></div>)
{loading ? (<div className="text-center py-12"><Loader2 className="h-6 w-6 mx-auto mb-2 animate-spin dg-text-blue" /><p className="text-xs text-[var(--dg-text-muted)]"> {t('monitor.zabbixDs.loading')} </p></div>)
: error ? (<div className="p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-xs dg-text-red flex items-start gap-2"><AlertTriangle className="h-4 w-4 mt-0.5" /><span>{error}</span></div>)
: hosts.length === 0 ? (<div className="text-center py-8 text-[var(--dg-text-muted)]"><Server className="h-8 w-8 mx-auto mb-2 dg-text-muted" /><p className="text-sm"> {i18n.t('monitor.zabbixdashboard.sdb131ab4')} </p></div>)
: hosts.length === 0 ? (<div className="text-center py-8 text-[var(--dg-text-muted)]"><Server className="h-8 w-8 mx-auto mb-2 dg-text-muted" /><p className="text-sm"> {t('monitor.zabbixDs.noHosts')} </p></div>)
: (
<div className="overflow-x-auto rounded-lg border border-[var(--dg-border)]">
<table className="w-full text-xs">
<thead className="bg-[var(--dg-bg-tertiary)] border-b border-[var(--dg-border)]"><tr>
{[
{ f: 'hostname' as SortField, l: i18n.t('kubernetes.workload.hostname') },{ f: 'cpu_usage' as SortField, l: 'CPU' },{ f: 'memory_usage' as SortField, l: i18n.t('kubernetes.pod.memory') },
{ f: 'disk_usage' as SortField, l: i18n.t('monitor.dashboard.col.disk') },{ f: 'load5' as SortField, l: 'Load5' },{ f: 'net_in_bps' as SortField, l: i18n.t('磁盘') },
{ f: 'net_out_bps' as SortField, l: i18n.t('流出') },{ f: 'problem_count' as SortField, l: i18n.t('问题') },{ f: 'uptime_days' as SortField, l: i18n.t('automation.scriptRun') },
{ f: 'hostname' as SortField, l: t('kubernetes.workload.hostname') },{ f: 'cpu_usage' as SortField, l: 'CPU' },{ f: 'memory_usage' as SortField, l: t('kubernetes.pod.memory') },
{ f: 'disk_usage' as SortField, l: t('monitor.dashboard.col.disk') },{ f: 'load5' as SortField, l: 'Load5' },{ f: 'net_in_bps' as SortField, l: t('monitor.zabbixDs.disk') },
{ f: 'net_out_bps' as SortField, l: t('monitor.zabbixDs.flowOut') },{ f: 'problem_count' as SortField, l: t('monitor.zabbixDs.problems') },{ f: 'uptime_days' as SortField, l: t('automation.scriptRun') },
].map(({ f, l }) => (
<th key={f} className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)] cursor-pointer hover:text-[var(--dg-text-primary)] select-none whitespace-nowrap" onClick={() => toggleSort(f)}>
<div className="flex items-center gap-1">{l}<SI f={f} /></div></th>
@@ -140,15 +141,15 @@ export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword
{sorted.length > pageSize && (
<div className="flex items-center justify-between px-3 py-2 bg-[var(--dg-bg-tertiary)] border-t border-[var(--dg-border)] text-xs">
<div className="flex items-center gap-2 text-[var(--dg-text-muted)]">
<span>共 {sorted.length} 台主机</span>
<span>{t('monitor.zabbixDs.totalHosts', { count: sorted.length })}</span>
<select className="bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded px-2 py-0.5 text-xs" value={pageSize} onChange={e => { setPageSize(Number(e.target.value)); setPage(1); }}>
<option value={10}>10/页</option><option value={20}>20/页</option><option value={50}>50/页</option>
<option value={10}>10{t('monitor.zabbixDs.perPage')}</option><option value={20}>20{t('monitor.zabbixDs.perPage')}</option><option value={50}>50{t('monitor.zabbixDs.perPage')}</option>
</select>
</div>
<div className="flex items-center gap-1">
<button className="px-2 py-0.5 rounded hover:bg-[var(--dg-bg-primary)] disabled:opacity-30" disabled={safePage <= 1} onClick={() => setPage(Math.max(1, safePage - 1))}>上一页</button>
<button className="px-2 py-0.5 rounded hover:bg-[var(--dg-bg-primary)] disabled:opacity-30" disabled={safePage <= 1} onClick={() => setPage(Math.max(1, safePage - 1))}>{t('monitor.zabbixDs.prevPage')}</button>
<span className="px-2 text-[var(--dg-text-primary)]">{safePage} / {totalPages}</span>
<button className="px-2 py-0.5 rounded hover:bg-[var(--dg-bg-primary)] disabled:opacity-30" disabled={safePage >= totalPages} onClick={() => setPage(Math.min(totalPages, safePage + 1))}>下一页</button>
<button className="px-2 py-0.5 rounded hover:bg-[var(--dg-bg-primary)] disabled:opacity-30" disabled={safePage >= totalPages} onClick={() => setPage(Math.min(totalPages, safePage + 1))}>{t('monitor.zabbixDs.nextPage')}</button>
</div>
</div>
)}