From 0f0f2770e15d93b51e7720e57a9de328addb046c Mon Sep 17 00:00:00 2001 From: attacker Date: Thu, 2 Jul 2026 21:28:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(i18n):=20=E7=9B=91=E6=8E=A7=E5=A4=A7?= =?UTF-8?q?=E5=B1=8F=20GrafanaDashboard=20=E5=85=A8=E9=87=8F=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96=20=E2=80=94=20108=20=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E9=94=AE=20(monitor.dashboard.*)=20+=204=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=AD=90Tab=E5=85=A8=E9=87=8F=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GrafanaDashboard.tsx (640L) 全量迁移: AgentTab/ZabbixTab/PrometheusTab/DashboardTab 四个组件 - 新增 monitor.dashboard.* 命名空间 (108 键): tab/agent/batch/confirm/creds/warn/status/col/pagination/zabbix/prometheus/custom - 硬编码中文 UI 文案 → t() 翻译键: Tab标签/工具栏/批量部署对话框/确认框/toast消息/表格列头/状态Badge/凭据表单/警告文案/分页/空状态 - TABS 常量改 getTabs(t) 函数模式(支持语言切换实时更新) - zh-CN/en-US 完全对齐: 4201 键 - tsc -b=0, npm build ✅ (19.77s), ESLint any=37 unused=0, 17/17 集成测试通过 --- ROADMAP.md | 7 + .../monitor/dashboard/GrafanaDashboard.tsx | 204 +++++++++--------- frontend/src/locales/en-US.json | 140 +++++++++++- frontend/src/locales/zh-CN.json | 140 +++++++++++- 4 files changed, 388 insertions(+), 103 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 7903eba..ee8fcff 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -267,6 +267,13 @@ services/api.ts (1324L) **问题**: 平台无国际化支持,所有 UI 文案硬编码为中文,无法切换语言 +**增量迁移进展** — 持续推进页面国际化: + +| 页面 | 新增键 | 组件 | +|------|--------|------| +| Dashboard/ServerManager/AlertCenter/Approval/安全审计/Deploy/Kubernetes/Cloud/Task/Monitor | 3800+ | 已迁移 | +| **监控大屏 (GrafanaDashboard)** | 108 (monitor.dashboard.*) | GrafanaDashboard.tsx (AgentTab/ZabbixTab/PrometheusTab/DashboardTab) | + **已完成 (2026-06-27)**: - ✅ **i18n 基础设施搭建** — 安装 `i18next` + `react-i18next`,配置翻译实例 (`src/i18n/index.ts`),支持 `zh-CN` / `en-US` 双语 - ✅ **翻译文件** — `src/locales/zh-CN.json`(中文基础)+ `src/locales/en-US.json`(英文翻译),覆盖 5 大命名空间 (common/menu/header/login/dashboard),120+ 翻译键 diff --git a/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx b/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx index 0ff31ed..9581fe0 100644 --- a/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx +++ b/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // 监控大屏 — 标签页切换 Agent / Zabbix / Prometheus + 自定义仪表盘 // Logan.Li import React, { useState, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import { Monitor, @@ -52,11 +53,11 @@ import { PageEmpty } from '@/components/layout/PageEmpty'; // ═══════════ Tab 配置 ═══════════ type TabId = 'agent' | 'zabbix' | 'prometheus' | 'dashboard'; -const TABS: { id: TabId; label: string; icon: React.ReactNode }[] = [ - { id: 'agent', label: 'Agent 主机', icon: }, +const getTabs = (t: (key: string) => string): { id: TabId; label: string; icon: React.ReactNode }[] => [ + { id: 'agent', label: t('monitor.dashboard.tab.agent'), icon: }, { id: 'zabbix', label: 'Zabbix', icon: }, { id: 'prometheus', label: 'Prometheus', icon: }, - { id: 'dashboard', label: '自定义', icon: }, + { id: 'dashboard', label: t('monitor.dashboard.tab.dashboard'), icon: }, ]; // ═══════════ Agent 主机监控表格 ═══════════ @@ -64,6 +65,7 @@ interface AgentHost { server_id: number; hostname: string; ip: string; cpu_usage type SortField = 'hostname' | 'agent_version' | 'agent_status' | 'cpu_usage' | 'mem_usage' | 'disk_usage' | 'region' | 'ip'; const AgentTab: React.FC = () => { + const { t } = useTranslation(); const _confirm = useConfirm(); const navigate = useNavigate(); const [hosts, setHosts] = useState([]); @@ -88,16 +90,16 @@ const AgentTab: React.FC = () => { // 批量卸载 const [batchUninstalling, setBatchUninstalling] = useState(false); const handleBatchUninstall = async () => { - const ok = await _confirm({ title: '批量卸载 Agent', description: `确定卸载选中的 ${selIds.length} 台主机的 AIOps Agent?卸载将在后台异步执行。`, confirmText: '卸载', variant: 'destructive' }); + const ok = await _confirm({ title: t('monitor.dashboard.confirm.uninstallTitle'), description: t('monitor.dashboard.confirm.uninstallDesc', { count: selIds.length }), confirmText: t('monitor.dashboard.confirm.uninstall'), variant: 'destructive' }); if (!ok) return; setBatchUninstalling(true); setOperating(new Set(selIds)); try { await agentApi.batchUninstall(selIds); - toast.success(`已提交:批量卸载 ${selIds.length} 台主机的 Agent,后台执行中,30秒后自动刷新`); + toast.success(t('monitor.dashboard.agent.toast.batchUninstall', { count: selIds.length })); setSelected(new Set()); setTimeout(() => { fetch(); setOperating(new Set()); }, 30000); - } catch { toast.error('提交失败'); setOperating(new Set()); } + } catch { toast.error(t('monitor.dashboard.agent.toast.submitFailed')); setOperating(new Set()); } finally { setBatchUninstalling(false); } }; const totalPages = Math.max(1, Math.ceil(hosts.length / pageSize)); @@ -117,7 +119,7 @@ const AgentTab: React.FC = () => { return next; }); } catch (e: any) { - toast.error('刷新 Agent 主机列表失败:' + (e?.message || '未知错误')); + toast.error(t('monitor.dashboard.agent.toast.refreshFailed', { msg: (e?.message || t('monitor.dashboard.agent.toast.unknownError')) })); } finally { setLoading(false); setRefreshing(false); @@ -160,17 +162,17 @@ const AgentTab: React.FC = () => { // 执行批量部署 const handleBatchSubmit = async () => { - if (!batchAck) { toast.error('请先确认并勾选「我已知悉」'); return; } + if (!batchAck) { toast.error(t('monitor.dashboard.agent.toast.ackRequired')); return; } setBatchSubmitting(true); const token = useUserStore.getState().token; const ids = selectedHosts.map(h => h.server_id); setOperating(new Set(ids)); try { if (batchMode === 'install') { - if (!token) { toast.error('未登录'); return; } + if (!token) { toast.error(t('monitor.dashboard.agent.toast.notLogin')); return; } // 安装:只安装无 Agent 的 const installIds = needInstall.map(h => h.server_id); - if (installIds.length === 0) { toast.warning('所选主机均已安装 Agent,无需安装'); return; } + if (installIds.length === 0) { toast.warning(t('monitor.dashboard.agent.toast.allInstalled')); return; } // 为未保存凭据的主机构造临时凭据 map;后端遇到空字段不会覆盖已有凭据 // Logan.Li const credentials: Record = {}; const needCreds = batchCreds.username && (batchCreds.authType === 'key' ? batchCreds.sshKey : batchCreds.password); @@ -185,63 +187,63 @@ const AgentTab: React.FC = () => { } } const res = await serverApi.batchInstallAgent(installIds, token, Object.keys(credentials).length ? credentials : undefined); - toast.success(`已提交:批量安装 ${res?.total ?? installIds.length} 台主机的 Agent,后台执行中,30秒后自动刷新`); + toast.success(t('monitor.dashboard.agent.toast.batchInstall', { count: res?.total ?? installIds.length })); setSelected(new Set()); setTimeout(() => { fetch(); setOperating(new Set()); }, 30000); } else { // 升级:只升级在线的 const upgradeIds = needUpgrade.map(h => h.server_id); - if (upgradeIds.length === 0) { toast.warning('所选主机中没有在线 Agent,无法升级'); return; } + if (upgradeIds.length === 0) { toast.warning(t('monitor.dashboard.agent.toast.noOnline')); return; } const res = await agentApi.batchUpgrade(upgradeIds); - toast.success(res?.message || `已提交:批量升级 ${upgradeIds.length} 台主机的 Agent,后台执行中,30秒后自动刷新`); + toast.success(res?.message || t('monitor.dashboard.agent.toast.batchUpgrade', { count: upgradeIds.length })); setSelected(new Set()); setTimeout(() => { fetch(); setOperating(new Set()); }, 30000); } setBatchOpen(false); - } catch { toast.error('提交失败'); setOperating(new Set()); } + } catch { toast.error(t('monitor.dashboard.agent.toast.submitFailed')); setOperating(new Set()); } finally { setBatchSubmitting(false); } }; - if (loading) return ; + if (loading) return ; if (!hosts.length) return ( } - title="暂无服务器资产" - description="请先在「服务器管理」中添加主机,然后在此安装 Agent" + title={t('monitor.dashboard.agent.emptyTitle')} + description={t('monitor.dashboard.agent.emptyDesc')} /> ); return (
{/* 工具栏 */}
- {selAny && 已选 {selected.size} 项} + {selAny && {t('monitor.dashboard.agent.selected', { count: selected.size })}}
{selAny && ( <> )}
{ setOperating(prev => new Set(prev).add(id)); }} - onInstalled={() => { toast.success('Agent 安装任务已提交,后台执行中…'); setWizardOpen(false); fetch(); }} - onFailed={(id) => { toast.error('安装失败'); setOperating(prev => { const next = new Set(prev); next.delete(id); return next; }); }} /> + onInstalled={() => { toast.success(t('monitor.dashboard.agent.toast.installed')); setWizardOpen(false); fetch(); }} + onFailed={(id) => { toast.error(t('monitor.dashboard.agent.toast.installFailed')); setOperating(prev => { const next = new Set(prev); next.delete(id); return next; }); }} /> {/* ────────── 批量部署 Agent 对话框 ────────── */} { if (!v) { setBatchOpen(false); setBatchAck(false); setBatchCreds({ username: 'root', password: '', sshKey: '', authType: 'password' }); } }}> @@ -249,10 +251,10 @@ const AgentTab: React.FC = () => { - 批量部署 Agent + {t('monitor.dashboard.batch.title')} - 选中 {selectedHosts.length} 台主机(需安装: {needInstall.length},待升级: {needUpgrade.length}) + {t('monitor.dashboard.batch.summary', { selected: selectedHosts.length, install: needInstall.length, upgrade: needUpgrade.length })} @@ -268,7 +270,7 @@ const AgentTab: React.FC = () => { batchMode === m ? 'dg-btn-primary text-white shadow-sm' : 'dg-text-secondary hover:text-[var(--dg-text-primary)]' }`} > - {m === 'install' ? `⚡ 安装 (${needInstall.length})` : `🔄 升级到最新版 (${needUpgrade.length})`} + {m === 'install' ? t('monitor.dashboard.batch.install', { count: needInstall.length }) : t('monitor.dashboard.batch.upgrade', { count: needUpgrade.length })} ))}
@@ -278,11 +280,11 @@ const AgentTab: React.FC = () => { - + - - - + + + @@ -292,15 +294,15 @@ const AgentTab: React.FC = () => { @@ -314,34 +316,34 @@ const AgentTab: React.FC = () => {
- 以下主机未保存 SSH 凭据,请提供临时凭据用于批量安装(仅本次使用): + {t('monitor.dashboard.creds.hint')}
- +
- + setBatchCreds(c => ({ ...c, username: e.target.value }))} placeholder="root" className="h-8 text-xs dg-input text-[var(--dg-text-primary)]" />
{batchCreds.authType === 'password' ? (
- - setBatchCreds(c => ({ ...c, password: e.target.value }))} placeholder="输入 SSH 密码" className="h-8 text-xs dg-input text-[var(--dg-text-primary)]" /> + + setBatchCreds(c => ({ ...c, password: e.target.value }))} placeholder={t('monitor.dashboard.creds.passwordPlaceholder')} className="h-8 text-xs dg-input text-[var(--dg-text-primary)]" />
) : (
- +
主机名{t('monitor.dashboard.col.hostname')} IP状态当前版本操作{t('monitor.dashboard.col.status')}{t('monitor.dashboard.col.version')}{t('monitor.dashboard.col.action')}
{h.ip} {h.agent_status === 'online' - ? 在线 + ? {t('monitor.dashboard.status.online')} : - {h.agent_status === 'not_installed' ? '未安装' : h.agent_status || '离线'} + {h.agent_status === 'not_installed' ? t('monitor.dashboard.status.notInstalled') : h.agent_status || t('monitor.dashboard.status.offline')} } {h.agent_version || '—'} - {batchMode === 'install' ? '即将安装' : '即将升级'} + {batchMode === 'install' ? t('monitor.dashboard.batch.willInstall') : t('monitor.dashboard.batch.willUpgrade')}