feat(i18n): 剩余模块全量国际化 — 51% 覆盖率 (231 文件接入 t())

批量转换 163 个文件,3204 处中文替换为 t() 调用:
- Monitor: 日志中心/告警中心/数据源/大屏/站点拨测/SNMP/中间件 全部转换
- Kubernetes: ResourceVisualEditor/NamespaceResourcePage/拓扑/事件 全部转换
- Deploy: 流水线/发布/凭证/RBAC 全部转换
- Server/Cloud/Agent/Task/Layout/UI: 全部转换
- 翻译词典扩展至 6474 行 (22 namespaces, 5000+ keys)
- tsc 编译零错误
This commit is contained in:
attacker committed 2026-07-03 01:51:57 +08:00
1 parent 78c27037e3
commit ecec73989b
166 files changed
+6925 -2800

No files matched your search

+45 -42
View File
@@ -17,6 +17,8 @@ import UserGroupsManager from './UserGroupsManager';
import { JobRoleCreateDialog } from './deploy-rbac/JobRoleCreateDialog';
import { matchPipelines, pipelineDisplayLabel, type PipelineMatchItem } from './deploy-rbac/matchPipeline';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
import i18n from '@/i18n';
interface ItemRole {
id: number;
@@ -37,15 +39,16 @@ interface AssignedUser {
}
const ROLE_LABEL: Record<string, string> = {
owner: '拥有者',
editor: '编辑者',
viewer: '查看者',
owner: i18n.t('common.owner'),
editor: i18n.t('pipeline.editor'),
viewer: i18n.t('pipeline.viewer'),
};
const RBAC_TABS = ['roles', 'users', 'preview', 'groups'] as const;
// 发布 Job 权限 — 正则匹配 code / job_name,Jenkins Role Strategy 风格 // Logan.Li
export default function DeployRBAC() {
const { t } = useTranslation();
const [searchParams] = useSearchParams();
const tabFromUrl = searchParams.get('tab');
const [roles, setRoles] = useState<ItemRole[]>([]);
@@ -110,7 +113,7 @@ export default function DeployRBAC() {
const handleCreate = async () => {
if (!newName.trim() || !newCode.trim() || !newPattern.trim()) {
toast.error('请填写名称、标识与正则'); return;
toast.error(t('deploy.deployrbac.sf6e80b')); return;
}
setSaving(true);
try {
@@ -121,84 +124,84 @@ export default function DeployRBAC() {
role: newRole,
description: newDesc,
});
toast.success('已创建');
toast.success(t('deploy.toast.created'));
setCreateOpen(false);
setNewName(''); setNewCode(''); setNewPattern(''); setNewRole('editor'); setNewDesc('');
loadData();
} catch (e: unknown) {
toast.error((e as Error)?.message || '创建失败');
toast.error((e as Error)?.message || t('monitor.quickSetupFailed'));
} finally { setSaving(false); }
};
const handleUpdate = async (id: number) => {
if (!editPattern.trim()) { toast.error('正则不能为空'); return; }
if (!editPattern.trim()) { toast.error(t('pipeline.regexEmpty')); return; }
setSaving(true);
try {
await deployApi.updateItemRole(id, { pattern: editPattern, role: editRole });
toast.success('已更新');
toast.success(t('deploy.toast.updated'));
setEditing(null);
loadData();
} catch (e: unknown) {
toast.error((e as Error)?.message || '更新失败');
toast.error((e as Error)?.message || t('alert.updateFailed'));
} finally { setSaving(false); }
};
const handleDelete = async (role: ItemRole) => {
const ok = await _confirm({
title: '删除权限',
description: `确定删除「${role.name}」?将影响 ${matchCount[role.id]?.length || 0} 个发布任务`,
title: t('deploy.deployrbac.s5d79e4'),
description: t('deploy.deployrbac.s2c1ef5', { role.name: role.name, matchCount[role.id]?.length || 0: matchCount[role.id]?.length || 0 }),
variant: 'destructive',
});
if (!ok) return;
try { await deployApi.deleteItemRole(role.id); toast.success('已删除'); loadData(); }
catch { toast.error('删除失败'); }
try { await deployApi.deleteItemRole(role.id); toast.success(t('alert.deleted')); loadData(); }
catch { toast.error(t('common.deleteFailed')); }
};
const handleToggleStatus = async (role: ItemRole) => {
try {
await deployApi.updateItemRole(role.id, { status: role.status ? 0 : 1 });
toast.success(role.status ? '已禁用' : '已启用');
toast.success(role.status ? t('alert.disabled') : t('alert.enabled'));
loadData();
} catch { toast.error('操作失败'); }
} catch { toast.error(t('common.operationFailed')); }
};
const handleAssign = async () => {
if (!selectedUserId || !assignRoleId) { toast.error('请选择用户和权限'); return; }
if (!selectedUserId || !assignRoleId) { toast.error(t('deploy.deployrbac.s0fc539')); return; }
setAssignLoading(true);
try {
await deployApi.assignItemRole(Number(selectedUserId), {
user_id: Number(selectedUserId),
item_role_id: Number(assignRoleId),
});
toast.success('已分配');
toast.success(t('common.assigned'));
setAssignRoleId('');
loadAssignments();
} catch (e: unknown) {
toast.error((e as Error)?.message || '分配失败');
toast.error((e as Error)?.message || t('deploy.rbac.assignFail'));
} finally { setAssignLoading(false); }
};
const handleRevoke = async (id: number) => {
try {
await deployApi.removeItemRole(Number(selectedUserId), id);
toast.success('已撤销');
toast.success(t('deploy.toast.revoked'));
loadAssignments();
} catch { toast.error('撤销失败'); }
} catch { toast.error(t('deploy.toast.revokeFailed')); }
};
const selectedUser = users.find((u) => String(u.id) === selectedUserId);
const pageTabs = [
{ id: 'roles', label: '权限策略', badge: roles.length || undefined },
{ id: 'users', label: '用户授权' },
{ id: 'groups', label: '用户组' },
{ id: 'preview', label: '命中预览' },
{ id: 'roles', label: t('deploy.deployrbac.s93ef07'), badge: roles.length || undefined },
{ id: 'users', label: t('deploy.deployrbac.s2de895') },
{ id: 'groups', label: t('pipeline.userGroup') },
{ id: 'preview', label: t('pipeline.matchPreview') },
];
return (
<PageShell
variant="list"
title="Job 权限"
subtitle="按流水线 code / Job 名称正则分配发布权限"
title=t('deploy.deployNav.jobPermission')
subtitle=t('deploy.deployrbac.sa596d6')
icon={<Shield className="h-5 w-5" />}
loading={loading}
tabs={pageTabs}
@@ -215,9 +218,9 @@ export default function DeployRBAC() {
roles.length === 0 ? (
<PageEmpty
icon={<Shield className="h-10 w-10" />}
title="暂无 Job 权限策略"
description="创建策略后,用户将按正则自动获得匹配流水线的访问权限"
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" />新建权限</Button>}
title=t('deploy.deployrbac.sca578c')
description=t('deploy.deployrbac.s5ac382')
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" />{t('deploy.deployrbac.s605884')}</Button>}
/>
) : (
<div className="space-y-2">
@@ -231,7 +234,7 @@ export default function DeployRBAC() {
<Badge variant="outline" className="text-[10px] border-[var(--dg-border)]">
{ROLE_LABEL[role.role] || role.role}
</Badge>
{role.status === 0 && <Badge variant="outline" className="text-[10px]">已禁用</Badge>}
{role.status === 0 && <Badge variant="outline" className="text-[10px]">{t('alert.disabled')}</Badge>}
<Badge
variant="outline"
className="text-[10px] cursor-pointer border-[var(--dg-border)]"
@@ -252,9 +255,9 @@ export default function DeployRBAC() {
<Select value={editRole} onValueChange={setEditRole}>
<SelectTrigger className="h-7 w-20 text-xs"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="owner">拥有者</SelectItem>
<SelectItem value="editor">编辑者</SelectItem>
<SelectItem value="viewer">查看者</SelectItem>
<SelectItem value="owner">{t('common.owner')}</SelectItem>
<SelectItem value="editor">{t('pipeline.editor')}</SelectItem>
<SelectItem value="viewer">{t('pipeline.viewer')}</SelectItem>
</SelectContent>
</Select>
<Button size="icon" variant="ghost" className="h-7 w-7" onClick={() => handleUpdate(role.id)}><Check className="w-3.5 h-3.5" /></Button>
@@ -262,8 +265,8 @@ export default function DeployRBAC() {
</>
) : (
<>
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => { setEditing(role.id); setEditPattern(role.pattern); setEditRole(role.role); }}>编辑</Button>
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => handleToggleStatus(role)}>{role.status ? '禁用' : '启用'}</Button>
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => { setEditing(role.id); setEditPattern(role.pattern); setEditRole(role.role); }}>{t('common.edit')}</Button>
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => handleToggleStatus(role)}>{role.status ? t('common.disable') : t('common.enable')}</Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-red)]" onClick={() => handleDelete(role)}><Trash2 className="w-3.5 h-3.5" /></Button>
</>
)}
@@ -281,9 +284,9 @@ export default function DeployRBAC() {
<CardContent className="p-4 space-y-4">
<div className="flex flex-wrap gap-3 items-end">
<div className="space-y-1 flex-1 min-w-[200px]">
<Label className="text-xs">用户</Label>
<Label className="text-xs">{t('system.roleUsers')}</Label>
<Select value={selectedUserId} onValueChange={setSelectedUserId}>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder="选择用户" /></SelectTrigger>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder=t('deploy.rbac.selectUser') /></SelectTrigger>
<SelectContent className="max-h-60">
{users.map((u) => (
<SelectItem key={u.id as number} value={String(u.id)}>
@@ -294,9 +297,9 @@ export default function DeployRBAC() {
</Select>
</div>
<div className="space-y-1 w-48">
<Label className="text-xs">权限策略</Label>
<Label className="text-xs">{t('deploy.deployrbac.s93ef07')}</Label>
<Select value={assignRoleId} onValueChange={setAssignRoleId}>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder="选择策略" /></SelectTrigger>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder=t('kubernetes.hpa.selectPolicy') /></SelectTrigger>
<SelectContent>
{roles.filter((r) => r.status === 1).map((r) => (
<SelectItem key={r.id} value={String(r.id)}>{r.name} ({ROLE_LABEL[r.role]})</SelectItem>
@@ -311,7 +314,7 @@ export default function DeployRBAC() {
</div>
{selectedUser ? (
assignments.length === 0 ? (
<p className="text-xs text-[var(--dg-text-muted)]">该用户尚未分配 Job 权限</p>
<p className="text-xs text-[var(--dg-text-muted)]">{t('deploy.deployrbac.sef9548')}</p>
) : (
<div className="space-y-1.5">
{assignments.map((a) => {
@@ -332,7 +335,7 @@ export default function DeployRBAC() {
</div>
)
) : (
<p className="text-xs text-[var(--dg-text-muted)] flex items-center gap-1"><Users className="w-3.5 h-3.5" />选择用户后查看已分配权限</p>
<p className="text-xs text-[var(--dg-text-muted)] flex items-center gap-1"><Users className="w-3.5 h-3.5" />{t('deploy.deployrbac.s9be00c')}</p>
)}
</CardContent>
</Card>
@@ -367,7 +370,7 @@ export default function DeployRBAC() {
</Badge>
))}
{previewRoleId !== null && previewMatches.length === 0 && (
<span className="text-xs text-[var(--dg-text-muted)]">无匹配 — 请调整正则</span>
<span className="text-xs text-[var(--dg-text-muted)]">{t('deploy.deployrbac.s64096f')}</span>
)}
</div>
</CardContent>