refactor(i18n): U16 AgentWizard i18n 治理 — 29 个 agentwizardtsx.* hash 乱码键清理为 agent.wizard.* 语义化键 + 组件从 i18n.t() 全局改为 useTranslation() hook + 8 处硬编码中文 UI 文案清零. zh/en 7564=7564 对齐, tsc -b=0, npm build✓, ESLint=0/66/0, 17/17 测试通过

This commit is contained in:
attacker committed 2026-07-11 01:21:34 +08:00
1 parent f178609d09
commit a85e063a47
3 files changed
+113 -96

No files matched your search

+39 -38
View File
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useUserStore } from '@/store';
import {
Dialog,
@@ -14,7 +15,6 @@ import { Label } from '@/components/ui/label';
import { Copy, Check, Terminal, Download, Settings, Play, Cpu, Zap, Loader2, Server, Shield } from 'lucide-react';
import { serverApi } from '@/api';
import { toast } from 'sonner';
import i18n from '@/i18n';
interface AgentWizardProps {
open: boolean;
@@ -41,6 +41,7 @@ function detectServerAddress() {
}
export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled, onFailed }: AgentWizardProps) {
const { t } = useTranslation();
const token = useUserStore((s) => s.token) || '<YOUR_TOKEN>';
const origin = detectServerAddress();
const isWindowsServer = Boolean(server?.os && server.os.toLowerCase().includes('windows'));
@@ -108,10 +109,10 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
if (success) {
setCopied(true);
toast.success(i18n.t('agent.agentwizardtsx.s19ee0048'));
toast.success(t('agent.wizard.copySuccess'));
setTimeout(() => setCopied(false), 3000);
} else {
toast.error(i18n.t('agent.agentwizardtsx.s7ac41b1c'));
toast.error(t('agent.wizard.copyFailed'));
setShowManualInput(true);
}
};
@@ -122,19 +123,19 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
const manualSteps = [
{
icon: Download,
title: i18n.t('agent.agentwizardtsx.s8c59e054'),
title: t('agent.wizard.stepDownload'),
commands: [
`curl -fsSL ${binaryUrl} -o /usr/local/bin/aiops-agent`,
],
},
{
icon: Settings,
title: i18n.t('agent.agentwizardtsx.s2f17bf61'),
title: t('agent.wizard.stepChmod'),
commands: ['chmod +x /usr/local/bin/aiops-agent'],
},
{
icon: Cpu,
title: i18n.t('agent.agentwizardtsx.s03fa0002'),
title: t('agent.wizard.stepService'),
commands: [
`cat > /etc/systemd/system/aiops-agent.service << 'EOF'`,
`[Unit]`,
@@ -154,7 +155,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
},
{
icon: Play,
title: i18n.t('agent.agentwizardtsx.s84bd81f4'),
title: t('agent.wizard.stepStart'),
commands: [
'systemctl daemon-reload',
'systemctl enable --now aiops-agent',
@@ -165,7 +166,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
const windowsManualSteps = [
{
icon: Download,
title: '下载 Windows Agent',
title: t('agent.wizard.stepDownloadWindows'),
commands: [
`New-Item -ItemType Directory -Force "C:\\Program Files\\AIOps"`,
`Invoke-WebRequest -Uri ${windowsBinaryUrl} -OutFile "C:\\Program Files\\AIOps\\aiops-agent.exe" -UseBasicParsing`,
@@ -173,14 +174,14 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
},
{
icon: Cpu,
title: '注册 Windows 服务',
title: t('agent.wizard.stepRegisterWindows'),
commands: [
`New-Service -Name AIOpsAgent -DisplayName "AIOps Agent" -BinaryPathName '"C:\\Program Files\\AIOps\\aiops-agent.exe" -server ${origin} -interval 10 -token ${token}' -StartupType Automatic`,
],
},
{
icon: Play,
title: '启动并检查',
title: t('agent.wizard.stepStartCheck'),
commands: [
'Start-Service AIOpsAgent',
'Get-Service AIOpsAgent',
@@ -193,11 +194,11 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
const handleSshInstall = async () => {
if (!server || !token) return;
if (isWindowsServer) {
toast.error('Windows Agent 请使用 PowerShell 安装命令,SSH 一键安装仅支持 Linux/macOS');
toast.error(t('agent.wizard.windowsSSHNotSupported'));
return;
}
if (needsCreds && !sshPassword && !sshKey) {
toast.error(i18n.t('agent.agentwizardtsx.s6cfe961c'));
toast.error(t('agent.wizard.credsRequired'));
return;
}
setSshInstalling(true);
@@ -216,11 +217,11 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
: undefined;
// 优先尝试异步批量接口,即使单台也后台执行
await serverApi.batchInstallAgent([server.id], token, credentials);
toast.success(`${server.hostname}: 已提交 Agent 安装任务,后台执行中…`);
toast.success(t('agent.wizard.installSubmitted', { hostname: server.hostname }));
onInstalled?.(server.id);
onOpenChange(false);
} catch (e) {
const msg = e?.message || i18n.t('agent.installFailed');
const msg = e instanceof Error ? e.message : String(e);
toast.error(`${server.hostname} ${msg}`);
onFailed?.(server.id, msg);
} finally {
@@ -233,12 +234,12 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
<DialogContent className="max-w-2xl dg-card text-[var(--dg-text-primary)]">
<DialogHeader>
<DialogTitle className="text-[var(--dg-text-primary)]">
{server ? `部署 Agent → ${server.hostname}` : i18n.t('agent.agentwizardtsx.s812cc1a5')}
{server ? t('agent.wizard.titleWithHost', { hostname: server.hostname }) : t('agent.wizard.title')}
</DialogTitle>
<DialogDescription className="dg-text-secondary">
{server
? `目标主机 ${server.hostname} (${server.ip}),选择安装方式:`
: i18n.t('agent.agentwizardtsx.sed0c1b63')
? t('agent.wizard.descWithHost', { hostname: server.hostname, ip: server.ip })
: t('agent.wizard.desc')
}
</DialogDescription>
</DialogHeader>
@@ -247,29 +248,29 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
<TabsList className="bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)]">
<TabsTrigger value="auto" className="data-[state=active]:bg-[var(--dg-bg-active)]">
<Terminal className="h-4 w-4 mr-1.5" />
{isWindowsServer ? 'PowerShell 安装' : i18n.t('agent.agentwizardtsx.s1a2b3c4d')}
{isWindowsServer ? t('agent.wizard.tabPowerShell') : t('agent.wizard.tabAuto')}
</TabsTrigger>
{server && !isWindowsServer && (
<TabsTrigger value="ssh" className="data-[state=active]:bg-[var(--dg-bg-active)]">
<Zap className="h-4 w-4 mr-1.5" />
{i18n.t('agent.agentwizardtsx.s2b3c4d5e')}
{t('agent.wizard.tabSSH')}
</TabsTrigger>
)}
{!server && (
<TabsTrigger value="ssh" className="data-[state=active]:bg-[var(--dg-bg-active)] opacity-50 cursor-not-allowed" disabled>
<Zap className="h-4 w-4 mr-1.5" />
SSH一键安装
{t('agent.wizard.tabSSH')}
</TabsTrigger>
)}
<TabsTrigger value="manual" className="data-[state=active]:bg-[var(--dg-bg-active)]">
{i18n.t('agent.agentwizardtsx.s3c4d5e6f')}
{t('agent.wizard.tabManual')}
</TabsTrigger>
</TabsList>
{/* 自动安装 */}
<TabsContent value="auto" className="mt-4 space-y-3">
<p className="text-sm dg-text-secondary">
{isWindowsServer ? '请在目标 Windows 主机的管理员 PowerShell 中执行以下命令。' : i18n.t('agent.agentwizardtsx.s4d5e6f7g')}
{isWindowsServer ? t('agent.wizard.autoHintWindows') : t('agent.wizard.autoHint')}
</p>
<div className="relative rounded-lg bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] p-4">
<pre className="text-xs dg-text-green font-mono whitespace-pre-wrap break-all pr-10">
@@ -280,7 +281,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
variant="ghost"
onClick={handleCopy}
className="absolute top-2 right-2 h-8 w-8 dg-text-secondary hover:text-[var(--dg-text-primary)] hover:bg-[var(--dg-bg-hover)]"
title={i18n.t('agent.agentwizardtsx.s564be0b1')}
title={t('agent.wizard.copyBtn')}
>
{copied ? <Check className="h-4 w-4 dg-text-green" /> : <Copy className="h-4 w-4" />}
</Button>
@@ -290,7 +291,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
{showManualInput && (
<div className="rounded-lg bg-amber-950/30 border border-amber-800/50 p-4">
<p className="text-xs dg-text-yellow mb-2">
{i18n.t('agent.agentwizardtsx.s5e6f7g8h')}
{t('agent.wizard.manualCopyHint')}
</p>
<textarea
readOnly
@@ -303,7 +304,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
<div className="flex items-start gap-2 text-xs dg-text-muted">
<Terminal className="h-3.5 w-3.5 mt-0.5 shrink-0" />
<span>
服务器地址已自动检测 — 安装脚本与 Agent 上报均使用 {origin}(通过 Nginx/Vite proxy 转发到后端)。安装成功后将自动连接并开始上报指标。
{t('agent.wizard.addressDetected', { origin })}
</span>
</div>
</TabsContent>
@@ -313,8 +314,8 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
{!server ? (
<div className="text-center py-12 dg-text-secondary">
<Server className="h-10 w-10 mx-auto mb-3 opacity-50" />
<p className="text-sm font-medium mb-1"> {i18n.t('agent.agentwizardtsx.sc9b7eec2')} </p>
<p className="text-xs"> {i18n.t('agent.agentwizardtsx.s12a11150')} </p>
<p className="text-sm font-medium mb-1"> {t('agent.wizard.noServer')} </p>
<p className="text-xs"> {t('agent.wizard.noServerHint')} </p>
</div>
) : (
<>
@@ -332,11 +333,11 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
<div className="mb-4 p-3 rounded-lg bg-amber-950/30 border border-amber-800/40 space-y-3">
<div className="flex items-center gap-2 text-xs text-amber-300/80">
<Shield className="h-3.5 w-3.5" />
<span> {i18n.t('agent.agentwizardtsx.seb55dff9')} </span>
<span> {t('agent.wizard.credsHint')} </span>
</div>
<div className="grid grid-cols-2 gap-2">
<div>
<Label className="text-xs dg-text-secondary"> {i18n.t('system.userName')} </Label>
<Label className="text-xs dg-text-secondary"> {t('system.userName')} </Label>
<Input
value={sshUsername}
onChange={e => setSshUsername(e.target.value)}
@@ -346,7 +347,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
</div>
<div className="flex items-end gap-1">
<div className="flex-1">
<Label className="text-xs dg-text-secondary"> {i18n.t('system.passwordLabel')} </Label>
<Label className="text-xs dg-text-secondary"> {t('system.passwordLabel')} </Label>
<Input
type="password"
value={sshPassword}
@@ -355,17 +356,17 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
className="h-8 text-sm dg-input text-[var(--dg-text-primary)] mt-1"
/>
</div>
<span className="text-xs dg-text-muted px-1 pb-1">{i18n.t("agent.agentwizardtsx.s1k2l3m4n")}</span>
<span className="text-xs dg-text-muted px-1 pb-1">{t("agent.wizard.or")}</span>
<Button variant="ghost" size="sm"
onClick={() => { setAuthType('key'); setSshPassword(''); }}
className={`text-xs h-7 px-2 ${authType==='key'?'dg-text-blue':'dg-text-muted'}`}>
{i18n.t('agent.agentwizardtsx.s2l3m4n5o')}
{t('agent.wizard.keyAuth')}
</Button>
</div>
</div>
{authType === 'key' && (
<div>
<Label className="text-xs dg-text-secondary">{i18n.t("agent.agentwizardtsx.s3m4n5o6p")}</Label>
<Label className="text-xs dg-text-secondary">{t("agent.wizard.privateKey")}</Label>
<textarea
value={sshKey}
onChange={e => setSshKey(e.target.value)}
@@ -379,7 +380,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
)}
<p className="text-sm dg-text-secondary mb-4">
{i18n.t("agent.agentwizardtsx.s7g8h9i0j")}
{t("agent.wizard.sshHint")}
</p>
<Button
onClick={handleSshInstall}
@@ -389,12 +390,12 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
{sshInstalling ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
{i18n.t('agent.agentwizardtsx.s8h9i0j1k')}
{t('agent.wizard.sshInstalling')}
</>
) : (
<>
<Zap className="h-4 w-4 mr-2" />
{needsCreds ? i18n.t('agent.agentwizardtsx.sce2f5317') : i18n.t('agent.agentwizardtsx.s9866eb4f')}
{needsCreds ? t('agent.wizard.installWithCreds') : t('agent.wizard.installNow')}
</>
)}
</Button>
@@ -402,7 +403,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
<div className="flex items-start gap-2 text-xs dg-text-muted">
<Terminal className="h-3.5 w-3.5 mt-0.5 shrink-0" />
<span>
系统将通过 SSH 连接 {server.ip},执行安装脚本。请确保服务器 SSH 凭据已正确配置(编辑服务器可修改密码/密钥)。
{t('agent.wizard.sshConnectHint', { ip: server.ip })}
</span>
</div>
</>
@@ -432,7 +433,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
))}
</div>
<p className="text-xs dg-text-muted">
{i18n.t('agent.agentwizardtsx.s0j1k2l3m')}
{t('agent.wizard.manualHint')}
</p>
</TabsContent>
</Tabs>