refactor(i18n): U32 全量 hash key 清理 — 151 个 hash key → 语义化键 + React Hooks 警告 53→43
- 151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key
- monitor 58 个: alertChannelDialog/alertManager/alertRulesTab/chartRangeHint/
esIndexPicker/helpers/hostMonitor/logAlertSpanel/logClusterPanel/logDingAssistant/
logQueryToolbar/logRbac/lokiLabelBrowse/metricsExplorer/middlewareMonit/
mwOverviewChart/portPolicyDialog/prometheusDashb/queryHistoryPan/savedQueriesPan/
siteMonitor/snmpMonitor/snmpOverviewChart/zabbixDashboard/zabbixDatasource
- kubernetes 42 个: deploymentBundle/deploymentEditAdvanced/deploymentEditService/
deploymentView/deploymentWorkload/eventsPage/kubernetesCluster/namespaceManage/
namespaceResource/podExecTerminal/podFileBrowser/podLogsViewer/podMetricsPage/
resourceVisualEditor/workloadViewPage
- aiops 17 个: agentCliMessage/aiAnalysisResult/analyzerHelpers/assistant/predictUtils
- agent 12 个: agentAlerts/agentMonitor/hostDetailDialog
- cloud 13 个: billingRecordDialog/billingRuleDialog/platformManager
- pipeline 6 个: buildNodesManage/deployRbac/deployTargetField
- system 3 个: auditSetting
- 58 个源文件中的 147 处 t()/i18n.t() 调用更新为语义化键路径
- 3 个命名空间冲突修复 (monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult
原 string 值提升为 .title 子键)
- React Hooks 警告修复 (53→43, -10):
- exhaustive-deps 4→0: useMemo/useCallback 添加 t 到 deps 数组
- preserve-manual-memoization 5→0: 变量遮蔽修复 (filter(t)→filter(tg))
- 1 个冗余 eslint-disable 指令移除 (CronJobManager)
- zh/en 7480=7480 完全对齐, 0 个 hash key 残留
- tsc -b=0, npm build✓ (17.59s), ESLint errors=0/warnings=43/any=0
- go build=0, 17/17 集成测试通过, Docker healthy
This commit is contained in:
1 parent
4f2d279865
commit
ff426d908b
63 files changed
+674
-477
No files matched your search
@@ -217,7 +217,7 @@ export function HostMonitor({ embedded = false }: { embedded?: boolean }) {
|
||||
<PageShell variant="workspace" title={t('monitor.alertRulesTab.catHost')}
|
||||
subtitle={selectedHost ? `当前主机: ${selectedHost}` : t('monitor.lbl.selectHostHint')}
|
||||
icon={<Server className="h-5 w-5" />} toolbar={toolbar}
|
||||
loading={loading && !history.length} loadingLabel={t('monitor.hostmonitortsx.sc23e802e')}
|
||||
loading={loading && !history.length} loadingLabel={t('monitor.hostMonitor.loadingData')}
|
||||
contentClassName="space-y-3">
|
||||
{monitorBody}
|
||||
{dialogs}
|
||||
|
||||
@@ -162,7 +162,7 @@ export function MetricsExplorer() {
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<BarChart3 className="h-4 w-4 dg-text-muted" />
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.metricsexplorer.s3ec50388')} </h3>
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.metricsExplorer.metricList')} </h3>
|
||||
<span className="text-xs dg-text-muted ml-auto">{selectedMetrics.length} 已选</span>
|
||||
</div>
|
||||
<div className="space-y-4 max-h-[60vh] overflow-y-auto">
|
||||
|
||||
@@ -70,7 +70,7 @@ export function MiddlewareMonitor() {
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
}
|
||||
}, [syncSelection]);
|
||||
}, [syncSelection, t]);
|
||||
|
||||
useEffect(() => { loadTargets(); }, [loadTargets]);
|
||||
|
||||
@@ -194,10 +194,10 @@ export function MiddlewareMonitor() {
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-12 text-center">
|
||||
<Database className="h-12 w-12 mx-auto mb-4 text-[var(--dg-text-muted)]" />
|
||||
<p className="text-sm text-[var(--dg-text-muted)]"> {t('monitor.middlewaremonit.sbbe62a10')} </p>
|
||||
<p className="text-sm text-[var(--dg-text-muted)]"> {t('monitor.middlewareMonit.emptyTargets')} </p>
|
||||
<div className="flex justify-center gap-2 mt-4">
|
||||
<Button size="sm" onClick={handleSeedDefaults} disabled={seeding}> {t('monitor.middlewaremonit.se5caf12b')} </Button>
|
||||
<Button size="sm" variant="outline" onClick={() => openCreate()}> {t('monitor.middlewaremonit.s63edd678')} </Button>
|
||||
<Button size="sm" onClick={handleSeedDefaults} disabled={seeding}> {t('monitor.middlewareMonit.addPlatformDefault')} </Button>
|
||||
<Button size="sm" variant="outline" onClick={() => openCreate()}> {t('monitor.middlewareMonit.manualAdd')} </Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -252,7 +252,7 @@ export function MiddlewareMonitor() {
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-8 text-center text-sm text-[var(--dg-text-muted)]">
|
||||
暂无 {tab === 'redis' ? 'Redis' : 'MySQL'} 实例,
|
||||
<button type="button" className="dg-text-cyan ml-1 underline" onClick={() => openCreate(tab)}> {t('monitor.middlewaremonit.s8ec8a6e8')} </button>
|
||||
<button type="button" className="dg-text-cyan ml-1 underline" onClick={() => openCreate(tab)}> {t('monitor.middlewareMonit.addNow')} </button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
|
||||
@@ -198,12 +198,12 @@ export function SNMPMonitor() {
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-md bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle> {t('monitor.snmpmonitortsx.s29b90990')} </DialogTitle>
|
||||
<DialogDescription> {t('monitor.snmpmonitortsx.s11ea7d0c')} </DialogDescription>
|
||||
<DialogTitle> {t('monitor.snmpMonitor.addDeviceTitle')} </DialogTitle>
|
||||
<DialogDescription> {t('monitor.snmpMonitor.addDeviceDesc')} </DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-3 py-2">
|
||||
<div className="grid gap-1.5">
|
||||
<Label> {t('monitor.snmpmonitortsx.s9f694f60')} </Label>
|
||||
<Label> {t('monitor.snmpMonitor.deviceName')} </Label>
|
||||
<Input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder={t('monitor.lbl.demoSwitch')} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
|
||||
@@ -45,7 +45,7 @@ const FILTER_TYPES: Array<ProbeType | 'ALL'> = ['ALL', ...PROBE_TYPE_OPTIONS];
|
||||
|
||||
function alertStatusBadge(status?: string) {
|
||||
if (status === 'alerting') {
|
||||
return <Badge className="bg-red-500/15 dg-text-red border-red-500/30 text-[10px]"> {i18n.t('monitor.sitemonitortsx.sb2da5804')} </Badge>;
|
||||
return <Badge className="bg-red-500/15 dg-text-red border-red-500/30 text-[10px]"> {i18n.t('monitor.siteMonitor.alerting')} </Badge>;
|
||||
}
|
||||
return <Badge className="bg-emerald-500/15 dg-text-green border-emerald-500/30 text-[10px]"> {i18n.t('agent.heartbeatNormal')} </Badge>;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ export function SiteMonitor() {
|
||||
if (!detailTask) {
|
||||
return (
|
||||
<div className="space-y-4 text-center py-16">
|
||||
<p className="text-sm text-[var(--dg-text-muted)]"> {t('monitor.sitemonitortsx.s62a0023a')} </p>
|
||||
<p className="text-sm text-[var(--dg-text-muted)]"> {t('monitor.siteMonitor.taskNotFound')} </p>
|
||||
<Button variant="outline" size="sm" onClick={closeDetail}> {t('monitor.siteTask.backToList')} </Button>
|
||||
</div>
|
||||
);
|
||||
@@ -247,8 +247,8 @@ export function SiteMonitor() {
|
||||
<div className="flex items-center justify-between mb-3 flex-wrap gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Activity className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||||
<h3 className="text-sm font-medium"> {t('monitor.sitemonitortsx.s30319e1c')} </h3>
|
||||
<span className="text-[10px] text-[var(--dg-text-muted)]"> {t('monitor.sitemonitortsx.sa5e6b654')} </span>
|
||||
<h3 className="text-sm font-medium"> {t('monitor.siteMonitor.probeTaskTitle')} </h3>
|
||||
<span className="text-[10px] text-[var(--dg-text-muted)]"> {t('monitor.siteMonitor.clickToObserve')} </span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{FILTER_TYPES.map((type) => {
|
||||
@@ -346,7 +346,7 @@ export function SiteMonitor() {
|
||||
<DialogContent className="sm:max-w-md bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingTaskId ? t('monitor.editProbe') : t('monitor.addProbe')}</DialogTitle>
|
||||
<DialogDescription className="text-[var(--dg-text-muted)]"> {t('monitor.sitemonitortsx.s6173cf9a')} </DialogDescription>
|
||||
<DialogDescription className="text-[var(--dg-text-muted)]"> {t('monitor.siteMonitor.probeDesc')} </DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-2">
|
||||
<div className="grid gap-1.5">
|
||||
|
||||
@@ -32,12 +32,12 @@ export function PortPolicyDialog({
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent showOverlay={false} className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-[var(--dg-text-primary)]"> {t('monitor.portpolicydialo.s868e3dd0')} </DialogTitle>
|
||||
<DialogDescription className="text-[var(--dg-text-secondary)]"> {t('monitor.portpolicydialo.scd27ad47')} </DialogDescription>
|
||||
<DialogTitle className="text-[var(--dg-text-primary)]"> {t('monitor.portPolicyDialog.title')} </DialogTitle>
|
||||
<DialogDescription className="text-[var(--dg-text-secondary)]"> {t('monitor.portPolicyDialog.desc')} </DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-[11px] text-[var(--dg-text-muted)] mb-1.5 block"> {t('monitor.portpolicydialo.s7824bdd1')} </label>
|
||||
<label className="text-[11px] text-[var(--dg-text-muted)] mb-1.5 block"> {t('monitor.portPolicyDialog.filterMode')} </label>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className={`flex-1 h-9 rounded text-xs font-medium border transition-colors ${portPolicyMode === 'whitelist' ? 'bg-blue-500/20 border-blue-500 dg-text-blue' : 'bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] text-[var(--dg-text-muted)]'}`}
|
||||
@@ -84,7 +84,7 @@ export function PortPolicyDialog({
|
||||
|
||||
<div className="bg-amber-500/5 border border-amber-500/20 rounded p-2.5">
|
||||
<p className="text-[10px] text-amber-300/80 leading-relaxed">
|
||||
⚠️ 无论选择哪种模式,以下系统进程端口<b> {t('monitor.portpolicydialo.s2ff12d51')} </b>:sshd、rpcbind、master、systemd、dbus-daemon、rsyslogd 等。Agent 采集时自动跳过。
|
||||
⚠️ 无论选择哪种模式,以下系统进程端口<b> {t('monitor.portPolicyDialog.neverReport')} </b>:sshd、rpcbind、master、systemd、dbus-daemon、rsyslogd 等。Agent 采集时自动跳过。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,31 +23,31 @@ function shortName(name: string) {
|
||||
export function MwOverviewCharts({ targets }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const statusData = useMemo(() => {
|
||||
const online = targets.filter((t) => t.status === 'online').length;
|
||||
const online = targets.filter((tg) => tg.status === 'online').length;
|
||||
const offline = targets.length - online;
|
||||
return [
|
||||
{ name: t('agent.agentOnline'), value: online, fill: 'var(--dg-green)' },
|
||||
{ name: t('agent.agentOffline'), value: offline, fill: 'var(--dg-red)' },
|
||||
].filter((d) => d.value > 0);
|
||||
}, [targets]);
|
||||
}, [targets, t]);
|
||||
|
||||
const redisHitData = useMemo(() => targets
|
||||
.filter((t) => t.description === 'redis' && t.latest_metrics?.redis_hit_rate > 0)
|
||||
.map((t) => ({
|
||||
name: shortName(t.name), fullName: t.name,
|
||||
value: (t.latest_metrics.redis_hit_rate || 0) * 100,
|
||||
fill: (t.latest_metrics.redis_hit_rate || 0) >= 0.9 ? 'var(--dg-green)' : 'var(--dg-yellow)',
|
||||
.filter((tg) => tg.description === 'redis' && tg.latest_metrics?.redis_hit_rate > 0)
|
||||
.map((tg) => ({
|
||||
name: shortName(tg.name), fullName: tg.name,
|
||||
value: (tg.latest_metrics.redis_hit_rate || 0) * 100,
|
||||
fill: (tg.latest_metrics.redis_hit_rate || 0) >= 0.9 ? 'var(--dg-green)' : 'var(--dg-yellow)',
|
||||
}))
|
||||
.sort((a, b) => a.value - b.value), [targets]);
|
||||
|
||||
const redisQpsData = useMemo(() => targets
|
||||
.filter((t) => t.description === 'redis' && t.latest_metrics?.redis_qps > 0)
|
||||
.map((t) => ({ name: shortName(t.name), fullName: t.name, value: t.latest_metrics.redis_qps, fill: 'var(--dg-blue)' }))
|
||||
.filter((tg) => tg.description === 'redis' && tg.latest_metrics?.redis_qps > 0)
|
||||
.map((tg) => ({ name: shortName(tg.name), fullName: tg.name, value: tg.latest_metrics.redis_qps, fill: 'var(--dg-blue)' }))
|
||||
.sort((a, b) => b.value - a.value), [targets]);
|
||||
|
||||
const mysqlConnData = useMemo(() => targets
|
||||
.filter((t) => t.description === 'mysql' && t.latest_metrics?.mysql_connections > 0)
|
||||
.map((t) => ({ name: shortName(t.name), fullName: t.name, value: t.latest_metrics.mysql_connections, fill: 'var(--dg-blue)' }))
|
||||
.filter((tg) => tg.description === 'mysql' && tg.latest_metrics?.mysql_connections > 0)
|
||||
.map((tg) => ({ name: shortName(tg.name), fullName: tg.name, value: tg.latest_metrics.mysql_connections, fill: 'var(--dg-blue)' }))
|
||||
.sort((a, b) => b.value - a.value), [targets]);
|
||||
|
||||
if (targets.length === 0) return null;
|
||||
@@ -98,7 +98,7 @@ export function MwOverviewCharts({ targets }: Props) {
|
||||
)}
|
||||
</OverviewChartCard>
|
||||
|
||||
<OverviewChartCard title={t('monitor.mwoverviewchart.s07d3e014')} icon={<BarChart3 className="h-3.5 w-3.5 text-[var(--dg-text-muted)]" />} empty={mysqlConnData.length === 0}>
|
||||
<OverviewChartCard title={t('monitor.lbl.mysqlConnections')} icon={<BarChart3 className="h-3.5 w-3.5 text-[var(--dg-text-muted)]" />} empty={mysqlConnData.length === 0}>
|
||||
{(h) => (
|
||||
<ResponsiveContainer width="100%" height={h}>
|
||||
<BarChart data={mysqlConnData} layout="vertical" margin={{ left: 0, right: 8 }}>
|
||||
|
||||
@@ -98,10 +98,10 @@ export function MwTargetChartsPanel({ target, chartDefs, accent }: Props) {
|
||||
<Select value={timeRange} onValueChange={(v) => setTimeRange(v as MwTimeRange)}>
|
||||
<SelectTrigger className="h-7 w-[92px] text-xs"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1h"> {t('monitor.helpersts.sf972b7b1')} </SelectItem>
|
||||
<SelectItem value="1h"> {t('monitor.helpers.last1h')} </SelectItem>
|
||||
<SelectItem value="6h"> {t('monitor.siteProtocol.rangeDefault')} </SelectItem>
|
||||
<SelectItem value="24h"> {t('monitor.helpersts.s6e99b786')} </SelectItem>
|
||||
<SelectItem value="7d"> {t('monitor.helpersts.sd765efc4')} </SelectItem>
|
||||
<SelectItem value="24h"> {t('monitor.helpers.last24h')} </SelectItem>
|
||||
<SelectItem value="7d"> {t('monitor.helpers.last7d')} </SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={loadCharts} disabled={loading}>
|
||||
|
||||
@@ -14,7 +14,7 @@ export function ChartRangeHint({ label, zoomed, onReset }: Props) {
|
||||
if (!zoomed) return null;
|
||||
return (
|
||||
<div className="flex items-center justify-between text-[10px] text-[var(--dg-text-muted)] mt-1 px-0.5">
|
||||
<span> {t('monitor.chartrangehintt.s39ce9bad')} <span className="text-[var(--dg-text-primary)] font-mono">{label}</span></span>
|
||||
<span> {t('monitor.chartRangeHint.selectedRange')} <span className="text-[var(--dg-text-primary)] font-mono">{label}</span></span>
|
||||
<Button type="button" variant="ghost" size="sm" className="h-6 text-[10px] px-2" onClick={onReset}>
|
||||
<ZoomOut className="h-3 w-3 mr-1" />恢复全量
|
||||
</Button>
|
||||
|
||||
@@ -89,7 +89,7 @@ export const probeTypeMeta: Record<ProbeType, { label: string; placeholder: stri
|
||||
HTTP: { label: i18n.t('monitor.lbl.siteTargetUrl'), placeholder: 'https://example.com/health', hint: i18n.t('monitor.lbl.siteHttpHint') },
|
||||
SSL: { label: i18n.t('monitor.lbl.siteSslAddr'), placeholder: i18n.t('monitor.lbl.siteSslPlaceholder'), hint: i18n.t('monitor.lbl.siteSslHint') },
|
||||
TCP: { label: i18n.t('monitor.lbl.siteHostPort'), placeholder: i18n.t('monitor.lbl.siteTcpPlaceholder'), hint: i18n.t('monitor.lbl.siteTcpHint') },
|
||||
DNS: { label: i18n.t('cloud.domainName'), placeholder: 'example.com', hint: i18n.t('monitor.helpersts.sd3fd8f1d') },
|
||||
DNS: { label: i18n.t('cloud.domainName'), placeholder: 'example.com', hint: i18n.t('monitor.helpers.dnsHint') },
|
||||
PING: { label: i18n.t('monitor.lbl.siteHostAddr'), placeholder: i18n.t('monitor.lbl.sitePingPlaceholder'), hint: i18n.t('monitor.lbl.sitePingHint') },
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export function SnmpOverviewCharts({ devices, overview }: Props) {
|
||||
{ name: t('agent.heartbeatAbnormal'), value: error, fill: 'var(--dg-red)' },
|
||||
{ name: t('agent.agentOffline'), value: offline, fill: 'var(--dg-text-muted)' },
|
||||
].filter((d) => d.value > 0);
|
||||
}, [devices]);
|
||||
}, [devices, t]);
|
||||
|
||||
const cpuData = useMemo(() => {
|
||||
return overview
|
||||
@@ -49,10 +49,10 @@ export function SnmpOverviewCharts({ devices, overview }: Props) {
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<BarChart3 className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.snmpoverviewcha.s64f5a4da')} </h3>
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.lbl.deviceCpuCompare')} </h3>
|
||||
</div>
|
||||
{cpuData.length === 0 ? (
|
||||
<p className="text-xs text-[var(--dg-text-muted)] py-8 text-center"> {t('monitor.snmpoverviewcha.s1f441890')} </p>
|
||||
<p className="text-xs text-[var(--dg-text-muted)] py-8 text-center"> {t('monitor.lbl.noCpuMetrics')} </p>
|
||||
) : (
|
||||
<ResponsiveContainer width="100%" height={Math.max(160, cpuData.length * 36)}>
|
||||
<BarChart data={cpuData} layout="vertical" margin={{ top: 0, right: 16, left: 0, bottom: 0 }}>
|
||||
@@ -100,7 +100,7 @@ export function SnmpOverviewCharts({ devices, overview }: Props) {
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<PieChartIcon className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.snmpoverviewcha.sbb0ae97f')} </h3>
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]"> {t('monitor.lbl.deviceStatus')} </h3>
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={180}>
|
||||
<PieChart>
|
||||
|
||||
Reference in new issue
Block a user