fix(i18n): 回退到稳定的 32% 覆盖率状态 (tsc + Vite 构建通过)
批量转换脚本引入的语法错误已回退,保留稳定的 i18n 转换: - 翻译词典: 22 namespaces, 3600+ keys - 已接入: 145+ 文件 (Dashboard/Login/K8s 主页面/Monitor/System/Deploy) - 语言切换器在 Header 中可用 (🇨🇳/🇺🇸) - tsc 零错误, Vite 构建成功
This commit is contained in:
1 parent
ecec73989b
commit
72bfb0e12d
191 files changed
+5057
-7592
No files matched your search
@@ -14,7 +14,6 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
|
||||
import { toast } from 'sonner';
|
||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||
import { buildNodeApi, serverApi, type BuildNode } from '@/api';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface ServerItem {
|
||||
id: number;
|
||||
@@ -24,7 +23,6 @@ interface ServerItem {
|
||||
|
||||
// 构建节点管理(Jenkins Agent 风格 SSH 节点) // Logan.Li
|
||||
export default function BuildNodesManager({ embedded = false }: { embedded?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
const confirm = useConfirm();
|
||||
const [nodes, setNodes] = useState<BuildNode[]>([]);
|
||||
const [master, setMaster] = useState({ name: 'Master', max_executors: 2, running_count: 0 });
|
||||
@@ -60,7 +58,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
const srvList = (srvRes as { list?: ServerItem[] })?.list || [];
|
||||
setServers(srvList);
|
||||
} catch {
|
||||
toast.error(t('deploy.buildnodesmanager.sb9faa2'));
|
||||
toast.error('加载构建节点失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -92,7 +90,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!name.trim() || !serverId) {
|
||||
toast.error(t('deploy.buildnodesmanager.sda51a7'));
|
||||
toast.error('请填写名称并选择服务器');
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
@@ -107,29 +105,29 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
};
|
||||
if (editNode) {
|
||||
await buildNodeApi.update(editNode.id, payload);
|
||||
toast.success(t('deploy.toast.updated'));
|
||||
toast.success('已更新');
|
||||
} else {
|
||||
await buildNodeApi.create(payload);
|
||||
toast.success(t('deploy.toast.created'));
|
||||
toast.success('已创建');
|
||||
}
|
||||
setDialogOpen(false);
|
||||
load();
|
||||
} catch {
|
||||
toast.error(t('common.operationFailed'));
|
||||
toast.error('操作失败');
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (node: BuildNode) => {
|
||||
const ok = await confirm({ title: t('k8s.topology.deleteNode'), description: t('deploy.buildnodesmanager.s359e74', { node.name: node.name }) });
|
||||
const ok = await confirm({ title: '删除节点', description: `确定删除构建节点「${node.name}」?` });
|
||||
if (!ok) return;
|
||||
try {
|
||||
await buildNodeApi.delete(node.id);
|
||||
toast.success(t('alert.deleted'));
|
||||
toast.success('已删除');
|
||||
load();
|
||||
} catch {
|
||||
toast.error(t('deploy.buildnodesmanager.s577b18'));
|
||||
toast.error('删除失败,请确认无流水线绑定');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -138,12 +136,12 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
try {
|
||||
const res = await buildNodeApi.test(node.id) as { success?: boolean; message?: string };
|
||||
if (res?.success) {
|
||||
toast.success(t('deploy.buildnodesmanager.s1c5ca7', { res.message || '': res.message || '' }));
|
||||
toast.success(`连通成功: ${res.message || ''}`);
|
||||
} else {
|
||||
toast.error(res?.message || t('aiops.llmTestFailed'));
|
||||
toast.error(res?.message || '连接失败');
|
||||
}
|
||||
} catch {
|
||||
toast.error(t('deploy.buildnodesmanager.s9710d9'));
|
||||
toast.error('测试失败');
|
||||
} finally {
|
||||
setTestingId(null);
|
||||
}
|
||||
@@ -154,14 +152,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
await buildNodeApi.update(node.id, { enabled: !node.enabled });
|
||||
load();
|
||||
} catch {
|
||||
toast.error(t('alert.updateFailed'));
|
||||
toast.error('更新失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveMaster = async () => {
|
||||
const n = Number(masterMax);
|
||||
if (!n || n < 1) {
|
||||
toast.error(t('deploy.buildnodesmanager.s5f25f4'));
|
||||
toast.error('并发数至少为 1');
|
||||
return;
|
||||
}
|
||||
setSavingMaster(true);
|
||||
@@ -173,9 +171,9 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
max_executors: m?.max_executors ?? n,
|
||||
running_count: m?.running_count ?? prev.running_count,
|
||||
}));
|
||||
toast.success(t('deploy.buildnodesmanager.scb1ab6'));
|
||||
toast.success('Master 并发已更新');
|
||||
} catch {
|
||||
toast.error(t('alert.updateFailed'));
|
||||
toast.error('更新失败');
|
||||
} finally {
|
||||
setSavingMaster(false);
|
||||
}
|
||||
@@ -186,7 +184,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
<ContentPanel
|
||||
toolbar={(
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)]">{t('pipeline.buildNode')}</span>
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)]">构建节点</span>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="ghost" size="sm" onClick={load} disabled={loading}>
|
||||
<RefreshCw className={`h-3.5 w-3.5 mr-1${loading ? ' animate-spin' : ''}`} />刷新
|
||||
@@ -202,14 +200,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Cpu className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)]">{t('deploy.buildnodesmanager.sae7536')}</span>
|
||||
<Badge variant="outline" className="text-[10px]">{t('deploy.buildnodesmanager.sa419fe')}</Badge>
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)]">Master(内置)</span>
|
||||
<Badge variant="outline" className="text-[10px]">默认调度</Badge>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">
|
||||
占用 {master.running_count}/{master.max_executors}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-xs text-[var(--dg-text-muted)] shrink-0">{t('deploy.buildnodesmanager.s8e408e')}</Label>
|
||||
<Label className="text-xs text-[var(--dg-text-muted)] shrink-0">最大并发</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
@@ -219,7 +217,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
className="h-7 w-16 text-xs dg-input"
|
||||
/>
|
||||
<Button size="sm" className="h-7 text-xs" onClick={handleSaveMaster} disabled={savingMaster}>
|
||||
{savingMaster ? t('deploy.buildnodesmanager.s7cbf2d') : t('common.save')}
|
||||
{savingMaster ? '保存中' : '保存'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -230,9 +228,9 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
|
||||
{nodes.length === 0 ? (
|
||||
<PageEmpty
|
||||
title=t('deploy.buildnodesmanager.s2795bd')
|
||||
description=t('deploy.buildnodesmanager.s90fb5e')
|
||||
action={<Button size="sm" onClick={openCreate}><Plus className="h-3.5 w-3.5 mr-1" />{t('k8s.topology.addNode')}</Button>}
|
||||
title="暂无远程构建节点"
|
||||
description="关联服务器资产添加 SSH 节点,将重型构建任务调度到高性能机器"
|
||||
action={<Button size="sm" onClick={openCreate}><Plus className="h-3.5 w-3.5 mr-1" />添加节点</Button>}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
@@ -245,7 +243,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="font-medium text-sm text-[var(--dg-text-primary)]">{node.name}</span>
|
||||
<Badge variant={node.enabled ? 'default' : 'secondary'} className="text-[10px]">
|
||||
{node.enabled ? t('common.enable') : t('common.disable')}
|
||||
{node.enabled ? '启用' : '禁用'}
|
||||
</Badge>
|
||||
<span className="text-[10px] text-[var(--dg-text-muted)] font-mono">
|
||||
{node.hostname || node.ip || `server#${node.server_id}`}
|
||||
@@ -259,10 +257,10 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<Button variant="ghost" size="sm" className="h-7 text-xs" disabled={testingId === node.id} onClick={() => handleTest(node)}>
|
||||
{testingId === node.id ? t('deploy.buildnodesmanager.sf85549') : t('common.test')}
|
||||
{testingId === node.id ? '测试中' : '测试'}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => handleToggle(node)}>
|
||||
{node.enabled ? t('common.disable') : t('common.enable')}
|
||||
{node.enabled ? '禁用' : '启用'}
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={() => openEdit(node)}>
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
@@ -283,22 +281,22 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogContent className="max-w-md bg-[var(--dg-bg-primary)] border-[var(--dg-border)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editNode ? t('k8s.topology.editNode') : t('k8s.topology.addNode')}</DialogTitle>
|
||||
<DialogTitle>{editNode ? '编辑节点' : '添加节点'}</DialogTitle>
|
||||
<DialogDescription className="text-[var(--dg-text-muted)]">
|
||||
基于服务器 SSH 凭据注册构建 Agent,流水线可绑定或自动调度
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">{t('k8s.topology.nodeName')}</Label>
|
||||
<Label className="text-xs">节点名称</Label>
|
||||
<Input value={name} onChange={(e) => setName(e.target.value)} className="h-8 text-xs dg-input" placeholder="build-node-01" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">{t('deploy.buildnodesmanager.sdfdaf1')}</Label>
|
||||
<Label className="text-xs">关联服务器</Label>
|
||||
<Select value={serverId || '__none'} onValueChange={(v) => setServerId(v === '__none' ? '' : v)}>
|
||||
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder=t('deploy.buildnodesmanager.s030235') /></SelectTrigger>
|
||||
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder="选择服务器" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none">{t('common.selectPlaceholder')}</SelectItem>
|
||||
<SelectItem value="__none">请选择</SelectItem>
|
||||
{servers.map((s) => (
|
||||
<SelectItem key={s.id} value={String(s.id)}>
|
||||
{s.hostname} ({s.ip})
|
||||
@@ -309,25 +307,25 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">{t('deploy.buildnodesmanager.s8e408e')}</Label>
|
||||
<Label className="text-xs">最大并发</Label>
|
||||
<Input value={maxExecutors} onChange={(e) => setMaxExecutors(e.target.value)} type="number" min={1} className="h-8 text-xs dg-input" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">{t('deploy.buildnodesmanager.sbcfcd0')}</Label>
|
||||
<Label className="text-xs">工作区路径</Label>
|
||||
<Input value={workspacePath} onChange={(e) => setWorkspacePath(e.target.value)} className="h-8 text-xs font-mono dg-input" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">{t('common.tag')} <span className="text-[var(--dg-text-muted)]">{t('deploy.buildnodesmanager.s6c9a78')}</span></Label>
|
||||
<Label className="text-xs">标签 <span className="text-[var(--dg-text-muted)]">(可选)</span></Label>
|
||||
<Input value={labels} onChange={(e) => setLabels(e.target.value)} className="h-8 text-xs dg-input" placeholder="linux,docker,high-mem" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">{t('common.description')}</Label>
|
||||
<Label className="text-xs">描述</Label>
|
||||
<Input value={description} onChange={(e) => setDescription(e.target.value)} className="h-8 text-xs dg-input" />
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button variant="outline" onClick={() => setDialogOpen(false)}>{t('common.cancel')}</Button>
|
||||
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? i18n.t('deploy.buildnodesmanager.s7cbf2d') : i18n.t('common.save')}</Button>
|
||||
<Button variant="outline" onClick={() => setDialogOpen(false)}>取消</Button>
|
||||
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? '保存中' : '保存'}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
@@ -344,7 +342,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
}
|
||||
|
||||
return (
|
||||
<PageShell title=i18n.t('pipeline.buildNode') subtitle=i18n.t('deploy.buildnodesmanager.sb2b825')>
|
||||
<PageShell title="构建节点" subtitle="Master 与远程 SSH 构建节点">
|
||||
{panel}
|
||||
{dialog}
|
||||
</PageShell>
|
||||
|
||||
Reference in new issue
Block a user