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:
1 parent
78c27037e3
commit
ecec73989b
166 files changed
+6925
-2800
No files matched your search
@@ -22,11 +22,13 @@ import { PodFileBrowser } from './PodFileBrowser';
|
||||
import { formatK8sTime, statusColor, STATUS_COLORS } from './k8s-yaml-utils';
|
||||
import { PageShell } from '@/components/layout/PageShell';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type WorkloadKind = 'StatefulSet' | 'DaemonSet';
|
||||
type ViewMode = 'view' | 'yaml';
|
||||
|
||||
function StatCard({ label, value, sub, color }: { label: string; value: React.ReactNode; sub?: string; color?: string }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-3">
|
||||
@@ -40,8 +42,9 @@ function StatCard({ label, value, sub, color }: { label: string; value: React.Re
|
||||
|
||||
// Helper: render key-value pairs as badges
|
||||
function LabelBadges({ labels }: { labels: Record<string, string> | undefined }) {
|
||||
const { t } = useTranslation();
|
||||
const entries = Object.entries(labels || {});
|
||||
if (entries.length === 0) return <span className="text-xs text-[var(--dg-text-muted)]">无</span>;
|
||||
if (entries.length === 0) return <span className="text-xs text-[var(--dg-text-muted)]">{t('common.none')}</span>;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{entries.map(([k, v]) => (
|
||||
@@ -54,6 +57,7 @@ function LabelBadges({ labels }: { labels: Record<string, string> | undefined })
|
||||
}
|
||||
|
||||
export default function WorkloadViewPage() {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams<{ clusterCode: string; namespace: string; name: string; mode?: string }>();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -119,9 +123,9 @@ export default function WorkloadViewPage() {
|
||||
|
||||
const handleRestart = async () => {
|
||||
const ok = await confirm({
|
||||
title: '滚动重启',
|
||||
description: `确认重启 ${kind} ${name}?将触发滚动重启。`,
|
||||
confirmText: '重启',
|
||||
title: t('kubernetes.deployment.restart'),
|
||||
description: t('kubernetes.workloadviewpage.s6139f5', { kind: kind, name: name }),
|
||||
confirmText: t('common.reboot'),
|
||||
});
|
||||
if (!ok) return;
|
||||
setActionLoading(true);
|
||||
@@ -131,10 +135,10 @@ export default function WorkloadViewPage() {
|
||||
} else {
|
||||
await kubernetesApi.restartDaemonSet(clusterCode, namespace, name);
|
||||
}
|
||||
toast({ title: '重启指令已下发', description: `${kind} 滚动重启已触发` });
|
||||
toast({ title: t('kubernetes.workloadviewpage.s68a700'), description: t('kubernetes.workloadviewpage.sb0614d', { kind: kind }) });
|
||||
setTimeout(loadData, 1000);
|
||||
} catch (e) {
|
||||
toast({ title: '重启失败', description: e?.message || String(e), variant: 'destructive' });
|
||||
toast({ title: t('kubernetes.deployment.restartFailed'), description: e?.message || String(e), variant: 'destructive' });
|
||||
} finally { setActionLoading(false); }
|
||||
};
|
||||
|
||||
@@ -142,11 +146,11 @@ export default function WorkloadViewPage() {
|
||||
setActionLoading(true);
|
||||
try {
|
||||
await kubernetesApi.scaleStatefulSet(clusterCode, namespace, name, scaleValue);
|
||||
toast({ title: '扩缩容成功', description: `StatefulSet 副本数已更新为 ${scaleValue}` });
|
||||
toast({ title: t('kubernetes.workloadviewpage.s0b3ea1'), description: t('kubernetes.workloadviewpage.s27c3cf', { scaleValue: scaleValue }) });
|
||||
setScaleModal(false);
|
||||
setTimeout(loadData, 1000);
|
||||
} catch (e) {
|
||||
toast({ title: '扩缩容失败', description: e?.message || String(e), variant: 'destructive' });
|
||||
toast({ title: t('kubernetes.workloadviewpage.s0b329f'), description: e?.message || String(e), variant: 'destructive' });
|
||||
} finally { setActionLoading(false); }
|
||||
};
|
||||
|
||||
@@ -190,7 +194,7 @@ export default function WorkloadViewPage() {
|
||||
|
||||
const modeTabs = (
|
||||
<div className="flex items-center gap-1 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-lg p-0.5">
|
||||
<button className={`px-3 py-1 text-xs rounded transition-colors ${mode === 'view' ? 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)]' : 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'}`} onClick={() => handleModeChange('view')}>查看</button>
|
||||
<button className={`px-3 py-1 text-xs rounded transition-colors ${mode === 'view' ? 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)]' : 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'}`} onClick={() => handleModeChange('view')}>{t('common.view')}</button>
|
||||
<button className={`px-3 py-1 text-xs rounded transition-colors ${mode === 'yaml' ? 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)]' : 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'}`} onClick={() => handleModeChange('yaml')}>YAML</button>
|
||||
</div>
|
||||
);
|
||||
@@ -202,7 +206,7 @@ export default function WorkloadViewPage() {
|
||||
subtitle={`${clusterCode} / ${namespace} · ${kind}`}
|
||||
icon={<Boxes className="h-5 w-5" />}
|
||||
loading={loading}
|
||||
loadingLabel="加载工作负载..."
|
||||
loadingLabel=t('kubernetes.workloadviewpage.s4eb429')
|
||||
actions={(
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => navigate(-1)}>
|
||||
@@ -215,7 +219,7 @@ export default function WorkloadViewPage() {
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" disabled={actionLoading} onClick={handleRestart}>
|
||||
<RotateCcw className="h-3.5 w-3.5 mr-1" />{actionLoading ? '重启中...' : '重启'}
|
||||
<RotateCcw className="h-3.5 w-3.5 mr-1" />{actionLoading ? t('kubernetes.workloadviewpage.s9ac104') : t('common.reboot')}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => setYamlModal(true)}>
|
||||
<FileCode className="h-3.5 w-3.5 mr-1" />YAML
|
||||
@@ -227,8 +231,8 @@ export default function WorkloadViewPage() {
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{kind}</Badge>
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{namespace}</Badge>
|
||||
{isHealthy
|
||||
? <Badge className="bg-[var(--dg-green)]/20 text-[var(--dg-green)] border-[var(--dg-green)]/30">健康</Badge>
|
||||
: <Badge className="bg-[var(--dg-orange)]/20 text-[var(--dg-orange)] border-[var(--dg-orange)]/30">更新中</Badge>}
|
||||
? <Badge className="bg-[var(--dg-green)]/20 text-[var(--dg-green)] border-[var(--dg-green)]/30">{t('common.healthy')}</Badge>
|
||||
: <Badge className="bg-[var(--dg-orange)]/20 text-[var(--dg-orange)] border-[var(--dg-orange)]/30">{t('kubernetes.workloadviewpage.sf339d4')}</Badge>}
|
||||
</div>
|
||||
|
||||
{!loading && (mode === 'yaml' ? (
|
||||
@@ -251,21 +255,21 @@ export default function WorkloadViewPage() {
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-3">
|
||||
{isSts ? (
|
||||
<>
|
||||
<StatCard label="副本就绪" value={`${readyReplicas}/${replicas}`} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} sub={isHealthy ? '全部就绪' : '部分未就绪'} />
|
||||
<StatCard label="当前副本" value={currentReplicas} color={STATUS_COLORS.blue} />
|
||||
<StatCard label="已更新副本" value={updatedReplicas} color={STATUS_COLORS.green} />
|
||||
<StatCard label="服务名" value={spec.serviceName || '-'} color="var(--dg-text-secondary)" />
|
||||
<StatCard label="Pod 数量" value={pods.length} color="var(--dg-text-secondary)" />
|
||||
<StatCard label="创建时间" value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
|
||||
<StatCard label=t('kubernetes.workloadviewpage.sea6396') value={`${readyReplicas}/${replicas}`} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} sub={isHealthy ? t('kubernetes.workloadviewpage.s23c944') : t('kubernetes.workloadviewpage.sac1002')} />
|
||||
<StatCard label=t('kubernetes.hpa.currentReplicas') value={currentReplicas} color={STATUS_COLORS.blue} />
|
||||
<StatCard label=t('kubernetes.deployment.updatedReplicas') value={updatedReplicas} color={STATUS_COLORS.green} />
|
||||
<StatCard label=t('kubernetes.workloadviewpage.sf592ef') value={spec.serviceName || '-'} color="var(--dg-text-secondary)" />
|
||||
<StatCard label=t('kubernetes.workloadviewpage.s55edf2') value={pods.length} color="var(--dg-text-secondary)" />
|
||||
<StatCard label=t('common.createdAt') value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<StatCard label="期望调度" value={desiredScheduled} color={STATUS_COLORS.blue} />
|
||||
<StatCard label="当前调度" value={currentScheduled} color="var(--dg-text-secondary)" />
|
||||
<StatCard label="就绪数量" value={numberReady} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} />
|
||||
<StatCard label="可用数量" value={numberAvailable} color={STATUS_COLORS.green} />
|
||||
<StatCard label="已更新" value={numberUpdated} color="var(--dg-text-secondary)" />
|
||||
<StatCard label="创建时间" value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
|
||||
<StatCard label=t('kubernetes.workloadviewpage.sd78bc8') value={desiredScheduled} color={STATUS_COLORS.blue} />
|
||||
<StatCard label=t('kubernetes.workloadviewpage.sb76164') value={currentScheduled} color="var(--dg-text-secondary)" />
|
||||
<StatCard label=t('kubernetes.daemonset.numberReady') value={numberReady} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} />
|
||||
<StatCard label=t('kubernetes.daemonset.numberAvailable') value={numberAvailable} color={STATUS_COLORS.green} />
|
||||
<StatCard label=t('deploy.toast.updated') value={numberUpdated} color="var(--dg-text-secondary)" />
|
||||
<StatCard label=t('common.createdAt') value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -280,18 +284,18 @@ export default function WorkloadViewPage() {
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0 space-y-2 text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">策略类型</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.s0511a2')}</span>
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{strategyType}</Badge>
|
||||
</div>
|
||||
{strategyType === 'RollingUpdate' && isSts && updateStrategy.rollingUpdate && (
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">分区 (partition)</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.s45cb9d')}</span>
|
||||
<span className="text-[var(--dg-text-secondary)] font-mono">{updateStrategy.rollingUpdate.partition ?? 0}</span>
|
||||
</div>
|
||||
{updateStrategy.rollingUpdate.maxUnavailable && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">最大不可用</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.daemonset.maxUnavailable')}</span>
|
||||
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxUnavailable === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxUnavailable) : updateStrategy.rollingUpdate.maxUnavailable}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -301,13 +305,13 @@ export default function WorkloadViewPage() {
|
||||
<>
|
||||
{updateStrategy.rollingUpdate.maxUnavailable && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">最大不可用</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.daemonset.maxUnavailable')}</span>
|
||||
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxUnavailable === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxUnavailable) : updateStrategy.rollingUpdate.maxUnavailable}</span>
|
||||
</div>
|
||||
)}
|
||||
{updateStrategy.rollingUpdate.maxSurge && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">最大 surge</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.sfed4ce')}</span>
|
||||
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxSurge === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxSurge) : updateStrategy.rollingUpdate.maxSurge}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -315,12 +319,12 @@ export default function WorkloadViewPage() {
|
||||
)}
|
||||
{isSts && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">Pod 管理策略</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.statefulset.podManagementPolicy')}</span>
|
||||
<span className="text-[var(--dg-text-secondary)] font-mono">{spec.podManagementPolicy || 'OrderedReady'}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between">
|
||||
<span className="text-[var(--dg-text-muted)]">修订历史限制</span>
|
||||
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.s450a4e')}</span>
|
||||
<span className="text-[var(--dg-text-secondary)] font-mono">{spec.revisionHistoryLimit ?? 10}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -369,10 +373,10 @@ export default function WorkloadViewPage() {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-[var(--dg-border)] hover:bg-transparent">
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">名称</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">访问模式</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">存储类</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">大小</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.name')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('k8s.resource.accessMode')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('k8s.resource.storageClass')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('deploy.table.size')}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -414,7 +418,7 @@ export default function WorkloadViewPage() {
|
||||
</div>
|
||||
)}
|
||||
{containers.length === 0 ? (
|
||||
<p className="text-xs text-[var(--dg-text-muted)] py-3">暂无容器数据,请查看 YAML</p>
|
||||
<p className="text-xs text-[var(--dg-text-muted)] py-3">{t('kubernetes.workloadviewpage.se4b610')}</p>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<p className="text-[11px] text-[var(--dg-text-muted)] mb-1 uppercase tracking-wide">Containers</p>
|
||||
@@ -437,17 +441,17 @@ export default function WorkloadViewPage() {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-[var(--dg-border)] hover:bg-transparent">
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">Pod 名称</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">状态</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">所在节点</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('kubernetes.pod.podName')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.status')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('kubernetes.pod.nodeName')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">Pod IP</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">重启</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">操作</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.reboot')}</TableHead>
|
||||
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.actions')}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{pods.length === 0 ? (
|
||||
<TableRow><TableCell colSpan={6} className="text-center text-[var(--dg-text-muted)] py-8">暂无 Pod</TableCell></TableRow>
|
||||
<TableRow><TableCell colSpan={6} className="text-center text-[var(--dg-text-muted)] py-8">{t('k8s.deploymentDetail.noPod')}</TableCell></TableRow>
|
||||
) : pods.map((pod: JsonData) => (
|
||||
<TableRow key={pod.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||
<TableCell className="font-medium text-[var(--dg-text-primary)]">{pod.name}</TableCell>
|
||||
@@ -484,7 +488,7 @@ export default function WorkloadViewPage() {
|
||||
{scaleModal && (
|
||||
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center" onClick={() => setScaleModal(false)}>
|
||||
<div className="bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-lg p-6 w-80" onClick={e => e.stopPropagation()}>
|
||||
<h3 className="text-sm font-semibold text-[var(--dg-text-primary)] mb-4">伸缩 StatefulSet</h3>
|
||||
<h3 className="text-sm font-semibold text-[var(--dg-text-primary)] mb-4">{t('kubernetes.workloadviewpage.s7f541e')}</h3>
|
||||
<div className="flex items-center justify-center gap-4 mb-4">
|
||||
<Button variant="outline" size="icon" className="h-9 w-9" onClick={() => setScaleValue(Math.max(0, scaleValue - 1))}>
|
||||
<Minus className="h-4 w-4" />
|
||||
@@ -495,9 +499,9 @@ export default function WorkloadViewPage() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" className="flex-1 border-[var(--dg-border)] text-[var(--dg-text-secondary)]" onClick={() => setScaleModal(false)}>取消</Button>
|
||||
<Button variant="outline" size="sm" className="flex-1 border-[var(--dg-border)] text-[var(--dg-text-secondary)]" onClick={() => setScaleModal(false)}>{t('common.cancel')}</Button>
|
||||
<Button size="sm" className="flex-1 bg-[var(--dg-blue)] hover:opacity-90" disabled={actionLoading} onClick={handleScale}>
|
||||
{actionLoading ? '执行中...' : '确认'}
|
||||
{actionLoading ? t('pipeline.executing') : t('common.confirm')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -544,6 +548,7 @@ export default function WorkloadViewPage() {
|
||||
|
||||
// Container info sub-component
|
||||
function ContainerInfo({ c }: { c: JsonData}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="border border-[var(--dg-border)] rounded-lg p-3 bg-[var(--dg-bg-primary)]/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
|
||||
Reference in new issue
Block a user