fix(k8s): 修复 shared-components.tsx JSX 结构错误致前端构建失败 + 补全残余 i18n 硬编码中文

- fix: shared-components.tsx ContainerInfo 端口区块多余 <div> 标签致 JSX 闭合错误 (TS17008)
- i18n: EventsPage/DeploymentDetail/DeploymentEditAdvanced/Containers/Storage 等 K8s 组件残余硬编码中文→t() 调用
- i18n: zh/en 7987 键完全对齐, 新增 k8s.eventsPage/k8s.workload 等语义化键
- tsc -b=0, npm build✓ (17.48s), ESLint errors=0/warnings=43/any=0, go build=0
- 17/17 集成测试通过, Docker 部署 healthy
This commit is contained in:
attacker committed 2026-07-14 20:07:46 +08:00
1 parent 39a4432f79
commit bd1ccefa7f
13 files changed
+172 -42

No files matched your search

@@ -176,7 +176,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
<Tabs defaultValue="pods">
<TabsList className="bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)]">
<TabsTrigger value="pods" className="data-[state=active]:bg-[var(--dg-bg-active)]">
Pod 列表 ({pods.length})
{i18n.t('k8s.deploymentDetail.podList')} ({pods.length})
</TabsTrigger>
<TabsTrigger value="logs" className="data-[state=active]:bg-[var(--dg-bg-active)]">
<FileText className="h-4 w-4 mr-1" />{i18n.t("common.podLogs")}
@@ -281,7 +281,7 @@ export function DeploymentEditAdvanced({ clusterCode, namespace, deploymentName,
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={onCancel}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button className="bg-blue-600 hover:bg-blue-700" disabled={saving} onClick={save}>
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : <Save className="h-4 w-4 mr-1" />}
保存
{i18n.t('common.save')}
</Button>
</div>
</div>
@@ -202,7 +202,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={onCancel}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button className="bg-blue-600 hover:bg-blue-700" disabled={saving} onClick={save}>
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : <Save className="h-4 w-4 mr-1" />}
保存
{i18n.t('common.save')}
</Button>
</div>
</div>
@@ -343,7 +343,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={onCancel}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button className="bg-blue-600 hover:bg-blue-700" disabled={saving} onClick={save}>
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : <Save className="h-4 w-4 mr-1" />}
保存
{i18n.t('common.save')}
</Button>
</div>
</div>
@@ -165,12 +165,12 @@ export default function EventsPage() {
<span className="text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.eventsPage.filterLabel')} </span>
{selectedReason && (
<button type="button" className="inline-flex items-center gap-1 rounded border border-[var(--dg-border)] px-2 py-0.5 text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-tertiary)]" onClick={() => setSelectedReason('')}>
原因 {selectedReason} <X className="h-3 w-3" />
{i18n.t('k8s.eventsPage.reasonLabel')} {selectedReason} <X className="h-3 w-3" />
</button>
)}
{selectedKind && (
<button type="button" className="inline-flex items-center gap-1 rounded border border-[var(--dg-border)] px-2 py-0.5 text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-tertiary)]" onClick={() => setSelectedKind('')}>
类型 {selectedKind} <X className="h-3 w-3" />
{i18n.t('k8s.eventsPage.kindLabel')} {selectedKind} <X className="h-3 w-3" />
</button>
)}
</div>
@@ -240,7 +240,7 @@ export default function EventsPage() {
<CardContent className="p-4">
<h3 className="text-sm font-medium text-[var(--dg-text-primary)] mb-3 flex items-center gap-1.5">
<AlertTriangle className="h-4 w-4 text-[var(--dg-orange)]" />
最新告警 ({summary.recent_warnings.length})
{i18n.t('k8s.eventsPage.recentWarnings')} ({summary.recent_warnings.length})
</h3>
<div className="space-y-1">
{summary.recent_warnings.map((w: JsonData, i: number) => (
@@ -164,7 +164,7 @@ export function KubernetesClusterDialog({
/>
{nameError && <p className="text-xs dg-text-red">{nameError}</p>}
<p className="text-xs text-[var(--dg-text-muted)]">
只允许英文、数字和连字符(-),首尾不能是连字符。名称将作为集群唯一标识。
{i18n.t('k8s.clusterDialog.codeRule')}
</p>
</div>
<div className="space-y-2">
@@ -243,7 +243,7 @@ export function KubernetesClusterDialog({
/>
{!hasKubeConfig && (
<p className="text-xs text-[var(--dg-text-muted)]">
粘贴 kubeconfig 内容后将自动解析 API Server 地址
{i18n.t('k8s.clusterDialog.kubeconfigHint')}
</p>
)}
</div>
@@ -255,7 +255,7 @@ export function KubernetesClusterDialog({
onClick={() => { onOpenChange(false); }}
className="border-[var(--dg-border)] text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)] hover:bg-[var(--dg-bg-hover)]"
>
取消
{i18n.t('common.cancel')}
</Button>
<Button
onClick={handleSubmit}
@@ -132,7 +132,7 @@ export function KubernetesNodes() {
<TableCell>{getRoleBadge(n.role)}</TableCell>
<TableCell>{getStatusBadge(n.status)}</TableCell>
<TableCell className="text-sm text-[var(--dg-text-secondary)]">
{n.cpu ? `${n.cpu}核` : '-'} / {n.memory ? `${Math.round(n.memory / 1024)}GB` : '-'}
{n.cpu ? `${n.cpu}${i18n.t('k8s.nodes.cores')}` : '-'} / {n.memory ? `${Math.round(n.memory / 1024)}GB` : '-'}
</TableCell>
<TableCell>
{(() => {
@@ -158,7 +158,7 @@ export default function NamespaceManagementPage() {
onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
/>
<p className="text-xs text-[var(--dg-text-muted)]">
名称需符合 DNS-1123 标准(小写字母/数字/连字符)
{i18n.t('k8s.nsMgmt.dnsHint')}
</p>
</div>
</div>
@@ -166,7 +166,7 @@ export default function NamespaceManagementPage() {
<Button variant="outline" onClick={() => setCreateOpen(false)}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button onClick={handleCreate} disabled={creating || !newName.trim()}>
{creating ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : null}
创建
{i18n.t('common.create')}
</Button>
</DialogFooter>
</DialogContent>
@@ -280,7 +280,7 @@ export default function NamespaceManagementPage() {
{i18n.t('common.deleteNamespaceWarn')} <span className="font-bold">{deleteTarget}</span> {i18n.t('common.allResources')}
</p>
<p className="text-xs text-[var(--dg-red)]/70 mt-1">
此操作不可逆,所有 Deployment/Pod/Service/ConfigMap 等资源将被永久删除。
{i18n.t('k8s.nsMgmt.confirmDeleteDesc')}
</p>
</div>
</div>
@@ -288,7 +288,7 @@ export default function NamespaceManagementPage() {
<Button variant="outline" onClick={() => setDeleteTarget(null)}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button variant="destructive" onClick={handleDelete} disabled={deleting}>
{deleting ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : null}
确认删除
{i18n.t('k8s.nsMgmt.confirmDelete')}
</Button>
</DialogFooter>
</DialogContent>
@@ -119,7 +119,7 @@ export function PodFileBrowser({
const text = await kubernetesApi.readPodFileText(clusterCode, namespace, podName, filePath, container || undefined);
setPreview({ file: fileName, content: text });
} catch (e) {
setPreview({ file: fileName, content: `读取失败: ${(e as JsonData).message}` });
setPreview({ file: fileName, content: `${i18n.t('k8s.podFiles.readError')}: ${(e as JsonData).message}` });
}
};
@@ -137,7 +137,7 @@ export function PodFileBrowser({
await kubernetesApi.uploadPodFile(clusterCode, namespace, podName, currentPath, file, container || undefined);
loadFiles(currentPath);
} catch (err) {
setError(`上传失败: ${(err as JsonData).message}`);
setError(`${i18n.t('k8s.podFiles.uploadError')}: ${(err as JsonData).message}`);
} finally {
setUploading(false);
if (fileInputRef.current) fileInputRef.current.value = '';
@@ -211,7 +211,7 @@ export function PodFileBrowser({
<div className="flex items-center gap-2">
<input type="file" ref={fileInputRef} className="hidden" onChange={handleUpload} />
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-xs" onClick={() => fileInputRef.current?.click()} disabled={uploading}>
{uploading ? <Loader2 className="h-3 w-3 animate-spin mr-1" /> : <Upload className="h-3 w-3 mr-1" />}上传
{uploading ? <Loader2 className="h-3 w-3 animate-spin mr-1" /> : <Upload className="h-3 w-3 mr-1" />}{i18n.t('common.upload')}
</Button>
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-xs" onClick={() => loadFiles(currentPath)} disabled={loading}>
{loading ? <Loader2 className="h-3 w-3 animate-spin" /> : <RefreshCw className="h-3 w-3" />}
@@ -97,6 +97,6 @@ export async function saveDeploymentBundleYaml(
await kubernetesApi.applyYAML(clusterCode, docYaml);
continue;
}
throw new Error(`不支持的资源类型: ${manifest.kind}(仅支持 Deployment / Service)`);
throw new Error(i18n.t('k8s.bundleYaml.unsupportedType', { kind: manifest.kind }));
}
}
@@ -57,7 +57,7 @@ export function ContainerInfo({ c }: { c: JsonData }) {
内存: <span className="text-[var(--dg-text-primary)] font-mono">{c.resources?.requests?.memory || '-'}/{c.resources?.limits?.memory || '-'}</span>
</div>
<div className="text-[var(--dg-text-secondary)]">
端口: <span className="text-[var(--dg-text-primary)] font-mono">
{i18n.t('k8s.workload.portsLabel')}: <span className="text-[var(--dg-text-primary)] font-mono">
{(c.ports || []).map((p: JsonData) => p.containerPort || p.port).join(', ') || '-'}
</span>
</div>