feat(i18n): 安全批量转换 135 文件 — 覆盖率 34%→62%
使用 i18n.t() 全局方式(避免 hook 定位问题): - 288 目标文件中 135 个成功转换 - 2228 处中文替换为 i18n.t() 调用 - 1006 个新翻译 key 添加到 zh-CN.json + en-US.json - 词典总量: 3554 keys (22 namespaces) - tsc 零错误 + Vite 构建成功 - 6 个含代码示例/数据字符串的文件安全回退
This commit is contained in:
1 parent
72bfb0e12d
commit
55f216cfbb
131 files changed
+4049
-1908
No files matched your search
@@ -25,6 +25,7 @@ import { useToast } from '@/hooks/use-toast';
|
||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||
import { PageShell } from '@/components/layout/PageShell';
|
||||
import { ContentPanel } from '@/components/layout/ContentPanel';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
type ResourceType =
|
||||
| 'deployments' | 'pods' | 'services' | 'ingresses'
|
||||
@@ -43,8 +44,8 @@ const BULK_EXPORT_TYPES: ResourceType[] = ['services', 'ingresses', 'configmaps'
|
||||
|
||||
const VisualActions = ({ name, opts }: { name: string; opts: { onYaml?: (n: string) => void; onEdit?: (n: string) => void } }) => (
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-[var(--dg-green)] hover:text-[var(--dg-green)]" title="可视化编辑"
|
||||
onClick={() => opts.onEdit?.(name)}><Pencil className="h-3.5 w-3.5 mr-1" />编辑</Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-[var(--dg-green)] hover:text-[var(--dg-green)]" title={i18n.t('k8s.resource.visualEditor')}
|
||||
onClick={() => opts.onEdit?.(name)}><Pencil className="h-3.5 w-3.5 mr-1" /> {i18n.t('common.edit')} </Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-secondary)]" title="YAML"
|
||||
onClick={() => opts.onYaml?.(name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
|
||||
</div>
|
||||
@@ -58,8 +59,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
renderRow: ( item: any, opts: { clusterCode: string; namespace: string; onDetail?: ( item: any) => void; onYaml?: (name: string) => void; onEdit?: (name: string) => void; onTerminal?: ( item: any) => void; onFileBrowser?: ( item: any) => void; onDeletePod?: ( item: any) => void; onPodDetail?: ( item: any) => void }) => React.ReactNode;
|
||||
}> = {
|
||||
deployments: {
|
||||
title: 'Deployments', subtitle: 'Deployment 工作负载运行状态', icon: Boxes,
|
||||
columns: ['名称', '命名空间', '副本', '镜像'],
|
||||
title: 'Deployments', subtitle: i18n.t('kubernetes.namespaceresour.s35c744fb'), icon: Boxes,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.capacitypagetsx.s0428b36a'), i18n.t('kubernetes.container.image')],
|
||||
renderRow: (d, opts) => (
|
||||
<TableRow key={d.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30 cursor-pointer" onClick={() => opts.onDetail?.(d)}>
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{d.name}</TableCell>
|
||||
@@ -83,8 +84,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
pods: {
|
||||
title: 'Pods', subtitle: 'Pod 实例运行状态', icon: Container,
|
||||
columns: ['名称', '命名空间', '状态', '节点', 'CPU', '内存', '重启', '镜像', '操作'],
|
||||
title: 'Pods', subtitle: i18n.t('kubernetes.namespaceresour.s780b7dd3'), icon: Container,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('system.terminalStatus'), i18n.t('kubernetes.node.title'), 'CPU', i18n.t('kubernetes.pod.memory'), i18n.t('agent.agentServiceRestart'), i18n.t('kubernetes.container.image'), i18n.t('system.auditAction')],
|
||||
renderRow: (p, opts) => {
|
||||
const statusMap: Record<string, string> = { Running: 'bg-[var(--dg-green)]/20 text-[var(--dg-green)] border-[var(--dg-green)]/30', Pending: 'bg-[var(--dg-orange)]/20 text-[var(--dg-orange)] border-[var(--dg-orange)]/30', Failed: 'bg-[var(--dg-red)]/20 text-[var(--dg-red)] border-[var(--dg-red)]/30' };
|
||||
return (
|
||||
@@ -104,13 +105,13 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell className="text-xs text-[var(--dg-text-muted)] max-w-[200px] truncate">{p.image || '-'}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-green)]" title="终端"
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-green)]" title={i18n.t('kubernetes.pod.terminal')}
|
||||
onClick={() => (opts as Record<string, any>).onTerminal?.(p)}><Terminal className="h-3.5 w-3.5" /></Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-orange)]" title="文件"
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-orange)]" title={i18n.t('k8s.pods.action.file')}
|
||||
onClick={() => (opts as Record<string, any>).onFileBrowser?.(p)}><HardDrive className="h-3.5 w-3.5" /></Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-blue)]" title="YAML"
|
||||
onClick={() => opts.onYaml?.(p.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-red)] hover:text-[var(--dg-red)]" title="删除"
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0 text-[var(--dg-red)] hover:text-[var(--dg-red)]" title={i18n.t('kubernetes.storage.delete2')}
|
||||
onClick={() => opts.onDeletePod?.(p)}><Trash2 className="h-3.5 w-3.5" /></Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -119,8 +120,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
},
|
||||
},
|
||||
services: {
|
||||
title: 'Services', subtitle: 'Service 网络服务', icon: Network,
|
||||
columns: ['名称', '命名空间', '类型', 'Cluster IP', '端口', '操作'],
|
||||
title: 'Services', subtitle: i18n.t('kubernetes.namespaceresour.s5e76b605'), icon: Network,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('system.credentialType'), 'Cluster IP', i18n.t('system.ldapPort'), i18n.t('system.auditAction')],
|
||||
renderRow: (s, opts) => (
|
||||
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{s.name}</TableCell>
|
||||
@@ -133,8 +134,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
ingresses: {
|
||||
title: 'Ingresses', subtitle: 'Ingress 路由规则', icon: Network,
|
||||
columns: ['名称', '命名空间', 'Hosts', 'TLS', '操作'],
|
||||
title: 'Ingresses', subtitle: i18n.t('kubernetes.namespaceresour.s8d501473'), icon: Network,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), 'Hosts', 'TLS', i18n.t('system.auditAction')],
|
||||
renderRow: (i, opts) => {
|
||||
const hosts = i.hosts || [];
|
||||
return (
|
||||
@@ -149,8 +150,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
},
|
||||
},
|
||||
configmaps: {
|
||||
title: 'ConfigMaps', subtitle: '配置字典', icon: FileText,
|
||||
columns: ['名称', '命名空间', '键数量', '操作'],
|
||||
title: 'ConfigMaps', subtitle: i18n.t('kubernetes.namespaceovervi.s0986a6e7'), icon: FileText,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.namespaceresour.sbd1fa02a'), i18n.t('system.auditAction')],
|
||||
renderRow: (c, opts) => (
|
||||
<TableRow key={c.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{c.name}</TableCell>
|
||||
@@ -161,8 +162,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
secrets: {
|
||||
title: 'Secrets', subtitle: '密钥管理(仅名称与类型)', icon: Shield,
|
||||
columns: ['名称', '命名空间', '类型', '操作'],
|
||||
title: 'Secrets', subtitle: i18n.t('kubernetes.namespaceresour.sd1424af7'), icon: Shield,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('system.credentialType'), i18n.t('system.auditAction')],
|
||||
renderRow: (s, opts) => (
|
||||
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{s.name}</TableCell>
|
||||
@@ -173,8 +174,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
hpas: {
|
||||
title: 'HPAs', subtitle: '水平自动伸缩', icon: Activity,
|
||||
columns: ['名称', '命名空间', '目标', '最小/最大', 'CPU%', 'Mem%'],
|
||||
title: 'HPAs', subtitle: i18n.t('kubernetes.namespaceresour.s9abc8d19'), icon: Activity,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('monitor.alertEvents.col.target'), i18n.t('k8s.hpaCronHpa.col.minMax'), 'CPU%', 'Mem%'],
|
||||
renderRow: (h, _opts) => (
|
||||
<TableRow key={h.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{h.name}</TableCell>
|
||||
@@ -187,21 +188,21 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
cronjobs: {
|
||||
title: 'CronJobs', subtitle: '定时任务', icon: Clock,
|
||||
columns: ['名称', '命名空间', '调度', '状态', '上次执行'],
|
||||
title: 'CronJobs', subtitle: i18n.t('automation.cronJobs'), icon: Clock,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.workload.scheduling'), i18n.t('system.terminalStatus'), i18n.t('automation.scriptLastExec')],
|
||||
renderRow: (cj) => (
|
||||
<TableRow key={cj.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{cj.name}</TableCell>
|
||||
<TableCell><Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{cj.namespace}</Badge></TableCell>
|
||||
<TableCell className="text-sm font-mono text-[var(--dg-text-secondary)]">{cj.schedule || '-'}</TableCell>
|
||||
<TableCell><Badge className={cj.suspend ? 'bg-[var(--dg-orange)]/20 text-[var(--dg-orange)]' : 'bg-[var(--dg-green)]/20 text-[var(--dg-green)]'}>{cj.suspend ? '已暂停' : '运行中'}</Badge></TableCell>
|
||||
<TableCell><Badge className={cj.suspend ? 'bg-[var(--dg-orange)]/20 text-[var(--dg-orange)]' : 'bg-[var(--dg-green)]/20 text-[var(--dg-green)]'}>{cj.suspend ? i18n.t('kubernetes.deployment.paused') : i18n.t('agent.agentServiceRunning')}</Badge></TableCell>
|
||||
<TableCell className="text-sm text-[var(--dg-text-secondary)]">{cj.last_schedule || '-'}</TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
pvcs: {
|
||||
title: '存储卷声明', subtitle: 'PersistentVolumeClaim', icon: Boxes,
|
||||
columns: ['名称', '命名空间', '状态', '大小', 'StorageClass', '操作'],
|
||||
title: i18n.t('kubernetes.namespaceresour.s18c90062'), subtitle: 'PersistentVolumeClaim', icon: Boxes,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('system.terminalStatus'), i18n.t('kubernetes.storage.pvcSize'), 'StorageClass', i18n.t('system.auditAction')],
|
||||
renderRow: (p, opts) => (
|
||||
<TableRow key={p.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{p.name}</TableCell>
|
||||
@@ -214,8 +215,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
statefulsets: {
|
||||
title: 'StatefulSets', subtitle: '有状态应用', icon: Boxes,
|
||||
columns: ['名称', '命名空间', '副本', '就绪', '镜像'],
|
||||
title: 'StatefulSets', subtitle: i18n.t('kubernetes.namespaceresour.sbfeb44d8'), icon: Boxes,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.capacitypagetsx.s0428b36a'), i18n.t('kubernetes.pod.ready'), i18n.t('kubernetes.container.image')],
|
||||
renderRow: (s, opts) => (
|
||||
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30 cursor-pointer" onClick={() => opts.onDetail?.(s)}>
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{s.name}</TableCell>
|
||||
@@ -227,8 +228,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
daemonsets: {
|
||||
title: 'DaemonSets', subtitle: '守护进程集', icon: Boxes,
|
||||
columns: ['名称', '命名空间', '期望', '当前', '就绪', '镜像'],
|
||||
title: 'DaemonSets', subtitle: i18n.t('kubernetes.namespaceresour.se1c074e2'), icon: Boxes,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('monitor.siteProtocol.http.expectedPrefix'), i18n.t('cloud.current'), i18n.t('kubernetes.pod.ready'), i18n.t('kubernetes.container.image')],
|
||||
renderRow: (d, opts) => (
|
||||
<TableRow key={d.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30 cursor-pointer" onClick={() => opts.onDetail?.(d)}>
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{d.name}</TableCell>
|
||||
@@ -241,8 +242,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
),
|
||||
},
|
||||
serviceaccounts: {
|
||||
title: 'ServiceAccounts', subtitle: '服务账号(RBAC 身份标识)', icon: Shield,
|
||||
columns: ['名称', '命名空间', 'Secret数', '镜像拉取凭证', '创建时间', '操作'],
|
||||
title: 'ServiceAccounts', subtitle: i18n.t('kubernetes.namespaceresour.s645c9a3d'), icon: Shield,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.namespaceresour.sbaf7ed70'), i18n.t('kubernetes.namespaceresour.s260c11ec'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
||||
renderRow: (sa, opts) => (
|
||||
<TableRow key={sa.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{sa.name}</TableCell>
|
||||
@@ -253,15 +254,15 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
</TableCell>
|
||||
<TableCell className="text-xs text-[var(--dg-text-muted)]">{sa.age || '-'}</TableCell>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title="查看YAML"
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title={i18n.t('kubernetes.namespaceresour.s4bde086b')}
|
||||
onClick={() => opts.onYaml?.(sa.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
roles: {
|
||||
title: 'Roles', subtitle: '命名空间级角色权限(RBAC)', icon: Shield,
|
||||
columns: ['名称', '命名空间', '规则数', '权限范围', '创建时间', '操作'],
|
||||
title: 'Roles', subtitle: i18n.t('kubernetes.namespaceresour.s8905a680'), icon: Shield,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.namespaceresour.secde032d'), i18n.t('system.apiKeyScope'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
||||
renderRow: (r, opts) => {
|
||||
const verbs = r.verbs || [];
|
||||
const resources = r.resources || [];
|
||||
@@ -281,9 +282,9 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell className="text-xs text-[var(--dg-text-muted)]">{r.age || '-'}</TableCell>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title="查看YAML"
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title={i18n.t('kubernetes.namespaceresour.s4bde086b')}
|
||||
onClick={() => opts.onYaml?.(r.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-green)] hover:text-[var(--dg-green)]" title="编辑"
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-green)] hover:text-[var(--dg-green)]" title={i18n.t('common.edit')}
|
||||
onClick={() => opts.onEdit?.(r.name)}><Pencil className="h-3.5 w-3.5" /></Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -292,8 +293,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
},
|
||||
},
|
||||
rolebindings: {
|
||||
title: 'RoleBindings', subtitle: '角色绑定关系(RBAC 授权)', icon: Shield,
|
||||
columns: ['名称', '命名空间', '角色引用', '绑定主体', '创建时间', '操作'],
|
||||
title: 'RoleBindings', subtitle: i18n.t('kubernetes.namespaceresour.sca10fe5c'), icon: Shield,
|
||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('kubernetes.namespaceresour.s227e5e28'), i18n.t('kubernetes.namespaceresour.s2667c7d8'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
||||
renderRow: (rb, opts) => {
|
||||
const subjects = rb.subjects || [];
|
||||
const subjStr = subjects.length > 3 ? subjects.slice(0, 3).join(', ') + ` +${subjects.length - 3}` : subjects.join(', ');
|
||||
@@ -312,9 +313,9 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell className="text-xs text-[var(--dg-text-muted)]">{rb.age || '-'}</TableCell>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title="查看YAML"
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title={i18n.t('kubernetes.namespaceresour.s4bde086b')}
|
||||
onClick={() => opts.onYaml?.(rb.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-green)] hover:text-[var(--dg-green)]" title="编辑"
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-green)] hover:text-[var(--dg-green)]" title={i18n.t('common.edit')}
|
||||
onClick={() => opts.onEdit?.(rb.name)}><Pencil className="h-3.5 w-3.5" /></Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -439,26 +440,26 @@ export default function NamespaceResourcePage() {
|
||||
if (!clusterCode || !namespace) return;
|
||||
try {
|
||||
await kubernetesApi.exportNamespaceResources(clusterCode, namespace);
|
||||
toast({ title: '导出完成', description: `${namespace} 资源 YAML 已开始下载` });
|
||||
toast({ title: i18n.t('common.exportComplete'), description: `${namespace} 资源 YAML 已开始下载` });
|
||||
} catch (e) {
|
||||
toast({ title: '导出失败', description: (e as Error)?.message || String(e), variant: 'destructive' });
|
||||
toast({ title: i18n.t('kubernetes.msg.exportFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
|
||||
}
|
||||
};
|
||||
|
||||
// Pod 删除 // Logan.Li
|
||||
const handleDeletePod = async (p: any) => {
|
||||
const ok = await confirm({
|
||||
title: '删除 Pod',
|
||||
title: i18n.t('kubernetes.pod.deletePod'),
|
||||
description: '确认删除 Pod "' + (p.name || '') + '"?操作不可恢复。',
|
||||
confirmText: '删除',
|
||||
confirmText: i18n.t('kubernetes.storage.delete2'),
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
await kubernetesApi.deletePod(clusterCode || '', namespace || '', p.name);
|
||||
toast({ title: 'Pod 已删除' });
|
||||
toast({ title: i18n.t('kubernetes.msg.podDeleted') });
|
||||
loadData();
|
||||
} catch (e) {
|
||||
toast({ title: '删除失败', description: (e as any)?.message || String(e), variant: 'destructive' });
|
||||
toast({ title: i18n.t('kubernetes.msg.deleteFailed2'), description: (e as any)?.message || String(e), variant: 'destructive' });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -467,7 +468,7 @@ export default function NamespaceResourcePage() {
|
||||
const searchToolbar = (
|
||||
<div className="relative w-56 flex-1 min-w-[200px] max-w-xs">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 dg-text-muted" />
|
||||
<Input placeholder="搜索..." value={search} onChange={(e) => setSearch(e.target.value)} className="pl-9 h-9 dg-input" />
|
||||
<Input placeholder={i18n.t('kubernetes.namespaceresour.s64e327ed')} value={search} onChange={(e) => setSearch(e.target.value)} className="pl-9 h-9 dg-input" />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -482,11 +483,11 @@ export default function NamespaceResourcePage() {
|
||||
actions={(
|
||||
<>
|
||||
<Select value={clusterCode || ''} onValueChange={(v) => navigate(`/kubernetes/${v}/namespace/${namespace || 'default'}/${type}`)}>
|
||||
<SelectTrigger className="w-48 h-9 dg-input"><SelectValue placeholder="选择集群" /></SelectTrigger>
|
||||
<SelectTrigger className="w-48 h-9 dg-input"><SelectValue placeholder={i18n.t('kubernetes.topology.selectCluster')} /></SelectTrigger>
|
||||
<SelectContent>{clusters.map((c) => <SelectItem key={c.id} value={c.code || String(c.id)}>{c.name}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
<Select value={namespace || ''} onValueChange={(v) => navigate(`/kubernetes/${clusterCode}/namespace/${v}/${type}`)}>
|
||||
<SelectTrigger className="w-40 h-9 dg-input"><SelectValue placeholder="命名空间" /></SelectTrigger>
|
||||
<SelectTrigger className="w-40 h-9 dg-input"><SelectValue placeholder={i18n.t('kubernetes.namespace.title')} /></SelectTrigger>
|
||||
<SelectContent className="max-h-[300px]">{namespaces.map((ns) => <SelectItem key={ns} value={ns}>{ns}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadData} disabled={loading}>
|
||||
@@ -495,16 +496,16 @@ export default function NamespaceResourcePage() {
|
||||
</Button>
|
||||
{BULK_EXPORT_TYPES.includes(type) && (
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportCSV} title="导出CSV">
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportCSV} title={i18n.t('kubernetes.namespaceresour.sef28d57c')}>
|
||||
<Download className="h-3 w-3 mr-1" />CSV
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportJSON} title="导出JSON">
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportJSON} title={i18n.t('kubernetes.namespaceresour.se11b48c7')}>
|
||||
<Download className="h-3 w-3 mr-1" />JSON
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{EXPORT_YAML_TYPES.includes(type) && (
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportYaml} title="导出命名空间 YAML(tar.gz)">
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportYaml} title={i18n.t('kubernetes.namespaceresour.s2f89924c')}>
|
||||
<Download className="h-3 w-3 mr-1" />导出YAML
|
||||
</Button>
|
||||
)}
|
||||
@@ -556,7 +557,7 @@ export default function NamespaceResourcePage() {
|
||||
<TableHeader><TableRow className="border-[var(--dg-border)] hover:bg-transparent">{config.columns.map((col) => <TableHead key={col} className="text-[var(--dg-text-secondary)]">{col}</TableHead>)}</TableRow></TableHeader>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow><TableCell colSpan={config.columns.length} className="text-center text-[var(--dg-text-muted)] py-12"><Loader2 className="h-5 w-5 animate-spin mx-auto mb-2" />加载中...</TableCell></TableRow>
|
||||
<TableRow><TableCell colSpan={config.columns.length} className="text-center text-[var(--dg-text-muted)] py-12"><Loader2 className="h-5 w-5 animate-spin mx-auto mb-2" /> {i18n.t('monitor.logPanel.logResults.loading')} </TableCell></TableRow>
|
||||
) : filtered.length === 0 ? (
|
||||
<TableRow><TableCell colSpan={config.columns.length} className="text-center text-[var(--dg-text-muted)] py-12"><config.icon className="h-8 w-8 mx-auto mb-2 opacity-50" />暂无 {config.title}</TableCell></TableRow>
|
||||
) : (
|
||||
@@ -598,7 +599,7 @@ export default function NamespaceResourcePage() {
|
||||
<div className="p-5 space-y-5">
|
||||
{/* 基本信息 */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-[var(--dg-text-primary)] mb-3">基本信息</h4>
|
||||
<h4 className="text-sm font-medium text-[var(--dg-text-primary)] mb-3"> {i18n.t('k8s.editMode.basic')} </h4>
|
||||
{(() => {
|
||||
const p = podDetail;
|
||||
const labels = p.labels || p.metadata?.labels || {};
|
||||
@@ -609,23 +610,23 @@ export default function NamespaceResourcePage() {
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-x-6 gap-y-2.5">
|
||||
<div>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">名称</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.secret.secretName')} </span>
|
||||
<p className="text-sm text-[var(--dg-text-primary)] font-mono">{p.name || '-'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">命名空间</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.namespace.title')} </span>
|
||||
<p className="text-sm text-[var(--dg-text-primary)]">{p.namespace || '-'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">状态</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('system.terminalStatus')} </span>
|
||||
<div><Badge className={statusMap[st] || 'bg-[var(--dg-text-muted)]/20 text-[var(--dg-text-secondary)]'}>{st}</Badge></div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">创建时间</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('system.userCreatedAt')} </span>
|
||||
<p className="text-sm text-[var(--dg-text-primary)]">{created ? new Date(created).toLocaleString('zh-CN') : '-'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">节点</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.node.title')} </span>
|
||||
<p className="text-sm text-[var(--dg-text-primary)]">{p.node_name || p.node || '-'}</p>
|
||||
</div>
|
||||
<div>
|
||||
@@ -633,7 +634,7 @@ export default function NamespaceResourcePage() {
|
||||
<p className="text-sm text-[var(--dg-text-primary)] font-mono">{p.ip || p.pod_ip || '-'}</p>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">标签</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.namespace.labels')} </span>
|
||||
<div className="flex flex-wrap gap-1 mt-0.5">
|
||||
{Object.keys(labels).length === 0 ? <span className="text-xs text-[var(--dg-text-muted)]">-</span>
|
||||
: Object.entries(labels).map(([k, v]) => (
|
||||
@@ -643,7 +644,7 @@ export default function NamespaceResourcePage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">注解</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.namespace.annotations')} </span>
|
||||
<div className="flex flex-wrap gap-1 mt-0.5">
|
||||
{Object.keys(annotations).length === 0 ? <span className="text-xs text-[var(--dg-text-muted)]">-</span>
|
||||
: Object.entries(annotations).slice(0, 6).map(([k, v]) => (
|
||||
@@ -659,19 +660,19 @@ export default function NamespaceResourcePage() {
|
||||
|
||||
{/* 现状详情(Conditions) */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-[var(--dg-text-primary)] mb-3">现状详情</h4>
|
||||
<h4 className="text-sm font-medium text-[var(--dg-text-primary)] mb-3"> {i18n.t('kubernetes.namespaceresour.s1d8d6e2e')} </h4>
|
||||
{(() => {
|
||||
const cond: any[] = podDetail.conditions || podDetail.pod_conditions || [];
|
||||
if (cond.length === 0) return <p className="text-xs text-[var(--dg-text-muted)] py-2">暂无数据</p>;
|
||||
if (cond.length === 0) return <p className="text-xs text-[var(--dg-text-muted)] py-2"> {i18n.t('aiops.assistantNoData')} </p>;
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-[var(--dg-border)] hover:bg-transparent">
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs">类型</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs">状态</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs">更新时间</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs">原因</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs">消息</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs"> {i18n.t('system.credentialType')} </TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs"> {i18n.t('system.terminalStatus')} </TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs"> {i18n.t('automation.scriptUpdatedAt')} </TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs"> {i18n.t('kubernetes.event.eventReason')} </TableHead>
|
||||
<TableHead className="text-[var(--dg-text-muted)] text-xs"> {i18n.t('kubernetes.event.eventMessage')} </TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -702,7 +703,7 @@ export default function NamespaceResourcePage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end px-5 py-3 border-t border-[var(--dg-border)]">
|
||||
<Button variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]" onClick={() => setPodDetail(null)}>关闭</Button>
|
||||
<Button variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]" onClick={() => setPodDetail(null)}> {i18n.t('agent.portClosed')} </Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user