refactor(i18n): U27 多模块 phantom key 批量清理 — Cloud/Deploy/Agent/Monitor/System 5 模块 25 文件, 180 个 phantom key → 51 残留, 129 个替换为语义化键, 新增 12 子命名空间, zh/en 7185→7294 +109 键对齐, tsc -b=0, npm build✓ (16.31s), ESLint=0/0/0, go build=0, 17/17 测试通过
This commit is contained in:
1 parent
7f5295cfc2
commit
b812b3b20c
27 files changed
+459
-148
No files matched your search
@@ -59,7 +59,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
const srvList = (srvRes as { list?: ServerItem[] })?.list || [];
|
||||
setServers(srvList);
|
||||
} catch {
|
||||
toast.error(i18n.t('加载构建节点失败'));
|
||||
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!name.trim() || !serverId) {
|
||||
toast.error(i18n.t('加载构建节点失败'));
|
||||
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
@@ -121,14 +121,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
};
|
||||
|
||||
const handleDelete = async (node: BuildNode) => {
|
||||
const ok = await confirm({ title: i18n.t('kubernetes.topology.deleteNode'), description: `确定删除构建节点「${node.name}」?` });
|
||||
const ok = await confirm({ title: i18n.t('kubernetes.topology.deleteNode'), description: i18n.t('deploy.buildNodes.deleteConfirm', { name: node.name }) });
|
||||
if (!ok) return;
|
||||
try {
|
||||
await buildNodeApi.delete(node.id);
|
||||
toast.success(i18n.t('monitor.silenceRules.deleted'));
|
||||
load();
|
||||
} catch {
|
||||
toast.error(i18n.t('加载构建节点失败'));
|
||||
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -137,7 +137,7 @@ 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(`连通成功: ${res.message || ''}`);
|
||||
toast.success(i18n.t('deploy.buildNodes.testSuccess') + (res.message ? `: ${res.message}` : ''));
|
||||
} else {
|
||||
toast.error(res?.message || i18n.t('aiops.llmTestFailed'));
|
||||
}
|
||||
@@ -160,7 +160,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
const handleSaveMaster = async () => {
|
||||
const n = Number(masterMax);
|
||||
if (!n || n < 1) {
|
||||
toast.error(i18n.t('加载构建节点失败'));
|
||||
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
|
||||
return;
|
||||
}
|
||||
setSavingMaster(true);
|
||||
@@ -172,7 +172,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
max_executors: m?.max_executors ?? n,
|
||||
running_count: m?.running_count ?? prev.running_count,
|
||||
}));
|
||||
toast.success(i18n.t('已删除'));
|
||||
toast.success(i18n.t('deploy.buildNodes.deleted'));
|
||||
} catch {
|
||||
toast.error(i18n.t('kubernetes.deployment.imageUpdateFailed'));
|
||||
} finally {
|
||||
@@ -201,10 +201,10 @@ 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)]">Master(内置)</span>
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)]">{i18n.t('deploy.buildNodes.masterBuiltin')}</span>
|
||||
<Badge variant="outline" className="text-[10px]"> {i18n.t('pipeline.buildnodesmanag.sa419fe03')} </Badge>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">
|
||||
占用 {master.running_count}/{master.max_executors}
|
||||
{i18n.t('deploy.buildNodes.executorUsage', { running: master.running_count, max: master.max_executors })}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -218,19 +218,19 @@ 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 ? i18n.t('保存中') : i18n.t('task.script.save')}
|
||||
{savingMaster ? i18n.t('deploy.buildNodes.saving') : i18n.t('task.script.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[11px] text-[var(--dg-text-muted)] mt-2">
|
||||
未指定节点时优先在 Master 执行;并发设为 1 时按触发顺序串行排队;满载后自动调度到空闲远程节点
|
||||
{i18n.t('deploy.buildNodes.schedulingHint')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{nodes.length === 0 ? (
|
||||
<PageEmpty
|
||||
title={i18n.t('暂无远程构建节点')}
|
||||
description={i18n.t('关联服务器资产添加 SSH 节点,将重型构建任务调度到高性能机器')}
|
||||
title={i18n.t('deploy.buildNodes.noRemoteNodes')}
|
||||
description={i18n.t('deploy.buildNodes.noRemoteDesc')}
|
||||
action={<Button size="sm" onClick={openCreate}><Plus className="h-3.5 w-3.5 mr-1" /> {i18n.t('kubernetes.topology.addNode')} </Button>}
|
||||
/>
|
||||
) : (
|
||||
@@ -251,14 +251,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 mt-1 text-[11px] text-[var(--dg-text-muted)]">
|
||||
<span>执行器 {node.running_count || 0}/{node.max_executors}</span>
|
||||
{node.labels && <span>标签 {node.labels}</span>}
|
||||
<span>{i18n.t('deploy.buildNodes.executorLabel', { running: node.running_count || 0, max: node.max_executors })}</span>
|
||||
{node.labels && <span>{i18n.t('deploy.buildNodes.labelsLabel', { labels: node.labels })}</span>}
|
||||
<span className="truncate">{node.workspace_path}</span>
|
||||
</div>
|
||||
</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 ? i18n.t('测试中') : i18n.t('pipeline.testTool')}
|
||||
{testingId === node.id ? i18n.t('deploy.buildNodes.testing') : i18n.t('pipeline.testTool')}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => handleToggle(node)}>
|
||||
{node.enabled ? i18n.t('system.userInactive') : i18n.t('system.userActive')}
|
||||
@@ -295,7 +295,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs"> {i18n.t('>标签 <span className=')} </Label>
|
||||
<Select value={serverId || '__none'} onValueChange={(v) => setServerId(v === '__none' ? '' : v)}>
|
||||
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('选择服务器')} /></SelectTrigger>
|
||||
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('deploy.buildNodes.selectServer')} /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none"> {i18n.t('k8s.storage.pleaseSelect')} </SelectItem>
|
||||
{servers.map((s) => (
|
||||
@@ -326,7 +326,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button variant="outline" onClick={() => setDialogOpen(false)}> {i18n.t('rbac.jobRole.cancel')} </Button>
|
||||
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? i18n.t('保存中') : i18n.t('task.script.save')}</Button>
|
||||
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? i18n.t('deploy.buildNodes.saving') : i18n.t('task.script.save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
@@ -343,7 +343,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
||||
}
|
||||
|
||||
return (
|
||||
<PageShell title={i18n.t('pipeline.buildNodes')} subtitle={i18n.t('构建节点')}>
|
||||
<PageShell title={i18n.t('pipeline.buildNodes')} subtitle={i18n.t('deploy.pipeline.buildNodes')}>
|
||||
{panel}
|
||||
{dialog}
|
||||
</PageShell>
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function DeployRBAC() {
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!newName.trim() || !newCode.trim() || !newPattern.trim()) {
|
||||
toast.error(i18n.t('请填写名称、标识与正则')); return;
|
||||
toast.error(i18n.t('deploy.rbac.policy.fillFields')); return;
|
||||
}
|
||||
setSaving(true);
|
||||
try {
|
||||
@@ -147,7 +147,7 @@ export default function DeployRBAC() {
|
||||
|
||||
const handleDelete = async (role: ItemRole) => {
|
||||
const ok = await _confirm({
|
||||
title: i18n.t('删除权限'),
|
||||
title: i18n.t('deploy.rbac.policy.deletePolicy'),
|
||||
description: `确定删除「${role.name}」?将影响 ${matchCount[role.id]?.length || 0} 个发布任务`,
|
||||
variant: 'destructive',
|
||||
});
|
||||
@@ -165,7 +165,7 @@ export default function DeployRBAC() {
|
||||
};
|
||||
|
||||
const handleAssign = async () => {
|
||||
if (!selectedUserId || !assignRoleId) { toast.error(i18n.t('请选择用户和权限')); return; }
|
||||
if (!selectedUserId || !assignRoleId) { toast.error(i18n.t('deploy.rbac.policy.selectUserPermission')); return; }
|
||||
setAssignLoading(true);
|
||||
try {
|
||||
await deployApi.assignItemRole(Number(selectedUserId), {
|
||||
@@ -190,8 +190,8 @@ export default function DeployRBAC() {
|
||||
|
||||
const selectedUser = users.find((u) => String(u.id) === selectedUserId);
|
||||
const pageTabs = [
|
||||
{ id: 'roles', label: i18n.t('权限策略'), badge: roles.length || undefined },
|
||||
{ id: 'users', label: i18n.t('用户授权') },
|
||||
{ id: 'roles', label: i18n.t('deploy.rbac.policy.policyStrategy'), badge: roles.length || undefined },
|
||||
{ id: 'users', label: i18n.t('deploy.rbac.policy.userAuth') },
|
||||
{ id: 'groups', label: i18n.t('pipeline.groupLabel') },
|
||||
{ id: 'preview', label: i18n.t('pipeline.matchPreview') },
|
||||
];
|
||||
@@ -200,7 +200,7 @@ export default function DeployRBAC() {
|
||||
<PageShell
|
||||
variant="list"
|
||||
title={i18n.t('deploy.deployNav.jobPermission')}
|
||||
subtitle={i18n.t('按流水线 code / Job 名称正则分配发布权限')}
|
||||
subtitle={i18n.t('deploy.rbac.policy.subtitle')}
|
||||
icon={<Shield className="h-5 w-5" />}
|
||||
loading={loading}
|
||||
tabs={pageTabs}
|
||||
@@ -217,8 +217,8 @@ export default function DeployRBAC() {
|
||||
roles.length === 0 ? (
|
||||
<PageEmpty
|
||||
icon={<Shield className="h-10 w-10" />}
|
||||
title={i18n.t('暂无 Job 权限策略')}
|
||||
description={i18n.t('创建策略后,用户将按正则自动获得匹配流水线的访问权限')}
|
||||
title={i18n.t('deploy.rbac.policy.noPolicy')}
|
||||
description={i18n.t('deploy.rbac.policy.createHint')}
|
||||
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" /> {i18n.t('monitor.logrbactsx.s6058845c')} </Button>}
|
||||
/>
|
||||
) : (
|
||||
@@ -296,7 +296,7 @@ export default function DeployRBAC() {
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1 w-48">
|
||||
<Label className="text-xs"> {i18n.t('权限策略')} </Label>
|
||||
<Label className="text-xs"> {i18n.t('deploy.rbac.policy.policyStrategy')} </Label>
|
||||
<Select value={assignRoleId} onValueChange={setAssignRoleId}>
|
||||
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder={i18n.t('kubernetes.hpa.selectPolicy')} /></SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -99,8 +99,8 @@ export default function UserGroupsManager({ embedded = false }: { embedded?: boo
|
||||
const listBody = !loading && groups.length === 0 ? (
|
||||
<PageEmpty
|
||||
icon={<Users className="h-10 w-10" />}
|
||||
title={i18n.t('暂无用户组')}
|
||||
description={i18n.t('点击「新建用户组」创建第一个用户组')}
|
||||
title={i18n.t('deploy.userGroup.noGroups')}
|
||||
description={i18n.t('deploy.userGroup.createFirstHint')}
|
||||
action={(
|
||||
<Button size="sm" onClick={() => { setEditId(null); setFormName(''); setFormDesc(''); setFormOpen(true); }}>
|
||||
<Plus className="w-4 h-4 mr-1" />新建用户组
|
||||
@@ -165,7 +165,7 @@ export default function UserGroupsManager({ embedded = false }: { embedded?: boo
|
||||
<DialogContent>
|
||||
<DialogHeader><DialogTitle>{editId ? i18n.t('deploy.rbac.groupEditTitle') : i18n.t('pipeline.newGroup')}</DialogTitle></DialogHeader>
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1"><Label> {i18n.t('monitor.groupName')} </Label><Input value={formName} onChange={e => setFormName(e.target.value)} placeholder={i18n.t('例如: 后端开发组')} /></div>
|
||||
<div className="space-y-1"><Label> {i18n.t('monitor.groupName')} </Label><Input value={formName} onChange={e => setFormName(e.target.value)} placeholder={i18n.t('deploy.userGroup.namePlaceholder')} /></div>
|
||||
<div className="space-y-1"><Label> {i18n.t('aiops.llmModelDesc')} </Label><Input value={formDesc} onChange={e => setFormDesc(e.target.value)} placeholder={i18n.t('rbac.jobRole.placeholder.description')} /></div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4 pt-4 border-t">
|
||||
@@ -190,14 +190,14 @@ export default function UserGroupsManager({ embedded = false }: { embedded?: boo
|
||||
<PageShell
|
||||
variant="list"
|
||||
title={i18n.t('pipeline.releaseTitle')}
|
||||
subtitle={i18n.t('用户组 — 批量授权流水线访问权限')}
|
||||
subtitle={i18n.t('deploy.userGroup.subtitle')}
|
||||
icon={<Users className="h-5 w-5" />}
|
||||
loading={loading}
|
||||
metrics={(
|
||||
<StatStrip
|
||||
items={[
|
||||
{ label: i18n.t('pipeline.groupLabel'), value: groups.length, color: 'blue' },
|
||||
{ label: i18n.t('成员总数'), value: memberCount, color: 'purple' },
|
||||
{ label: i18n.t('deploy.userGroup.memberCount'), value: memberCount, color: 'purple' },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -149,8 +149,8 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
||||
<div className="space-y-3">
|
||||
<div className="flex gap-1 p-1 rounded-lg bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)]">
|
||||
{([
|
||||
{ key: 'ssh' as const, label: i18n.t('主机发布'), icon: Server },
|
||||
{ key: 'k8s' as const, label: i18n.t('主机发布'), icon: Network },
|
||||
{ key: 'ssh' as const, label: i18n.t('deploy.target.hostDeploy'), icon: Server },
|
||||
{ key: 'k8s' as const, label: i18n.t('deploy.target.hostDeploy'), icon: Network },
|
||||
]).map((item) => {
|
||||
const active = value.deployTarget === item.key;
|
||||
const Icon = item.icon;
|
||||
@@ -200,7 +200,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
||||
<Input
|
||||
value={serverSearch}
|
||||
onChange={(e) => setServerSearch(e.target.value)}
|
||||
placeholder={i18n.t('搜索主机名或 IP')}
|
||||
placeholder={i18n.t('deploy.target.searchHost')}
|
||||
className="pl-8 h-8 text-xs dg-input"
|
||||
/>
|
||||
</div>
|
||||
@@ -296,7 +296,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
||||
<Input
|
||||
readOnly
|
||||
value={value.k8sDeployment ? `${value.k8sNamespace}/${value.k8sDeployment}` : ''}
|
||||
placeholder={i18n.t('从下方列表选择')}
|
||||
placeholder={i18n.t('deploy.target.selectFromList')}
|
||||
className="h-8 text-xs dg-input font-mono"
|
||||
/>
|
||||
</div>
|
||||
@@ -308,7 +308,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
||||
<Input
|
||||
value={depSearch}
|
||||
onChange={(e) => setDepSearch(e.target.value)}
|
||||
placeholder={i18n.t('搜索 Deployment 或命名空间')}
|
||||
placeholder={i18n.t('deploy.target.searchDeployment')}
|
||||
className="pl-8 h-8 text-xs dg-input"
|
||||
/>
|
||||
</div>
|
||||
@@ -353,7 +353,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
||||
<Input
|
||||
value={value.k8sContainerName}
|
||||
onChange={(e) => onChange({ k8sContainerName: e.target.value })}
|
||||
placeholder={i18n.t('搜索 Deployment 或命名空间')}
|
||||
placeholder={i18n.t('deploy.target.searchDeployment')}
|
||||
className="h-8 text-xs font-mono dg-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -10,12 +10,12 @@ import {
|
||||
|
||||
// 流水线阶段定义(固定顺序) // Logan.Li
|
||||
export const STAGE_DEFS = [
|
||||
{ key: 'checkout', label: i18n.t('pipeline.checkout'), icon: GitBranch, color: '#6366F1', order: 1, needs: ['repo'], desc: i18n.t('代码检出') },
|
||||
{ key: 'build', label: i18n.t('pipeline.build'), icon: Box, color: '#F59E0B', order: 2, needs: [], desc: i18n.t('构建') },
|
||||
{ key: 'test', label: i18n.t('pipeline.testTool'), icon: CheckCircle, color: '#10B981', order: 3, needs: [], desc: i18n.t('测试') },
|
||||
{ key: 'image', label: i18n.t('pipeline.imageBuild'), icon: Container, color: '#06B6D4', order: 4, needs: ['registry'], desc: i18n.t('镜像打包') },
|
||||
{ key: 'deploy', label: i18n.t('pipeline.deploy2'), icon: Rocket, color: '#EF4444', order: 5, needs: [], desc: i18n.t('部署') },
|
||||
{ key: 'verify', label: i18n.t('system.credentialVerify'), icon: ShieldCheck, color: '#3B82F6', order: 6, needs: [], desc: i18n.t('验证') },
|
||||
{ key: 'checkout', label: i18n.t('pipeline.checkout'), icon: GitBranch, color: '#6366F1', order: 1, needs: ['repo'], desc: i18n.t('deploy.stage.checkout') },
|
||||
{ key: 'build', label: i18n.t('pipeline.build'), icon: Box, color: '#F59E0B', order: 2, needs: [], desc: i18n.t('deploy.stage.build') },
|
||||
{ key: 'test', label: i18n.t('pipeline.testTool'), icon: CheckCircle, color: '#10B981', order: 3, needs: [], desc: i18n.t('deploy.stage.test') },
|
||||
{ key: 'image', label: i18n.t('pipeline.imageBuild'), icon: Container, color: '#06B6D4', order: 4, needs: ['registry'], desc: i18n.t('deploy.stage.imageBuild') },
|
||||
{ key: 'deploy', label: i18n.t('pipeline.deploy2'), icon: Rocket, color: '#EF4444', order: 5, needs: [], desc: i18n.t('deploy.stage.deploy') },
|
||||
{ key: 'verify', label: i18n.t('system.credentialVerify'), icon: ShieldCheck, color: '#3B82F6', order: 6, needs: [], desc: i18n.t('deploy.stage.verify') },
|
||||
].sort((a, b) => a.order - b.order);
|
||||
|
||||
// 运行日志条目 // Logan.Li
|
||||
|
||||
Reference in new issue
Block a user