refactor(i18n): U26 Kubernetes 模块 phantom key 大规模清理 — 32 文件 171 phantom key → 8 残留(均为误匹配), 163 个替换为 k8s.* 语义化键, 新增 19 子命名空间 + 扩展 8 个已有, zh/en 6990→7185 +195 键对齐, tsc -b=0, npm build✓ (17.59s), ESLint=0/53/0, go build=0, 17/17 测试通过
This commit is contained in:
1 parent
c6a50216dd
commit
7f5295cfc2
31 files changed
+657
-190
No files matched your search
@@ -516,6 +516,7 @@ services/api.ts (1324L)
|
|||||||
| U20 | ESLint static-components 清零 + CredDetailDialog 内联组件修复 + i18n 批量迁移损坏修复(CredDetailDialog.tsx `Field` 内联组件提取为模块级 `CredField` — 23 处 react-hooks/static-components 警告清零;修复 8 处被批量正则损坏的 `i18n.t()` 调用(`i18n.t('>名称:</span>')` 等 → 语义化键);6 处硬编码中文清零;新增 `deploy.cred.sensitiveWarn` 键;ESLint warnings 66→43 (-35%),tsc -b=0,npm build✓ (17.29s),17/17 测试通过) | P3 | **done** |
|
| U20 | ESLint static-components 清零 + CredDetailDialog 内联组件修复 + i18n 批量迁移损坏修复(CredDetailDialog.tsx `Field` 内联组件提取为模块级 `CredField` — 23 处 react-hooks/static-components 警告清零;修复 8 处被批量正则损坏的 `i18n.t()` 调用(`i18n.t('>名称:</span>')` 等 → 语义化键);6 处硬编码中文清零;新增 `deploy.cred.sensitiveWarn` 键;ESLint warnings 66→43 (-35%),tsc -b=0,npm build✓ (17.29s),17/17 测试通过) | P3 | **done** |
|
||||||
| U23 | Task 模块 i18n 治理(CronJobManager + CronJobFormDialog + ScriptManager + RemoteExecPage 4 文件 — `import i18n` 全局调用 → `useTranslation()` hook;42 处 phantom key (`i18n.t('Chinese text')` → 语义化 `task.cronJob.*`/`task.scriptList.*`/`task.remoteExec.*` 键);28 个孤儿 hash 键清理 (cronjobformdial.s*/remoteexecpaget.s*/systemtaskspanel.s*);50+ 处硬编码 JSX 中文 → t() 调用;zh/en 6825→6886 +61 键对齐,tsc -b=0,npm build✓ (17.46s),ESLint=0/45/0,go build=0,17/17 测试通过) | P3 | **done** |
|
| U23 | Task 模块 i18n 治理(CronJobManager + CronJobFormDialog + ScriptManager + RemoteExecPage 4 文件 — `import i18n` 全局调用 → `useTranslation()` hook;42 处 phantom key (`i18n.t('Chinese text')` → 语义化 `task.cronJob.*`/`task.scriptList.*`/`task.remoteExec.*` 键);28 个孤儿 hash 键清理 (cronjobformdial.s*/remoteexecpaget.s*/systemtaskspanel.s*);50+ 处硬编码 JSX 中文 → t() 调用;zh/en 6825→6886 +61 键对齐,tsc -b=0,npm build✓ (17.46s),ESLint=0/45/0,go build=0,17/17 测试通过) | P3 | **done** |
|
||||||
| U24 | Monitor 模块 i18n hook 迁移(26 个 .tsx 文件从 `import i18n` 全局 → `useTranslation()` hook + 89 个 phantom 中文文本键 → `monitor.lbl.*` 语义化键;299 处 `i18n.t()` 调用 → 74 处保留(模块级/.ts 工具文件) + 225 处迁移到 `t()` hook;1 个 .ts 工具文件 helpers.ts 中文文本键替换为语义化键;zh/en 6886→6975 +89 键对齐,tsc -b=0,npm build✓ (17.16s),ESLint=0/53/0,go build=0,Docker healthy) | P3 | **done** |
|
| U24 | Monitor 模块 i18n hook 迁移(26 个 .tsx 文件从 `import i18n` 全局 → `useTranslation()` hook + 89 个 phantom 中文文本键 → `monitor.lbl.*` 语义化键;299 处 `i18n.t()` 调用 → 74 处保留(模块级/.ts 工具文件) + 225 处迁移到 `t()` hook;1 个 .ts 工具文件 helpers.ts 中文文本键替换为语义化键;zh/en 6886→6975 +89 键对齐,tsc -b=0,npm build✓ (17.16s),ESLint=0/53/0,go build=0,Docker healthy) | P3 | **done** |
|
||||||
|
| U26 | Kubernetes 模块 phantom key 大规模清理(32 个 .tsx/.ts 文件,171 个中文 phantom key → 8 个残留(均为 ANSI 转义码/JSX 表达式误匹配),163 个成功替换为 k8s.* 语义化键;新增 19 个子命名空间(nsResource/nsOverview/deploymentWorkload/deploymentView/workloadView/controlBar/clusterOverview/nodes/podLogs/podExec/pvc/configMap/secret/service/ingress/nsMgmt/editContainers/capacityPage/podFile)+ 扩展 8 个已有子命名空间;zh/en 6990→7185 +195 键对齐,k8s 命名空间 65 子命名空间/940 键,tsc -b=0,npm build✓ (17.59s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default function ClusterOverviewPage() {
|
|||||||
const current = list.find((c: JsonData) => (c.code || String(c.id)) === clusterCode);
|
const current = list.find((c: JsonData) => (c.code || String(c.id)) === clusterCode);
|
||||||
setClusterInfo(current || null);
|
setClusterInfo(current || null);
|
||||||
if (!current && list.length > 0 && clusterCode) {
|
if (!current && list.length > 0 && clusterCode) {
|
||||||
toast({ title: i18n.t('集群未找到'), description: `找不到集群 ${clusterCode}`, variant: 'destructive' });
|
toast({ title: i18n.t('k8s.clusterOverview.notFound'), description: `找不到集群 ${clusterCode}`, variant: 'destructive' });
|
||||||
}
|
}
|
||||||
} catch { setClusters([]); }
|
} catch { setClusters([]); }
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -85,8 +85,8 @@ export default function ClusterOverviewPage() {
|
|||||||
return (
|
return (
|
||||||
<PageShell
|
<PageShell
|
||||||
variant="default"
|
variant="default"
|
||||||
title={clusterCode || i18n.t('集群概览')}
|
title={clusterCode || i18n.t('k8s.clusterOverview.title')}
|
||||||
subtitle={clusterInfo?.version ? `集群概览 · Kubernetes ${clusterInfo.version}` : i18n.t('集群概览')}
|
subtitle={clusterInfo?.version ? `集群概览 · Kubernetes ${clusterInfo.version}` : i18n.t('k8s.clusterOverview.title')}
|
||||||
icon={<Server className="h-5 w-5" />}
|
icon={<Server className="h-5 w-5" />}
|
||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
<StatStrip
|
<StatStrip
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
|||||||
setPods(allPods.filter((p: JsonData) => p.namespace === namespace && p.name.startsWith(deploymentName)));
|
setPods(allPods.filter((p: JsonData) => p.namespace === namespace && p.name.startsWith(deploymentName)));
|
||||||
setHpas(allHpas.filter((h: JsonData) => h.target_name === deploymentName && h.namespace === namespace));
|
setHpas(allHpas.filter((h: JsonData) => h.target_name === deploymentName && h.namespace === namespace));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(i18n.t('加载详情失败:'), e);
|
console.error(i18n.t('k8s.deploymentWorkload.loadFailed'), e);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
|||||||
setActionLoading('restart');
|
setActionLoading('restart');
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.restartDeployment(clusterCode, namespace, deploymentName);
|
await kubernetesApi.restartDeployment(clusterCode, namespace, deploymentName);
|
||||||
toast({ title: i18n.t('重启指令已下发'), description: i18n.t('Deployment 正在滚动重启') });
|
toast({ title: i18n.t('k8s.workloadView.restartSent'), description: i18n.t('k8s.deploymentWorkload.restarting') });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.deployment.restartFailed'), description: e?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.deployment.restartFailed'), description: e?.message || String(e), variant: 'destructive' });
|
||||||
} finally { setActionLoading(''); }
|
} finally { setActionLoading(''); }
|
||||||
@@ -97,7 +97,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
|||||||
setActionLoading('image');
|
setActionLoading('image');
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.updateDeploymentImage(clusterCode, namespace, deploymentName, newImage);
|
await kubernetesApi.updateDeploymentImage(clusterCode, namespace, deploymentName, newImage);
|
||||||
toast({ title: i18n.t('镜像更新成功'), description: `正在滚动更新为 ${newImage}` });
|
toast({ title: i18n.t('k8s.deploymentDetail.imageUpdated'), description: `正在滚动更新为 ${newImage}` });
|
||||||
setShowImageUpdate(false);
|
setShowImageUpdate(false);
|
||||||
loadData();
|
loadData();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -115,7 +115,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
|||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.deleteHPA(clusterCode, namespace, hpa.name);
|
await kubernetesApi.deleteHPA(clusterCode, namespace, hpa.name);
|
||||||
toast({ title: i18n.t('HPA 已删除') });
|
toast({ title: i18n.t('k8s.deploymentDetail.hpaDeleted') });
|
||||||
loadData();
|
loadData();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.deleteFailed2'), description: e?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.msg.deleteFailed2'), description: e?.message || String(e), variant: 'destructive' });
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
|
|||||||
? imagePullSecrets.filter(s => s.trim()).map((s: string) => ({ name: s }))
|
? imagePullSecrets.filter(s => s.trim()).map((s: string) => ({ name: s }))
|
||||||
: undefined;
|
: undefined;
|
||||||
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
|
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
|
||||||
toast({ title: i18n.t('容器配置已保存'), description: `${deploymentName} 更新已提交` });
|
toast({ title: i18n.t('k8s.editContainers.saved'), description: `${deploymentName} 更新已提交` });
|
||||||
onSaved(base);
|
onSaved(base);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e?.message || String(e), variant: 'destructive' });
|
||||||
@@ -152,7 +152,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
|
|||||||
{imagePullSecrets.length === 0 && <span className="text-[11px] dg-text-muted">无</span>}
|
{imagePullSecrets.length === 0 && <span className="text-[11px] dg-text-muted">无</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input placeholder={i18n.t('输入 Secret 名称后回车')} className="dg-input text-xs h-8"
|
<Input placeholder={i18n.t('k8s.editContainers.secretPlaceholder')} className="dg-input text-xs h-8"
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
const val = (e.target as HTMLInputElement).value.trim();
|
const val = (e.target as HTMLInputElement).value.trim();
|
||||||
@@ -170,7 +170,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
|
|||||||
{/* 初始化容器 */}
|
{/* 初始化容器 */}
|
||||||
<ContainerGroupSection
|
<ContainerGroupSection
|
||||||
title={i18n.t('kubernetes.container.initContainers')}
|
title={i18n.t('kubernetes.container.initContainers')}
|
||||||
description={i18n.t('在主容器启动前按顺序执行的容器,用于初始化环境')}
|
description={i18n.t('k8s.editContainers.initDesc')}
|
||||||
icon={<Play className="h-4 w-4 dg-text-purple" />}
|
icon={<Play className="h-4 w-4 dg-text-purple" />}
|
||||||
containers={initContainers}
|
containers={initContainers}
|
||||||
accentColor="purple"
|
accentColor="purple"
|
||||||
@@ -181,8 +181,8 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
|
|||||||
|
|
||||||
{/* 工作容器 */}
|
{/* 工作容器 */}
|
||||||
<ContainerGroupSection
|
<ContainerGroupSection
|
||||||
title={i18n.t('初始化容器')}
|
title={i18n.t('k8s.editContainers.initContainers')}
|
||||||
description={i18n.t('在主容器启动前按顺序执行的容器,用于初始化环境')}
|
description={i18n.t('k8s.editContainers.initDesc')}
|
||||||
icon={<Container className="h-4 w-4 dg-text-blue" />}
|
icon={<Container className="h-4 w-4 dg-text-blue" />}
|
||||||
containers={containers}
|
containers={containers}
|
||||||
accentColor="blue"
|
accentColor="blue"
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
|
|||||||
try {
|
try {
|
||||||
const yaml = await dumpManifest(svcManifest);
|
const yaml = await dumpManifest(svcManifest);
|
||||||
await kubernetesApi.applyYAML(clusterCode, yaml);
|
await kubernetesApi.applyYAML(clusterCode, yaml);
|
||||||
toast({ title: i18n.t('Service 已创建'), description: name });
|
toast({ title: i18n.t('k8s.editService.serviceCreated'), description: name });
|
||||||
setShowCreate(null);
|
setShowCreate(null);
|
||||||
load();
|
load();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -209,7 +209,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
|
|||||||
try {
|
try {
|
||||||
const yaml = await dumpManifest(ingManifest);
|
const yaml = await dumpManifest(ingManifest);
|
||||||
await kubernetesApi.applyYAML(clusterCode, yaml);
|
await kubernetesApi.applyYAML(clusterCode, yaml);
|
||||||
toast({ title: i18n.t('Service 已创建'), description: name });
|
toast({ title: i18n.t('k8s.editService.serviceCreated'), description: name });
|
||||||
setShowCreate(null);
|
setShowCreate(null);
|
||||||
load();
|
load();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -221,7 +221,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
|
|||||||
// 删除资源
|
// 删除资源
|
||||||
const deleteResource = async (kind: 'service' | 'ingress', name: string) => {
|
const deleteResource = async (kind: 'service' | 'ingress', name: string) => {
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: i18n.t('删除资源'),
|
title: i18n.t('common.delete'),
|
||||||
description: `确认删除 ${kind === 'service' ? 'Service' : 'Ingress'} 「${name}」?此操作不可撤销。`,
|
description: `确认删除 ${kind === 'service' ? 'Service' : 'Ingress'} 「${name}」?此操作不可撤销。`,
|
||||||
confirmText: i18n.t('kubernetes.storage.delete2'),
|
confirmText: i18n.t('kubernetes.storage.delete2'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
|
|||||||
@@ -40,12 +40,12 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const VOLUME_TYPES: { key: VolumeForm['type']; label: string; icon: typeof HardDrive; color: string }[] = [
|
const VOLUME_TYPES: { key: VolumeForm['type']; label: string; icon: typeof HardDrive; color: string }[] = [
|
||||||
{ key: 'persistentVolumeClaim', label: i18n.t('存储卷声明'), icon: Database, color: 'dg-text-green' },
|
{ key: 'persistentVolumeClaim', label: i18n.t('k8s.nsResource.pvcDesc'), icon: Database, color: 'dg-text-green' },
|
||||||
{ key: 'emptyDir', label: i18n.t('临时目录(EmptyDir)'), icon: Disc, color: 'dg-text-secondary' },
|
{ key: 'emptyDir', label: i18n.t('k8s.editStorage.emptyDir'), icon: Disc, color: 'dg-text-secondary' },
|
||||||
{ key: 'hostPath', label: 'HostPath', icon: HardDrive, color: 'dg-text-yellow' },
|
{ key: 'hostPath', label: 'HostPath', icon: HardDrive, color: 'dg-text-yellow' },
|
||||||
{ key: 'nfs', label: 'NFS', icon: Server, color: 'dg-text-purple' },
|
{ key: 'nfs', label: 'NFS', icon: Server, color: 'dg-text-purple' },
|
||||||
{ key: 'configMap', label: i18n.t('配置字典'), icon: File, color: 'dg-text-blue' },
|
{ key: 'configMap', label: i18n.t('k8s.nsResource.configMapDesc'), icon: File, color: 'dg-text-blue' },
|
||||||
{ key: 'secret', label: i18n.t('密文'), icon: Lock, color: 'text-rose-400' },
|
{ key: 'secret', label: i18n.t('k8s.secret.data'), icon: Lock, color: 'text-rose-400' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const SELECTABLE_TYPES = new Set(VOLUME_TYPES.map((t) => t.key));
|
const SELECTABLE_TYPES = new Set(VOLUME_TYPES.map((t) => t.key));
|
||||||
@@ -129,20 +129,20 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
|||||||
const save = async () => {
|
const save = async () => {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (!entry.volume.name.trim()) {
|
if (!entry.volume.name.trim()) {
|
||||||
toast({ title: i18n.t('请填写数据卷名称'), variant: 'destructive' });
|
toast({ title: i18n.t('k8s.editStorage.volumeNameRequired'), variant: 'destructive' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!formToVolume(entry.volume)) {
|
if (!formToVolume(entry.volume)) {
|
||||||
const hint = entry.volume.type === 'configMap' ? i18n.t('请选择 ConfigMap')
|
const hint = entry.volume.type === 'configMap' ? i18n.t('k8s.editStorage.selectConfigMap')
|
||||||
: entry.volume.type === 'secret' ? i18n.t('请选择 Secret')
|
: entry.volume.type === 'secret' ? i18n.t('k8s.editStorage.selectSecret')
|
||||||
: entry.volume.type === 'persistentVolumeClaim' ? i18n.t('请选择 PVC')
|
: entry.volume.type === 'persistentVolumeClaim' ? i18n.t('k8s.editStorage.selectPVC')
|
||||||
: i18n.t('的类型必填项');
|
: i18n.t('k8s.editStorage.typeRequiredHint');
|
||||||
toast({ title: i18n.t('数据卷配置不完整'), description: `请为「${entry.volume.name}」${hint}`, variant: 'destructive' });
|
toast({ title: i18n.t('k8s.editStorage.incompleteConfig'), description: `请为「${entry.volume.name}」${hint}`, variant: 'destructive' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const validMounts = entry.mounts.filter((m) => m.mountPath.trim());
|
const validMounts = entry.mounts.filter((m) => m.mountPath.trim());
|
||||||
if (validMounts.length === 0) {
|
if (validMounts.length === 0) {
|
||||||
toast({ title: i18n.t('请填写挂载路径'), description: `数据卷「${entry.volume.name}」需至少挂载到一个容器`, variant: 'destructive' });
|
toast({ title: i18n.t('k8s.editStorage.mountPathRequired'), description: `数据卷「${entry.volume.name}」需至少挂载到一个容器`, variant: 'destructive' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
|||||||
const { volumes, mounts } = volumeEntriesToStorage(entries);
|
const { volumes, mounts } = volumeEntriesToStorage(entries);
|
||||||
const base = applyStorageToManifest(rawManifest, volumes, mounts, passthroughVolumes);
|
const base = applyStorageToManifest(rawManifest, volumes, mounts, passthroughVolumes);
|
||||||
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
|
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
|
||||||
toast({ title: i18n.t('存储配置已保存'), description: `${deploymentName} 更新已提交` });
|
toast({ title: i18n.t('k8s.editStorage.saved'), description: `${deploymentName} 更新已提交` });
|
||||||
onSaved(base);
|
onSaved(base);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
|
||||||
@@ -234,7 +234,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
|||||||
if (vol.type === 'persistentVolumeClaim' && !vol.pvcName) patch.pvcName = name;
|
if (vol.type === 'persistentVolumeClaim' && !vol.pvcName) patch.pvcName = name;
|
||||||
updateVolume(i, patch);
|
updateVolume(i, patch);
|
||||||
}}
|
}}
|
||||||
placeholder={i18n.t('如 app-logs / fluent-bit-config')}
|
placeholder={i18n.t('k8s.editStorage.namePlaceholder')}
|
||||||
className="h-8 w-44 dg-input text-sm font-mono"
|
className="h-8 w-44 dg-input text-sm font-mono"
|
||||||
/>
|
/>
|
||||||
{/* 原生 select,避免 Radix Select 在 overflow 容器内无法下拉 // Logan.Li */}
|
{/* 原生 select,避免 Radix Select 在 overflow 容器内无法下拉 // Logan.Li */}
|
||||||
@@ -312,7 +312,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
|||||||
<Input
|
<Input
|
||||||
value={mount.mountPath}
|
value={mount.mountPath}
|
||||||
onChange={(e) => updateMount(i, mi, { mountPath: e.target.value })}
|
onChange={(e) => updateMount(i, mi, { mountPath: e.target.value })}
|
||||||
placeholder={i18n.t('挂载路径 /bitcloud/app/.../logs')}
|
placeholder={i18n.t('k8s.editStorage.mountPathPlaceholder')}
|
||||||
className="h-8 flex-1 min-w-[200px] dg-input text-sm font-mono"
|
className="h-8 flex-1 min-w-[200px] dg-input text-sm font-mono"
|
||||||
/>
|
/>
|
||||||
{entry.mounts.length > 1 && (
|
{entry.mounts.length > 1 && (
|
||||||
@@ -326,7 +326,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
|||||||
<Input
|
<Input
|
||||||
value={mount.subPath}
|
value={mount.subPath}
|
||||||
onChange={(e) => updateMount(i, mi, { subPath: e.target.value })}
|
onChange={(e) => updateMount(i, mi, { subPath: e.target.value })}
|
||||||
placeholder={i18n.t('挂载路径 /bitcloud/app/.../logs')}
|
placeholder={i18n.t('k8s.editStorage.mountPathPlaceholder')}
|
||||||
className="h-7 flex-1 max-w-md dg-input text-[var(--dg-text-secondary)] text-xs font-mono"
|
className="h-7 flex-1 max-w-md dg-input text-[var(--dg-text-secondary)] text-xs font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export function DeploymentView(props: DeploymentViewProps) {
|
|||||||
const handleRollback = async (rev: JsonData) => {
|
const handleRollback = async (rev: JsonData) => {
|
||||||
const revision = rev.revision ?? rev.revision_number ?? rev.number;
|
const revision = rev.revision ?? rev.revision_number ?? rev.number;
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: i18n.t('回滚版本'),
|
title: i18n.t('k8s.deploymentView.rollbackVersion'),
|
||||||
description: `确认回滚到修订版本 #${revision}?`,
|
description: `确认回滚到修订版本 #${revision}?`,
|
||||||
confirmText: i18n.t('pipeline.rollback'),
|
confirmText: i18n.t('pipeline.rollback'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
@@ -148,7 +148,7 @@ export function DeploymentView(props: DeploymentViewProps) {
|
|||||||
setRollbackLoading(revision);
|
setRollbackLoading(revision);
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.rollbackDeployment(clusterCode, namespace, deployment.name, Number(revision));
|
await kubernetesApi.rollbackDeployment(clusterCode, namespace, deployment.name, Number(revision));
|
||||||
toast({ title: i18n.t('回滚已下发'), description: `正在回滚到 #${revision}` });
|
toast({ title: i18n.t('k8s.deploymentView.rollbackSent'), description: `正在回滚到 #${revision}` });
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('pipeline.rollbackFailed'), description: e?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('pipeline.rollbackFailed'), description: e?.message || String(e), variant: 'destructive' });
|
||||||
@@ -160,7 +160,7 @@ export function DeploymentView(props: DeploymentViewProps) {
|
|||||||
setScaleLoading(true);
|
setScaleLoading(true);
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.scaleDeployment(clusterCode, namespace, deployment.name, scaleValue);
|
await kubernetesApi.scaleDeployment(clusterCode, namespace, deployment.name, scaleValue);
|
||||||
toast({ title: i18n.t('伸缩已提交'), description: `副本数 → ${scaleValue}` });
|
toast({ title: i18n.t('k8s.deploymentView.scaleSent'), description: `副本数 → ${scaleValue}` });
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.scaleFailed'), description: e?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.msg.scaleFailed'), description: e?.message || String(e), variant: 'destructive' });
|
||||||
@@ -168,10 +168,10 @@ export function DeploymentView(props: DeploymentViewProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
{ key: 'pods', label: i18n.t('容器组'), icon: Container, count: pods.length },
|
{ key: 'pods', label: i18n.t('k8s.deploymentView.pods'), icon: Container, count: pods.length },
|
||||||
{ key: 'services', label: i18n.t('访问方式'), icon: Network, count: services.length },
|
{ key: 'services', label: i18n.t('k8s.deploymentView.access'), icon: Network, count: services.length },
|
||||||
{ key: 'events', label: i18n.t('kubernetes.event.title'), icon: Activity, count: events.length },
|
{ key: 'events', label: i18n.t('kubernetes.event.title'), icon: Activity, count: events.length },
|
||||||
{ key: 'scale', label: i18n.t('容器伸缩'), icon: Plus },
|
{ key: 'scale', label: i18n.t('k8s.deploymentView.scale'), icon: Plus },
|
||||||
{ key: 'history', label: i18n.t('kubernetes.deployment.revisions'), icon: History, count: revisions.length },
|
{ key: 'history', label: i18n.t('kubernetes.deployment.revisions'), icon: History, count: revisions.length },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -186,8 +186,8 @@ export function DeploymentView(props: DeploymentViewProps) {
|
|||||||
{isHealthy ? 'Running' : 'Updating'}
|
{isHealthy ? 'Running' : 'Updating'}
|
||||||
</Badge>
|
</Badge>
|
||||||
} />
|
} />
|
||||||
<InfoItem label={i18n.t('状态')} value={<span className="font-mono">{ready}/{total}</span>} />
|
<InfoItem label={i18n.t('common.status')} value={<span className="font-mono">{ready}/{total}</span>} />
|
||||||
<InfoItem label={i18n.t('状态')} value={deployment?.strategy || 'RollingUpdate'} />
|
<InfoItem label={i18n.t('common.status')} value={deployment?.strategy || 'RollingUpdate'} />
|
||||||
{pods[0] && (
|
{pods[0] && (
|
||||||
<>
|
<>
|
||||||
<InfoItem label={i18n.t('kubernetes.pod.nodeName')} value={
|
<InfoItem label={i18n.t('kubernetes.pod.nodeName')} value={
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
|
|
||||||
setRevisions(toArray(revRes));
|
setRevisions(toArray(revRes));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(i18n.t('加载详情失败:'), e);
|
console.error(i18n.t('k8s.deploymentWorkload.loadFailed'), e);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
const { manifest: parsed } = await parseEditableManifest(yamlText);
|
const { manifest: parsed } = await parseEditableManifest(yamlText);
|
||||||
setManifest(parsed || deployment);
|
setManifest(parsed || deployment);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(i18n.t('加载 manifest 失败(编辑功能降级):'), e);
|
console.warn(i18n.t('k8s.deploymentWorkload.manifestLoadFailed'), e);
|
||||||
setManifest(deployment);
|
setManifest(deployment);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -191,7 +191,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
setActionLoading('restart');
|
setActionLoading('restart');
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.restartDeployment(clusterCode, namespace, deploymentName);
|
await kubernetesApi.restartDeployment(clusterCode, namespace, deploymentName);
|
||||||
toast({ title: i18n.t('重启指令已下发'), description: i18n.t('Deployment 正在滚动重启') });
|
toast({ title: i18n.t('k8s.workloadView.restartSent'), description: i18n.t('k8s.deploymentWorkload.restarting') });
|
||||||
setTimeout(loadData, 1500);
|
setTimeout(loadData, 1500);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.deployment.restartFailed'), description: e?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.deployment.restartFailed'), description: e?.message || String(e), variant: 'destructive' });
|
||||||
@@ -234,7 +234,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
await kubernetesApi.updateDeploymentImage(
|
await kubernetesApi.updateDeploymentImage(
|
||||||
clusterCode, namespace, deploymentName, newImage, cicdContainerName || undefined,
|
clusterCode, namespace, deploymentName, newImage, cicdContainerName || undefined,
|
||||||
);
|
);
|
||||||
toast({ title: i18n.t('扩缩容成功'), description: i18n.t('k8s.deploymentDetail.rollingUpdateImage', { image: newImage }) });
|
toast({ title: i18n.t('k8s.deploymentWorkload.scaleSuccess'), description: i18n.t('k8s.deploymentDetail.rollingUpdateImage', { image: newImage }) });
|
||||||
setModal(null);
|
setModal(null);
|
||||||
loadData();
|
loadData();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -288,7 +288,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
// ============ 渲染 // Logan.Li ============
|
// ============ 渲染 // Logan.Li ============
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<PageShell variant="workspace" title={deploymentName} subtitle={`${clusterCode} / ${namespace}`} loading loadingLabel={i18n.t('加载 Deployment...')} actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" /> {i18n.t('common.back')} </Button>}>
|
<PageShell variant="workspace" title={deploymentName} subtitle={`${clusterCode} / ${namespace}`} loading loadingLabel={i18n.t('k8s.deploymentWorkload.loading')} actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" /> {i18n.t('common.back')} </Button>}>
|
||||||
{null}
|
{null}
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
@@ -308,7 +308,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
if (name) navigate(`/kubernetes/${clusterCode}/namespace/${namespace}/workload/view/Deployment/${name}`);
|
if (name) navigate(`/kubernetes/${clusterCode}/namespace/${namespace}/workload/view/Deployment/${name}`);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<PageShell variant="workspace" title={i18n.t('新建 Deployment')} subtitle={`${clusterCode} / ${namespace}`} actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" /> {i18n.t('common.back')} </Button>}>
|
<PageShell variant="workspace" title={i18n.t('k8s.deploymentWorkload.create')} subtitle={`${clusterCode} / ${namespace}`} actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" /> {i18n.t('common.back')} </Button>}>
|
||||||
<EditModeContainer clusterCode={clusterCode} namespace={namespace} deploymentName={CREATE_DEPLOYMENT_SLUG} manifest={manifest} isCreate onSaved={handleCreated} onCancel={onBack} />
|
<EditModeContainer clusterCode={clusterCode} namespace={namespace} deploymentName={CREATE_DEPLOYMENT_SLUG} manifest={manifest} isCreate onSaved={handleCreated} onCancel={onBack} />
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
@@ -379,7 +379,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
resource="deployments"
|
resource="deployments"
|
||||||
name={deploymentName}
|
name={deploymentName}
|
||||||
inline
|
inline
|
||||||
resourceHint={i18n.t('含关联 Service')}
|
resourceHint={i18n.t('k8s.deploymentWorkload.withService')}
|
||||||
loadYamlFn={loadBundleYaml}
|
loadYamlFn={loadBundleYaml}
|
||||||
saveYamlFn={saveBundleYaml}
|
saveYamlFn={saveBundleYaml}
|
||||||
onSaved={loadData}
|
onSaved={loadData}
|
||||||
@@ -404,7 +404,7 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
|||||||
namespace={namespace}
|
namespace={namespace}
|
||||||
resource="deployments"
|
resource="deployments"
|
||||||
name={deploymentName}
|
name={deploymentName}
|
||||||
resourceHint={i18n.t('含关联 Service')}
|
resourceHint={i18n.t('k8s.deploymentWorkload.withService')}
|
||||||
loadYamlFn={loadBundleYaml}
|
loadYamlFn={loadBundleYaml}
|
||||||
saveYamlFn={saveBundleYaml}
|
saveYamlFn={saveBundleYaml}
|
||||||
onClose={() => setModal(null)}
|
onClose={() => setModal(null)}
|
||||||
|
|||||||
@@ -179,16 +179,16 @@ export default function EventsPage() {
|
|||||||
return (
|
return (
|
||||||
<PageShell
|
<PageShell
|
||||||
variant="list"
|
variant="list"
|
||||||
title={i18n.t('事件中心')}
|
title={i18n.t('k8s.event.center')}
|
||||||
subtitle={clusterName ? `${clusterName} · 集群事件与告警` : i18n.t('集群事件与告警')}
|
subtitle={clusterName ? `${clusterName} · 集群事件与告警` : i18n.t('k8s.event.clusterAlerts')}
|
||||||
icon={<Bell className="h-5 w-5" />}
|
icon={<Bell className="h-5 w-5" />}
|
||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
<StatStrip
|
<StatStrip
|
||||||
items={[
|
items={[
|
||||||
{ label: i18n.t('正常事件'), value: summary?.total ?? events.length, color: 'muted' },
|
{ label: i18n.t('k8s.event.normal'), value: summary?.total ?? events.length, color: 'muted' },
|
||||||
{ label: i18n.t('告警事件'), value: summary?.warning_count ?? warningCount, color: 'amber' },
|
{ label: i18n.t('k8s.event.warning'), value: summary?.warning_count ?? warningCount, color: 'amber' },
|
||||||
{ label: i18n.t('正常事件'), value: summary?.normal_count ?? normalCount, color: 'green' },
|
{ label: i18n.t('k8s.event.normal'), value: summary?.normal_count ?? normalCount, color: 'green' },
|
||||||
{ label: i18n.t('告警类型'), value: reasonData.length, color: 'red' },
|
{ label: i18n.t('k8s.event.warningType'), value: reasonData.length, color: 'red' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
@@ -229,10 +229,10 @@ export default function EventsPage() {
|
|||||||
>
|
>
|
||||||
{reasonData.length > 0 && (
|
{reasonData.length > 0 && (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||||
<RankPanel title={i18n.t('资源类型')} items={reasonData} selected={selectedReason} onSelect={setSelectedReason} />
|
<RankPanel title={i18n.t('k8s.event.resourceType')} items={reasonData} selected={selectedReason} onSelect={setSelectedReason} />
|
||||||
{nsData.length > 0 && <RankPanel title={i18n.t('kubernetes.namespace.title')} items={nsData} />}
|
{nsData.length > 0 && <RankPanel title={i18n.t('kubernetes.namespace.title')} items={nsData} />}
|
||||||
{kindData.length > 0 && (
|
{kindData.length > 0 && (
|
||||||
<RankPanel title={i18n.t('告警原因')} items={kindData} selected={selectedKind} onSelect={setSelectedKind} />
|
<RankPanel title={i18n.t('k8s.event.warningReason')} items={kindData} selected={selectedKind} onSelect={setSelectedKind} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function KubernetesClusterDialog({
|
|||||||
if (!value.trim()) {
|
if (!value.trim()) {
|
||||||
setNameError('');
|
setNameError('');
|
||||||
} else if (!NAME_RE.test(value)) {
|
} else if (!NAME_RE.test(value)) {
|
||||||
setNameError(i18n.t('只允许英文、数字和连字符(-),首尾不能是连字符'));
|
setNameError(i18n.t('k8s.clusterDialog.nameRule'));
|
||||||
} else {
|
} else {
|
||||||
setNameError('');
|
setNameError('');
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ export function KubernetesClusterDialog({
|
|||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!formData.name.trim() || !NAME_RE.test(formData.name.trim())) {
|
if (!formData.name.trim() || !NAME_RE.test(formData.name.trim())) {
|
||||||
toast.error(i18n.t('请输入有效的集群名称(英文/数字/连字符)')); return;
|
toast.error(i18n.t('k8s.clusterDialog.nameInvalid')); return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
@@ -133,7 +133,7 @@ export function KubernetesClusterDialog({
|
|||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
onSaved();
|
onSaved();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(i18n.t('保存集群失败:'), e);
|
console.error(i18n.t('k8s.clusterDialog.saveFailed'), e);
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ export function KubernetesClusterDialog({
|
|||||||
{editingCluster ? i18n.t('kubernetes.cluster.editCluster') : i18n.t('kubernetes.cluster.addCluster')}
|
{editingCluster ? i18n.t('kubernetes.cluster.editCluster') : i18n.t('kubernetes.cluster.addCluster')}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-[var(--dg-text-secondary)]">
|
<DialogDescription className="text-[var(--dg-text-secondary)]">
|
||||||
{editingCluster ? i18n.t('编辑集群') : i18n.t('添加集群')}
|
{editingCluster ? i18n.t('k8s.clusterDialog.editTitle') : i18n.t('k8s.clusterDialog.addTitle')}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4 py-2 max-h-[60vh] overflow-y-auto pr-1">
|
<div className="space-y-4 py-2 max-h-[60vh] overflow-y-auto pr-1">
|
||||||
@@ -159,7 +159,7 @@ export function KubernetesClusterDialog({
|
|||||||
<Input
|
<Input
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) => handleNameChange(e.target.value)}
|
onChange={(e) => handleNameChange(e.target.value)}
|
||||||
placeholder={i18n.t('如: prod-cluster')}
|
placeholder={i18n.t('k8s.clusterDialog.namePlaceholder')}
|
||||||
className={`bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] ${nameError ? 'border-red-500' : ''}`}
|
className={`bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] ${nameError ? 'border-red-500' : ''}`}
|
||||||
/>
|
/>
|
||||||
{nameError && <p className="text-xs dg-text-red">{nameError}</p>}
|
{nameError && <p className="text-xs dg-text-red">{nameError}</p>}
|
||||||
@@ -172,7 +172,7 @@ export function KubernetesClusterDialog({
|
|||||||
<Input
|
<Input
|
||||||
value={formData.version}
|
value={formData.version}
|
||||||
onChange={(e) => setFormData({ ...formData, version: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, version: e.target.value })}
|
||||||
placeholder={i18n.t('如: prod-cluster')}
|
placeholder={i18n.t('k8s.clusterDialog.namePlaceholder')}
|
||||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,7 +183,7 @@ export function KubernetesClusterDialog({
|
|||||||
<Input
|
<Input
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
placeholder={i18n.t('集群用途描述')}
|
placeholder={i18n.t('k8s.clusterDialog.descLabel')}
|
||||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -224,7 +224,7 @@ export function KubernetesClusterDialog({
|
|||||||
<Textarea
|
<Textarea
|
||||||
value={formData.token}
|
value={formData.token}
|
||||||
onChange={(e) => setFormData({ ...formData, token: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, token: e.target.value })}
|
||||||
placeholder={hasToken ? i18n.t('已配置,输入新值覆盖...') : i18n.t('粘贴 Service Account Token...')}
|
placeholder={hasToken ? i18n.t('k8s.clusterDialog.tokenConfigured') : i18n.t('k8s.clusterDialog.tokenPlaceholder')}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] font-mono text-xs"
|
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] font-mono text-xs"
|
||||||
/>
|
/>
|
||||||
@@ -237,7 +237,7 @@ export function KubernetesClusterDialog({
|
|||||||
<Textarea
|
<Textarea
|
||||||
value={formData.kube_config}
|
value={formData.kube_config}
|
||||||
onChange={(e) => handleKubeConfigChange(e.target.value)}
|
onChange={(e) => handleKubeConfigChange(e.target.value)}
|
||||||
placeholder={hasKubeConfig ? i18n.t('已配置,输入新值覆盖...') : 'apiVersion: v1\nkind: Config\nclusters:\n- cluster:\n server: https://10.0.0.1:6443\n ...'}
|
placeholder={hasKubeConfig ? i18n.t('k8s.clusterDialog.tokenConfigured') : 'apiVersion: v1\nkind: Config\nclusters:\n- cluster:\n server: https://10.0.0.1:6443\n ...'}
|
||||||
rows={10}
|
rows={10}
|
||||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] font-mono text-xs"
|
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] font-mono text-xs"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export function KubernetesNodes() {
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="all"> {i18n.t('选择集群')} </SelectItem>
|
<SelectItem value="all"> {i18n.t('k8s.nodes.selectCluster')} </SelectItem>
|
||||||
<SelectItem value="master">Master</SelectItem>
|
<SelectItem value="master">Master</SelectItem>
|
||||||
<SelectItem value="worker">Worker</SelectItem>
|
<SelectItem value="worker">Worker</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
@@ -86,7 +86,7 @@ export function KubernetesNodes() {
|
|||||||
<PageShell
|
<PageShell
|
||||||
variant="list"
|
variant="list"
|
||||||
title={i18n.t('menu.nodeManagement')}
|
title={i18n.t('menu.nodeManagement')}
|
||||||
subtitle={clusterLabel ? `${clusterLabel} · 工作节点状态与资源` : i18n.t('Kubernetes 集群工作节点状态与资源')}
|
subtitle={clusterLabel ? `${clusterLabel} · 工作节点状态与资源` : i18n.t('k8s.nodes.subtitle')}
|
||||||
icon={<Server className="h-5 w-5" />}
|
icon={<Server className="h-5 w-5" />}
|
||||||
actions={(
|
actions={(
|
||||||
<>
|
<>
|
||||||
@@ -125,7 +125,7 @@ export function KubernetesNodes() {
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<TableRow><TableCell colSpan={8} 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>
|
<TableRow><TableCell colSpan={8} 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 ? (
|
) : filtered.length === 0 ? (
|
||||||
<TableRow><TableCell colSpan={8} className="text-center text-[var(--dg-text-muted)] py-12"><Server className="h-8 w-8 mx-auto mb-2 opacity-50" />{selectedCluster ? i18n.t('k8s.empty.nodes') : i18n.t('暂无节点数据')}</TableCell></TableRow>
|
<TableRow><TableCell colSpan={8} className="text-center text-[var(--dg-text-muted)] py-12"><Server className="h-8 w-8 mx-auto mb-2 opacity-50" />{selectedCluster ? i18n.t('k8s.empty.nodes') : i18n.t('k8s.nodes.noData')}</TableCell></TableRow>
|
||||||
) : (
|
) : (
|
||||||
filtered.map((n) => (
|
filtered.map((n) => (
|
||||||
<TableRow key={n.id} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<TableRow key={n.id} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default function NamespaceManagementPage() {
|
|||||||
<Input
|
<Input
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
placeholder={i18n.t('搜索命名空间...')}
|
placeholder={i18n.t('k8s.nsMgmt.searchPlaceholder')}
|
||||||
className="max-w-xs dg-input"
|
className="max-w-xs dg-input"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -120,7 +120,7 @@ export default function NamespaceManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<PageShell
|
<PageShell
|
||||||
variant="list"
|
variant="list"
|
||||||
title={i18n.t('命名空间管理')}
|
title={i18n.t('k8s.nsMgmt.title')}
|
||||||
subtitle={`${clusterCode} · 资源配额与生命周期管理`}
|
subtitle={`${clusterCode} · 资源配额与生命周期管理`}
|
||||||
icon={<Boxes className="h-5 w-5" />}
|
icon={<Boxes className="h-5 w-5" />}
|
||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
@@ -130,7 +130,7 @@ export default function NamespaceManagementPage() {
|
|||||||
{ label: 'Deployments', value: totalDeploys, color: 'blue' },
|
{ label: 'Deployments', value: totalDeploys, color: 'blue' },
|
||||||
{ label: 'Pods', value: totalPods, color: 'purple' },
|
{ label: 'Pods', value: totalPods, color: 'purple' },
|
||||||
{ label: 'Services', value: totalSvcs, color: 'cyan' },
|
{ label: 'Services', value: totalSvcs, color: 'cyan' },
|
||||||
{ label: i18n.t('配额限制'), value: quotaCount, color: 'amber' },
|
{ label: i18n.t('k8s.nsMgmt.quotaLimit'), value: quotaCount, color: 'amber' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
@@ -148,7 +148,7 @@ export default function NamespaceManagementPage() {
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle> {i18n.t('例如: my-app')} </DialogTitle>
|
<DialogTitle> {i18n.t('k8s.nsMgmt.nameExample')} </DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4 py-4">
|
<div className="space-y-4 py-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -156,7 +156,7 @@ export default function NamespaceManagementPage() {
|
|||||||
<Input
|
<Input
|
||||||
value={newName}
|
value={newName}
|
||||||
onChange={(e) => setNewName(e.target.value)}
|
onChange={(e) => setNewName(e.target.value)}
|
||||||
placeholder={i18n.t('例如: my-app')}
|
placeholder={i18n.t('k8s.nsMgmt.nameExample')}
|
||||||
onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
|
onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-[var(--dg-text-muted)]">
|
<p className="text-xs text-[var(--dg-text-muted)]">
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ import type { JsonData } from '@/types/common';
|
|||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
|
||||||
const RESOURCE_LINKS = [
|
const RESOURCE_LINKS = [
|
||||||
{ type: 'deployments', label: 'Deployments', icon: Boxes, desc: i18n.t('工作负载副本管理') },
|
{ type: 'deployments', label: 'Deployments', icon: Boxes, desc: i18n.t('k8s.nsOverview.workloadReplicas') },
|
||||||
{ type: 'pods', label: 'Pods', icon: Container, desc: i18n.t('容器实例运行状态') },
|
{ type: 'pods', label: 'Pods', icon: Container, desc: i18n.t('k8s.nsOverview.podStatus') },
|
||||||
{ type: 'cronjobs', label: 'CronJobs', icon: Clock, desc: i18n.t('定时任务管理') },
|
{ type: 'cronjobs', label: 'CronJobs', icon: Clock, desc: i18n.t('k8s.nsOverview.cronjobManage') },
|
||||||
{ type: 'services', label: 'Services', icon: Network, desc: i18n.t('服务发现与负载均衡') },
|
{ type: 'services', label: 'Services', icon: Network, desc: i18n.t('k8s.nsOverview.serviceDiscovery') },
|
||||||
{ type: 'ingresses', label: 'Ingresses', icon: Network, desc: i18n.t('外部流量路由规则') },
|
{ type: 'ingresses', label: 'Ingresses', icon: Network, desc: i18n.t('k8s.nsOverview.externalRoute') },
|
||||||
{ type: 'configmaps', label: 'ConfigMaps', icon: FileText, desc: i18n.t('配置字典') },
|
{ type: 'configmaps', label: 'ConfigMaps', icon: FileText, desc: i18n.t('k8s.nsResource.configMapDesc') },
|
||||||
{ type: 'secrets', label: 'Secrets', icon: Shield, desc: i18n.t('密钥与凭证') },
|
{ type: 'secrets', label: 'Secrets', icon: Shield, desc: i18n.t('k8s.nsOverview.secrets') },
|
||||||
{ type: 'hpas', label: 'HPAs', icon: Activity, desc: i18n.t('水平自动伸缩策略') },
|
{ type: 'hpas', label: 'HPAs', icon: Activity, desc: i18n.t('k8s.nsOverview.hpaStrategy') },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function NamespaceOverviewPage() {
|
export default function NamespaceOverviewPage() {
|
||||||
@@ -71,7 +71,7 @@ export default function NamespaceOverviewPage() {
|
|||||||
subtitle={`${clusterCode} · 命名空间概览`}
|
subtitle={`${clusterCode} · 命名空间概览`}
|
||||||
icon={<Cloud className="h-5 w-5" />}
|
icon={<Cloud className="h-5 w-5" />}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
loadingLabel={i18n.t('加载资源统计...')}
|
loadingLabel={i18n.t('k8s.nsOverview.loadingStats')}
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
{RESOURCE_LINKS.map((r) => (
|
{RESOURCE_LINKS.map((r) => (
|
||||||
|
|||||||
@@ -204,16 +204,16 @@ export default function NamespaceResourcePage() {
|
|||||||
</Button>
|
</Button>
|
||||||
{BULK_EXPORT_TYPES.includes(type) && (
|
{BULK_EXPORT_TYPES.includes(type) && (
|
||||||
<>
|
<>
|
||||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportCSV} title={i18n.t('导出CSV')}>
|
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportCSV} title={i18n.t('k8s.nsResourcePage.exportCsv')}>
|
||||||
<Download className="h-3 w-3 mr-1" />CSV
|
<Download className="h-3 w-3 mr-1" />CSV
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportJSON} title={i18n.t('导出JSON')}>
|
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportJSON} title={i18n.t('k8s.nsResourcePage.exportJson')}>
|
||||||
<Download className="h-3 w-3 mr-1" />JSON
|
<Download className="h-3 w-3 mr-1" />JSON
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{EXPORT_YAML_TYPES.includes(type) && (
|
{EXPORT_YAML_TYPES.includes(type) && (
|
||||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportYaml} title={i18n.t('导出命名空间 YAML(tar.gz)')}>
|
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportYaml} title={i18n.t('k8s.nsResourcePage.exportYaml')}>
|
||||||
<Download className="h-3 w-3 mr-1" />导出YAML
|
<Download className="h-3 w-3 mr-1" />导出YAML
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export function PodFileBrowser({
|
|||||||
setEntries(list);
|
setEntries(list);
|
||||||
setCurrentPath(path);
|
setCurrentPath(path);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError((e as JsonData).message || i18n.t('加载文件列表失败'));
|
setError((e as JsonData).message || i18n.t('k8s.podFile.loadFailed'));
|
||||||
setEntries([]);
|
setEntries([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export default function PodMetricsPage() {
|
|||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
<StatStrip
|
<StatStrip
|
||||||
items={[
|
items={[
|
||||||
{ label: i18n.t('Pod 数'), value: metrics.length, color: 'muted' },
|
{ label: i18n.t('k8s.clusterOverview.podCount'), value: metrics.length, color: 'muted' },
|
||||||
{ label: i18n.t('k8s.podMetrics.cpu'), value: `${totalCPU}m`, color: 'blue' },
|
{ label: i18n.t('k8s.podMetrics.cpu'), value: `${totalCPU}m`, color: 'blue' },
|
||||||
{ label: i18n.t('k8s.podMetrics.memory'), value: `${totalMem.toFixed(0)}Mi`, color: 'green' },
|
{ label: i18n.t('k8s.podMetrics.memory'), value: `${totalMem.toFixed(0)}Mi`, color: 'green' },
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
setManifest(parsed);
|
setManifest(parsed);
|
||||||
if (!parsed) {
|
if (!parsed) {
|
||||||
setMode('yaml');
|
setMode('yaml');
|
||||||
toast({ title: i18n.t('可视化编辑不可用'), description: i18n.t('无法解析资源结构,已切换到 YAML 编辑模式') });
|
toast({ title: i18n.t('k8s.rve.visualUnavailable'), description: i18n.t('k8s.rve.fallbackToYaml') });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.loadFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.msg.loadFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
||||||
@@ -107,7 +107,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
const yamlStr = await prepareYamlForSave(editYaml);
|
const yamlStr = await prepareYamlForSave(editYaml);
|
||||||
if (isCreate) {
|
if (isCreate) {
|
||||||
await kubernetesApi.applyYAML(clusterCode, yamlStr);
|
await kubernetesApi.applyYAML(clusterCode, yamlStr);
|
||||||
toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: i18n.t('保存失败') });
|
toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: i18n.t('k8s.rve.saveFailed') });
|
||||||
} else {
|
} else {
|
||||||
await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr);
|
await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr);
|
||||||
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: `${resource}/${name} 已更新` });
|
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: `${resource}/${name} 已更新` });
|
||||||
@@ -127,7 +127,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
const switchToVisual = async () => {
|
const switchToVisual = async () => {
|
||||||
const parsed = await parseManifest(editYaml);
|
const parsed = await parseManifest(editYaml);
|
||||||
if (!parsed) {
|
if (!parsed) {
|
||||||
toast({ title: i18n.t('无法解析 YAML'), description: i18n.t('请检查语法后继续用 YAML 模式'), variant: 'destructive' });
|
toast({ title: i18n.t('k8s.rve.parseYamlFailed'), description: i18n.t('k8s.rve.checkSyntax'), variant: 'destructive' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setManifest(parsed);
|
setManifest(parsed);
|
||||||
@@ -156,7 +156,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
const downloadYamlContent = () => {
|
const downloadYamlContent = () => {
|
||||||
const fname = yamlDownloadFilename(resource, namespace, name || manifest?.metadata?.name || 'resource');
|
const fname = yamlDownloadFilename(resource, namespace, name || manifest?.metadata?.name || 'resource');
|
||||||
downloadYamlFile(fname, currentYamlText());
|
downloadYamlFile(fname, currentYamlText());
|
||||||
toast({ title: i18n.t('已下载'), description: fname });
|
toast({ title: i18n.t('k8s.rve.downloaded'), description: fname });
|
||||||
};
|
};
|
||||||
|
|
||||||
const resCfg = getYamlResourceConfig(resource);
|
const resCfg = getYamlResourceConfig(resource);
|
||||||
@@ -262,7 +262,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
{!['configmaps', 'secrets', 'services', 'ingresses', 'pvcs'].includes(resource) && (
|
{!['configmaps', 'secrets', 'services', 'ingresses', 'pvcs'].includes(resource) && (
|
||||||
<div className="flex-1 flex items-center justify-center text-center text-[var(--dg-text-muted)]">
|
<div className="flex-1 flex items-center justify-center text-center text-[var(--dg-text-muted)]">
|
||||||
<div>
|
<div>
|
||||||
<p> {i18n.t('域名 (如 api.example.com)')} </p>
|
<p> {i18n.t('k8s.rve.hostPlaceholder')} </p>
|
||||||
<Button variant="outline" size="sm" className="mt-4" onClick={() => setMode('yaml')}> {i18n.t('kubernetes.resourcevisuale.s54c78d4f')} </Button>
|
<Button variant="outline" size="sm" className="mt-4" onClick={() => setMode('yaml')}> {i18n.t('kubernetes.resourcevisuale.s54c78d4f')} </Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -270,7 +270,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center py-12 text-[var(--dg-text-muted)]">
|
<div className="text-center py-12 text-[var(--dg-text-muted)]">
|
||||||
<p> {i18n.t('键名 (如 LOG_LEVEL)')} </p>
|
<p> {i18n.t('k8s.rve.keyPlaceholder')} </p>
|
||||||
<Button variant="outline" size="sm" className="mt-4" onClick={() => setMode('yaml')}> {i18n.t('kubernetes.resourcevisuale.s54c78d4f')} </Button>
|
<Button variant="outline" size="sm" className="mt-4" onClick={() => setMode('yaml')}> {i18n.t('kubernetes.resourcevisuale.s54c78d4f')} </Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default function WorkloadViewPage() {
|
|||||||
} else {
|
} else {
|
||||||
await kubernetesApi.restartDaemonSet(clusterCode, namespace, name);
|
await kubernetesApi.restartDaemonSet(clusterCode, namespace, name);
|
||||||
}
|
}
|
||||||
toast({ title: i18n.t('重启指令已下发'), description: `${kind} 滚动重启已触发` });
|
toast({ title: i18n.t('k8s.workloadView.restartSent'), description: `${kind} 滚动重启已触发` });
|
||||||
setTimeout(loadData, 1000);
|
setTimeout(loadData, 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.deployment.restartFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.deployment.restartFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
||||||
@@ -122,7 +122,7 @@ export default function WorkloadViewPage() {
|
|||||||
setScaleModal(false);
|
setScaleModal(false);
|
||||||
setTimeout(loadData, 1000);
|
setTimeout(loadData, 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('重启失败'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
toast({ title: i18n.t('k8s.workloadView.restartFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
||||||
} finally { setActionLoading(false); }
|
} finally { setActionLoading(false); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ export default function WorkloadViewPage() {
|
|||||||
subtitle={`${clusterCode} / ${namespace} · ${kind}`}
|
subtitle={`${clusterCode} / ${namespace} · ${kind}`}
|
||||||
icon={<Boxes className="h-5 w-5" />}
|
icon={<Boxes className="h-5 w-5" />}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
loadingLabel={i18n.t('加载工作负载...')}
|
loadingLabel={i18n.t('k8s.workloadView.loading')}
|
||||||
actions={(
|
actions={(
|
||||||
<>
|
<>
|
||||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => navigate(-1)}>
|
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => navigate(-1)}>
|
||||||
@@ -162,7 +162,7 @@ export default function WorkloadViewPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button variant="outline" size="sm" className="dg-btn-secondary" disabled={actionLoading} onClick={handleRestart}>
|
<Button variant="outline" size="sm" className="dg-btn-secondary" disabled={actionLoading} onClick={handleRestart}>
|
||||||
<RotateCcw className="h-3.5 w-3.5 mr-1" />{actionLoading ? i18n.t('重启中...') : i18n.t('agent.agentServiceRestart')}
|
<RotateCcw className="h-3.5 w-3.5 mr-1" />{actionLoading ? i18n.t('k8s.workloadView.restarting') : i18n.t('agent.agentServiceRestart')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => setYamlModal(true)}>
|
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => setYamlModal(true)}>
|
||||||
<FileCode className="h-3.5 w-3.5 mr-1" />YAML
|
<FileCode className="h-3.5 w-3.5 mr-1" />YAML
|
||||||
@@ -201,14 +201,14 @@ export default function WorkloadViewPage() {
|
|||||||
<StatCard label={i18n.t('kubernetes.workloadviewpag.sea639697')} value={`${readyReplicas}/${replicas}`} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} sub={isHealthy ? i18n.t('kubernetes.workloadviewpag.s23c944c1') : i18n.t('kubernetes.workloadviewpag.sac100213')} />
|
<StatCard label={i18n.t('kubernetes.workloadviewpag.sea639697')} value={`${readyReplicas}/${replicas}`} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} sub={isHealthy ? i18n.t('kubernetes.workloadviewpag.s23c944c1') : i18n.t('kubernetes.workloadviewpag.sac100213')} />
|
||||||
<StatCard label={i18n.t('kubernetes.hpa.currentReplicas')} value={currentReplicas} color={STATUS_COLORS.blue} />
|
<StatCard label={i18n.t('kubernetes.hpa.currentReplicas')} value={currentReplicas} color={STATUS_COLORS.blue} />
|
||||||
<StatCard label={i18n.t('kubernetes.deployment.updatedReplicas')} value={updatedReplicas} color={STATUS_COLORS.green} />
|
<StatCard label={i18n.t('kubernetes.deployment.updatedReplicas')} value={updatedReplicas} color={STATUS_COLORS.green} />
|
||||||
<StatCard label={i18n.t('当前副本')} value={stats.spec.serviceName || '-'} color="var(--dg-text-secondary)" />
|
<StatCard label={i18n.t('k8s.workloadView.currentReplicas')} value={stats.spec.serviceName || '-'} color="var(--dg-text-secondary)" />
|
||||||
<StatCard label={i18n.t('当前副本')} value={pods.length} color="var(--dg-text-secondary)" />
|
<StatCard label={i18n.t('k8s.workloadView.currentReplicas')} value={pods.length} color="var(--dg-text-secondary)" />
|
||||||
<StatCard label={i18n.t('system.userCreatedAt')} value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
|
<StatCard label={i18n.t('system.userCreatedAt')} value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<StatCard label={i18n.t('期望调度')} value={desiredScheduled} color={STATUS_COLORS.blue} />
|
<StatCard label={i18n.t('k8s.workloadView.desiredSchedule')} value={desiredScheduled} color={STATUS_COLORS.blue} />
|
||||||
<StatCard label={i18n.t('当前调度')} value={currentScheduled} color="var(--dg-text-secondary)" />
|
<StatCard label={i18n.t('k8s.workloadView.actualSchedule')} value={currentScheduled} color="var(--dg-text-secondary)" />
|
||||||
<StatCard label={i18n.t('kubernetes.daemonset.numberReady')} value={numberReady} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} />
|
<StatCard label={i18n.t('kubernetes.daemonset.numberReady')} value={numberReady} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} />
|
||||||
<StatCard label={i18n.t('kubernetes.daemonset.numberAvailable')} value={numberAvailable} color={STATUS_COLORS.green} />
|
<StatCard label={i18n.t('kubernetes.daemonset.numberAvailable')} value={numberAvailable} color={STATUS_COLORS.green} />
|
||||||
<StatCard label={i18n.t('deploy.toast.updated')} value={numberUpdated} color="var(--dg-text-secondary)" />
|
<StatCard label={i18n.t('deploy.toast.updated')} value={numberUpdated} color="var(--dg-text-secondary)" />
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export function YamlViewer({
|
|||||||
setRawYaml(text);
|
setRawYaml(text);
|
||||||
await applyStripped(text);
|
await applyStripped(text);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('获取YAML失败'), description: (e as JsonData).message, variant: 'destructive' });
|
toast({ title: i18n.t('k8s.yamlViewer.fetchFailed'), description: (e as JsonData).message, variant: 'destructive' });
|
||||||
const err = `# Error: ${(e as Error).message}`;
|
const err = `# Error: ${(e as Error).message}`;
|
||||||
setRawYaml(err);
|
setRawYaml(err);
|
||||||
setDisplayYaml(err);
|
setDisplayYaml(err);
|
||||||
@@ -104,7 +104,7 @@ export function YamlViewer({
|
|||||||
} else {
|
} else {
|
||||||
await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr);
|
await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr);
|
||||||
}
|
}
|
||||||
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: saveYamlFn ? i18n.t('保存失败') : `${resource}/${name} 已更新` });
|
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: saveYamlFn ? i18n.t('k8s.rve.saveFailed') : `${resource}/${name} 已更新` });
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
setRawYaml(yamlStr);
|
setRawYaml(yamlStr);
|
||||||
setDisplayYaml(yamlStr);
|
setDisplayYaml(yamlStr);
|
||||||
@@ -125,7 +125,7 @@ export function YamlViewer({
|
|||||||
|
|
||||||
const downloadYaml = () => {
|
const downloadYaml = () => {
|
||||||
downloadYamlFile(yamlDownloadFilename(resource, namespace, name), currentYaml());
|
downloadYamlFile(yamlDownloadFilename(resource, namespace, name), currentYaml());
|
||||||
toast({ title: i18n.t('已下载'), description: yamlDownloadFilename(resource, namespace, name) });
|
toast({ title: i18n.t('k8s.rve.downloaded'), description: yamlDownloadFilename(resource, namespace, name) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const display = editing ? editYaml : (showRaw ? rawYaml : displayYaml);
|
const display = editing ? editYaml : (showRaw ? rawYaml : displayYaml);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export async function saveDeploymentBundleYaml(
|
|||||||
for (const manifest of manifests) {
|
for (const manifest of manifests) {
|
||||||
const kind = String(manifest.kind || '').toLowerCase();
|
const kind = String(manifest.kind || '').toLowerCase();
|
||||||
const name = String(manifest.metadata?.name || '').trim();
|
const name = String(manifest.metadata?.name || '').trim();
|
||||||
if (!kind || !name) throw new Error(i18n.t('每个 YAML 文档必须包含 kind 和 metadata.name'));
|
if (!kind || !name) throw new Error(i18n.t('k8s.misc.yamlDocError'));
|
||||||
|
|
||||||
const docYaml = await prepareManifestForSave(manifest);
|
const docYaml = await prepareManifestForSave(manifest);
|
||||||
if (kind === 'deployment') {
|
if (kind === 'deployment') {
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export function CreateHPADialog({ clusterCode, namespace, deploymentName, onCrea
|
|||||||
<div className="flex justify-end gap-2 px-6 py-4 border-t border-[var(--dg-border)] shrink-0">
|
<div className="flex justify-end gap-2 px-6 py-4 border-t border-[var(--dg-border)] shrink-0">
|
||||||
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={() => setOpen(false)}>取消</Button>
|
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={() => setOpen(false)}>取消</Button>
|
||||||
<Button className="bg-blue-600 hover:bg-blue-700" disabled={loading} onClick={handleCreate}>
|
<Button className="bg-blue-600 hover:bg-blue-700" disabled={loading} onClick={handleCreate}>
|
||||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : i18n.t('创建')}
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : i18n.t('common.create')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
renderRow: (item: any, opts: ResourceRowOpts) => React.ReactNode;
|
renderRow: (item: any, opts: ResourceRowOpts) => React.ReactNode;
|
||||||
}> = {
|
}> = {
|
||||||
deployments: {
|
deployments: {
|
||||||
title: 'Deployments', subtitle: i18n.t('Deployment 工作负载运行状态'), icon: Boxes,
|
title: 'Deployments', subtitle: i18n.t('k8s.nsResource.deployDesc'), icon: Boxes,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('名称'), i18n.t('kubernetes.container.image')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('common.name'), i18n.t('kubernetes.container.image')],
|
||||||
renderRow: (d, opts) => (
|
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)}>
|
<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>
|
<TableCell className="font-medium text-[var(--dg-text-primary)]">{d.name}</TableCell>
|
||||||
@@ -77,7 +77,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
pods: {
|
pods: {
|
||||||
title: 'Pods', subtitle: i18n.t('Pod 实例运行状态'), icon: Container,
|
title: 'Pods', subtitle: i18n.t('k8s.nsResource.podDesc'), 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')],
|
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) => {
|
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' };
|
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' };
|
||||||
@@ -113,7 +113,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
services: {
|
services: {
|
||||||
title: 'Services', subtitle: i18n.t('Service 网络服务'), icon: Network,
|
title: 'Services', subtitle: i18n.t('k8s.nsResource.serviceDesc'), 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')],
|
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) => (
|
renderRow: (s, opts) => (
|
||||||
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||||
@@ -127,7 +127,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
ingresses: {
|
ingresses: {
|
||||||
title: 'Ingresses', subtitle: i18n.t('Ingress 路由规则'), icon: Network,
|
title: 'Ingresses', subtitle: i18n.t('k8s.nsResource.ingressDesc'), icon: Network,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), 'Hosts', 'TLS', i18n.t('system.auditAction')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), 'Hosts', 'TLS', i18n.t('system.auditAction')],
|
||||||
renderRow: (i, opts) => {
|
renderRow: (i, opts) => {
|
||||||
const hosts = i.hosts || [];
|
const hosts = i.hosts || [];
|
||||||
@@ -143,8 +143,8 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
configmaps: {
|
configmaps: {
|
||||||
title: 'ConfigMaps', subtitle: i18n.t('配置字典'), icon: FileText,
|
title: 'ConfigMaps', subtitle: i18n.t('k8s.nsResource.configMapDesc'), icon: FileText,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('名称'), i18n.t('system.auditAction')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('common.name'), i18n.t('system.auditAction')],
|
||||||
renderRow: (c, opts) => (
|
renderRow: (c, opts) => (
|
||||||
<TableRow key={c.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<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>
|
<TableCell className="font-medium text-[var(--dg-text-primary)]">{c.name}</TableCell>
|
||||||
@@ -155,7 +155,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
secrets: {
|
secrets: {
|
||||||
title: 'Secrets', subtitle: i18n.t('密钥管理(仅名称与类型)'), icon: Shield,
|
title: 'Secrets', subtitle: i18n.t('k8s.nsResource.secretDesc'), icon: Shield,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('system.credentialType'), i18n.t('system.auditAction')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('system.credentialType'), i18n.t('system.auditAction')],
|
||||||
renderRow: (s, opts) => (
|
renderRow: (s, opts) => (
|
||||||
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<TableRow key={s.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||||
@@ -167,7 +167,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
hpas: {
|
hpas: {
|
||||||
title: 'HPAs', subtitle: i18n.t('水平自动伸缩'), icon: Activity,
|
title: 'HPAs', subtitle: i18n.t('k8s.nsResource.hpaDesc'), 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%'],
|
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) => (
|
renderRow: (h, _opts) => (
|
||||||
<TableRow key={h.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<TableRow key={h.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||||
@@ -194,7 +194,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
pvcs: {
|
pvcs: {
|
||||||
title: i18n.t('存储卷声明'), subtitle: 'PersistentVolumeClaim', icon: Boxes,
|
title: i18n.t('k8s.nsResource.pvcDesc'), 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')],
|
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) => (
|
renderRow: (p, opts) => (
|
||||||
<TableRow key={p.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<TableRow key={p.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
||||||
@@ -208,8 +208,8 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
statefulsets: {
|
statefulsets: {
|
||||||
title: 'StatefulSets', subtitle: i18n.t('有状态应用'), icon: Boxes,
|
title: 'StatefulSets', subtitle: i18n.t('k8s.nsResource.stsDesc'), icon: Boxes,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('名称'), i18n.t('kubernetes.pod.ready'), i18n.t('kubernetes.container.image')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('common.name'), i18n.t('kubernetes.pod.ready'), i18n.t('kubernetes.container.image')],
|
||||||
renderRow: (s, opts) => (
|
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)}>
|
<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>
|
<TableCell className="font-medium text-[var(--dg-text-primary)]">{s.name}</TableCell>
|
||||||
@@ -221,7 +221,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
daemonsets: {
|
daemonsets: {
|
||||||
title: 'DaemonSets', subtitle: i18n.t('守护进程集'), icon: Boxes,
|
title: 'DaemonSets', subtitle: i18n.t('k8s.nsResource.dsDesc'), 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')],
|
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) => (
|
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)}>
|
<TableRow key={d.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30 cursor-pointer" onClick={() => opts.onDetail?.(d)}>
|
||||||
@@ -235,8 +235,8 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
serviceaccounts: {
|
serviceaccounts: {
|
||||||
title: 'ServiceAccounts', subtitle: i18n.t('服务账号(RBAC 身份标识)'), icon: Shield,
|
title: 'ServiceAccounts', subtitle: i18n.t('k8s.nsResource.saDesc'), icon: Shield,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('名称'), i18n.t('命名空间'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('common.name'), i18n.t('common.namespace'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
||||||
renderRow: (sa, opts) => (
|
renderRow: (sa, opts) => (
|
||||||
<TableRow key={sa.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
|
<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>
|
<TableCell className="font-medium text-[var(--dg-text-primary)]">{sa.name}</TableCell>
|
||||||
@@ -247,15 +247,15 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-xs text-[var(--dg-text-muted)]">{sa.age || '-'}</TableCell>
|
<TableCell className="text-xs text-[var(--dg-text-muted)]">{sa.age || '-'}</TableCell>
|
||||||
<TableCell onClick={(e) => e.stopPropagation()}>
|
<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={i18n.t('查看YAML')}
|
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title={i18n.t('k8s.nsResource.viewYaml')}
|
||||||
onClick={() => opts.onYaml?.(sa.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
onClick={() => opts.onYaml?.(sa.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
roles: {
|
roles: {
|
||||||
title: 'Roles', subtitle: i18n.t('命名空间级角色权限(RBAC)'), icon: Shield,
|
title: 'Roles', subtitle: i18n.t('k8s.nsResource.roleDesc'), icon: Shield,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('名称'), i18n.t('system.apiKeyScope'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('common.name'), i18n.t('system.apiKeyScope'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
||||||
renderRow: (r, opts) => {
|
renderRow: (r, opts) => {
|
||||||
const verbs = r.verbs || [];
|
const verbs = r.verbs || [];
|
||||||
const resources = r.resources || [];
|
const resources = r.resources || [];
|
||||||
@@ -275,7 +275,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
<TableCell className="text-xs text-[var(--dg-text-muted)]">{r.age || '-'}</TableCell>
|
<TableCell className="text-xs text-[var(--dg-text-muted)]">{r.age || '-'}</TableCell>
|
||||||
<TableCell onClick={(e) => e.stopPropagation()}>
|
<TableCell onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="flex items-center gap-0.5">
|
<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={i18n.t('查看YAML')}
|
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title={i18n.t('k8s.nsResource.viewYaml')}
|
||||||
onClick={() => opts.onYaml?.(r.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
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={i18n.t('common.edit')}
|
<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>
|
onClick={() => opts.onEdit?.(r.name)}><Pencil className="h-3.5 w-3.5" /></Button>
|
||||||
@@ -286,8 +286,8 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
rolebindings: {
|
rolebindings: {
|
||||||
title: 'RoleBindings', subtitle: i18n.t('角色绑定关系(RBAC 授权)'), icon: Shield,
|
title: 'RoleBindings', subtitle: i18n.t('k88s.nsResource.rbDesc'), icon: Shield,
|
||||||
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('名称'), i18n.t('命名空间'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
columns: [i18n.t('kubernetes.secret.secretName'), i18n.t('kubernetes.namespace.title'), i18n.t('common.name'), i18n.t('common.namespace'), i18n.t('system.userCreatedAt'), i18n.t('system.auditAction')],
|
||||||
renderRow: (rb, opts) => {
|
renderRow: (rb, opts) => {
|
||||||
const subjects = rb.subjects || [];
|
const subjects = rb.subjects || [];
|
||||||
const subjStr = subjects.length > 3 ? subjects.slice(0, 3).join(', ') + ` +${subjects.length - 3}` : subjects.join(', ');
|
const subjStr = subjects.length > 3 ? subjects.slice(0, 3).join(', ') + ` +${subjects.length - 3}` : subjects.join(', ');
|
||||||
@@ -306,7 +306,7 @@ export const RESOURCE_CONFIG: Record<ResourceType, {
|
|||||||
<TableCell className="text-xs text-[var(--dg-text-muted)]">{rb.age || '-'}</TableCell>
|
<TableCell className="text-xs text-[var(--dg-text-muted)]">{rb.age || '-'}</TableCell>
|
||||||
<TableCell onClick={(e) => e.stopPropagation()}>
|
<TableCell onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="flex items-center gap-0.5">
|
<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={i18n.t('查看YAML')}
|
<Button variant="ghost" size="icon" className="h-7 w-7 text-[var(--dg-text-secondary)] hover:text-[var(--dg-blue)]" title={i18n.t('k8s.nsResource.viewYaml')}
|
||||||
onClick={() => opts.onYaml?.(rb.name)}><FileCode className="h-3.5 w-3.5" /></Button>
|
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={i18n.t('common.edit')}
|
<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>
|
onClick={() => opts.onEdit?.(rb.name)}><Pencil className="h-3.5 w-3.5" /></Button>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
size="sm"
|
size="sm"
|
||||||
className={cn('h-8 text-xs', previous ? 'bg-amber-600 hover:bg-amber-700' : 'border-[var(--dg-border)] text-[var(--dg-text-secondary)]')}
|
className={cn('h-8 text-xs', previous ? 'bg-amber-600 hover:bg-amber-700' : 'border-[var(--dg-border)] text-[var(--dg-text-secondary)]')}
|
||||||
onClick={onPreviousToggle}
|
onClick={onPreviousToggle}
|
||||||
title={i18n.t('查看上一次(崩溃)容器的日志')}
|
title={i18n.t('k8s.podLogs.previousLogs')}
|
||||||
>
|
>
|
||||||
<History className="h-3.5 w-3.5 mr-1" />
|
<History className="h-3.5 w-3.5 mr-1" />
|
||||||
previous
|
previous
|
||||||
@@ -102,7 +102,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
onSinceTimeChange(v ? new Date(v).toISOString() : '');
|
onSinceTimeChange(v ? new Date(v).toISOString() : '');
|
||||||
}}
|
}}
|
||||||
className="h-8 w-[170px] text-xs bg-[var(--dg-bg-hover)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="h-8 w-[170px] text-xs bg-[var(--dg-bg-hover)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
title={i18n.t('查看上一次(崩溃)容器的日志')}
|
title={i18n.t('k8s.podLogs.previousLogs')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
<Input
|
<Input
|
||||||
value={keyword}
|
value={keyword}
|
||||||
onChange={(e) => onKeywordChange(e.target.value)}
|
onChange={(e) => onKeywordChange(e.target.value)}
|
||||||
placeholder={i18n.t('关键字过滤…')}
|
placeholder={i18n.t('k8s.podLogs.keywordFilter')}
|
||||||
className="h-8 w-[160px] pl-7 text-xs bg-[var(--dg-bg-hover)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="h-8 w-[160px] pl-7 text-xs bg-[var(--dg-bg-hover)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,7 +124,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
variant="outline" size="sm"
|
variant="outline" size="sm"
|
||||||
className="h-8 text-xs border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
|
className="h-8 text-xs border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
|
||||||
onClick={onDownload}
|
onClick={onDownload}
|
||||||
title={i18n.t('查看上一次(崩溃)容器的日志')}
|
title={i18n.t('k8s.podLogs.previousLogs')}
|
||||||
disabled={filteredLineCount === 0}
|
disabled={filteredLineCount === 0}
|
||||||
>
|
>
|
||||||
<Download className="h-3.5 w-3.5 mr-1" />导出
|
<Download className="h-3.5 w-3.5 mr-1" />导出
|
||||||
@@ -136,7 +136,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
className="h-8 text-xs border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
|
className="h-8 text-xs border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
|
||||||
onClick={onRefresh}
|
onClick={onRefresh}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
title={i18n.t('查看上一次(崩溃)容器的日志')}
|
title={i18n.t('k8s.podLogs.previousLogs')}
|
||||||
>
|
>
|
||||||
<RefreshCw className={cn('h-3.5 w-3.5 mr-1', loading && 'animate-spin')} />刷新
|
<RefreshCw className={cn('h-3.5 w-3.5 mr-1', loading && 'animate-spin')} />刷新
|
||||||
</Button>
|
</Button>
|
||||||
@@ -148,7 +148,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
className="h-8 text-xs bg-emerald-600 hover:bg-emerald-700"
|
className="h-8 text-xs bg-emerald-600 hover:bg-emerald-700"
|
||||||
onClick={onStartStream}
|
onClick={onStartStream}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
title={i18n.t('查看上一次(崩溃)容器的日志')}
|
title={i18n.t('k8s.podLogs.previousLogs')}
|
||||||
>
|
>
|
||||||
<Play className="h-3.5 w-3.5 mr-1" />实时
|
<Play className="h-3.5 w-3.5 mr-1" />实时
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ export function ConfigMapEditor({ manifest, onSave, saving, isCreate }: ConfigMa
|
|||||||
|
|
||||||
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
||||||
<div className="flex items-center justify-between mb-2 shrink-0">
|
<div className="flex items-center justify-between mb-2 shrink-0">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('加载中...')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('common.loading')} </Label>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setExpandAll(!expandAll)}>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setExpandAll(!expandAll)}>
|
||||||
{expandAll ? i18n.t('折叠全部') : i18n.t('system.expandAll')}
|
{expandAll ? i18n.t('k8s.configMap.collapseAll') : i18n.t('system.expandAll')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRow}><Plus className="h-3 w-3 mr-1" /> {i18n.t('monitor.dashboard.custom.addBtn')} </Button>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRow}><Plus className="h-3 w-3 mr-1" /> {i18n.t('monitor.dashboard.custom.addBtn')} </Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,13 +63,13 @@ export function ConfigMapEditor({ manifest, onSave, saving, isCreate }: ConfigMa
|
|||||||
<Input
|
<Input
|
||||||
value={entry.key}
|
value={entry.key}
|
||||||
onChange={e => update(i, 'key', e.target.value)}
|
onChange={e => update(i, 'key', e.target.value)}
|
||||||
placeholder={i18n.t('键名 (如 LOG_LEVEL)')}
|
placeholder={i18n.t('k8s.rve.keyPlaceholder')}
|
||||||
className="flex-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="flex-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
/>
|
/>
|
||||||
<Textarea
|
<Textarea
|
||||||
value={entry.value}
|
value={entry.value}
|
||||||
onChange={e => update(i, 'value', e.target.value)}
|
onChange={e => update(i, 'value', e.target.value)}
|
||||||
placeholder={i18n.t('键名 (如 LOG_LEVEL)')}
|
placeholder={i18n.t('k8s.rve.keyPlaceholder')}
|
||||||
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
|
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
|
||||||
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
|
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
|
||||||
/>
|
/>
|
||||||
@@ -79,7 +79,7 @@ export function ConfigMapEditor({ manifest, onSave, saving, isCreate }: ConfigMa
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{entries.length === 0 && (
|
{entries.length === 0 && (
|
||||||
<div className="text-center py-8 text-[var(--dg-text-muted)] text-xs"> {i18n.t('名称')} </div>
|
<div className="text-center py-8 text-[var(--dg-text-muted)] text-xs"> {i18n.t('common.name')} </div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="h-8 text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]">
|
<SelectTrigger className="h-8 text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]">
|
||||||
<SelectValue placeholder={loadingClasses ? i18n.t('monitor.logPanel.logResults.loading') : i18n.t('加载中...')} />
|
<SelectValue placeholder={loadingClasses ? i18n.t('monitor.logPanel.logResults.loading') : i18n.t('common.loading')} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{ingressClasses.length > 0 ? (
|
{ingressClasses.length > 0 ? (
|
||||||
@@ -131,7 +131,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
</SelectItem>
|
</SelectItem>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
!loadingClasses && <div className="px-2 py-1.5 text-xs text-[var(--dg-text-muted)]"> {i18n.t('加载中...')} </div>
|
!loadingClasses && <div className="px-2 py-1.5 text-xs text-[var(--dg-text-muted)]"> {i18n.t('common.loading')} </div>
|
||||||
)}
|
)}
|
||||||
{/* 当前自定义值作为选项,确保选中态 */}
|
{/* 当前自定义值作为选项,确保选中态 */}
|
||||||
{ingressClass && !ingressClasses.some((ic) => ic.name === ingressClass) && (
|
{ingressClass && !ingressClasses.some((ic) => ic.name === ingressClass) && (
|
||||||
@@ -169,7 +169,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
||||||
<div className="flex items-center justify-between mb-2 shrink-0">
|
<div className="flex items-center justify-between mb-2 shrink-0">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('kubernetes.ingress.rules')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('kubernetes.ingress.rules')} </Label>
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRule}><Plus className="h-3 w-3 mr-1" /> {i18n.t('端口')} </Button>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRule}><Plus className="h-3 w-3 mr-1" /> {i18n.t('k8s.service.portLabel')} </Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-h-0 overflow-y-auto pr-1 space-y-3 border border-[var(--dg-border)] rounded-lg p-2 bg-[var(--dg-bg-secondary)]">
|
<div className="flex-1 min-h-0 overflow-y-auto pr-1 space-y-3 border border-[var(--dg-border)] rounded-lg p-2 bg-[var(--dg-bg-secondary)]">
|
||||||
{rules.map((rule, ri) => (
|
{rules.map((rule, ri) => (
|
||||||
@@ -178,7 +178,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
<Input
|
<Input
|
||||||
value={rule.host}
|
value={rule.host}
|
||||||
onChange={e => updateRule(ri, e.target.value)}
|
onChange={e => updateRule(ri, e.target.value)}
|
||||||
placeholder={i18n.t('域名 (如 api.example.com)')}
|
placeholder={i18n.t('k8s.rve.hostPlaceholder')}
|
||||||
className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
title={rule.host}
|
title={rule.host}
|
||||||
/>
|
/>
|
||||||
@@ -187,7 +187,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
{rule.paths.map((p, pi) => (
|
{rule.paths.map((p, pi) => (
|
||||||
<div key={pi} className="overflow-x-auto pl-4">
|
<div key={pi} className="overflow-x-auto pl-4">
|
||||||
<div className="grid grid-cols-[minmax(80px,1fr)_100px_minmax(80px,1fr)_60px_28px] gap-1.5 items-center min-w-[420px]">
|
<div className="grid grid-cols-[minmax(80px,1fr)_100px_minmax(80px,1fr)_60px_28px] gap-1.5 items-center min-w-[420px]">
|
||||||
<Input value={p.path} onChange={e => updatePath(ri, pi, 'path', e.target.value)} placeholder={i18n.t('路径 /')} className="font-mono text-xs h-7 min-w-0 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
<Input value={p.path} onChange={e => updatePath(ri, pi, 'path', e.target.value)} placeholder={i18n.t('k8s.ingress.pathPlaceholder')} className="font-mono text-xs h-7 min-w-0 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
||||||
<Select value={p.pathType} onValueChange={v => updatePath(ri, pi, 'pathType', v)}>
|
<Select value={p.pathType} onValueChange={v => updatePath(ri, pi, 'pathType', v)}>
|
||||||
<SelectTrigger className="h-7 text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"><SelectValue /></SelectTrigger>
|
<SelectTrigger className="h-7 text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"><SelectValue /></SelectTrigger>
|
||||||
<SelectContent><SelectItem value="Prefix">Prefix</SelectItem><SelectItem value="Exact">Exact</SelectItem></SelectContent>
|
<SelectContent><SelectItem value="Prefix">Prefix</SelectItem><SelectItem value="Exact">Exact</SelectItem></SelectContent>
|
||||||
@@ -201,7 +201,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
<Button variant="ghost" size="sm" className="h-6 text-xs text-[var(--dg-text-muted)]" onClick={() => addPath(ri)}><Plus className="h-3 w-3 mr-1" /> {i18n.t('kubernetes.ingress.addPath')} </Button>
|
<Button variant="ghost" size="sm" className="h-6 text-xs text-[var(--dg-text-muted)]" onClick={() => addPath(ri)}><Plus className="h-3 w-3 mr-1" /> {i18n.t('kubernetes.ingress.addPath')} </Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{rules.length === 0 && <div className="text-center py-4 text-[var(--dg-text-muted)] text-xs"> {i18n.t('名称')} </div>}
|
{rules.length === 0 && <div className="text-center py-4 text-[var(--dg-text-muted)] text-xs"> {i18n.t('common.name')} </div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
|
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
|
||||||
|
|||||||
@@ -44,14 +44,14 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4 mt-3 shrink-0">
|
<div className="grid grid-cols-2 gap-4 mt-3 shrink-0">
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-xs"> {i18n.t('加载中...')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-xs"> {i18n.t('common.loading')} </Label>
|
||||||
<Input
|
<Input
|
||||||
value={size}
|
value={size}
|
||||||
onChange={e => setSize(e.target.value)}
|
onChange={e => setSize(e.target.value)}
|
||||||
placeholder={i18n.t('如 10Gi')}
|
placeholder={i18n.t('k8s.pvc.sizePlaceholder')}
|
||||||
className="mt-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="mt-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
/>
|
/>
|
||||||
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1"> {i18n.t('名称')} </p>
|
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1"> {i18n.t('common.name')} </p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-xs"> {i18n.t('kubernetes.storage.accessModes')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-xs"> {i18n.t('kubernetes.storage.accessModes')} </Label>
|
||||||
@@ -72,7 +72,7 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps
|
|||||||
<Input
|
<Input
|
||||||
value={storageClass}
|
value={storageClass}
|
||||||
onChange={e => setStorageClass(e.target.value)}
|
onChange={e => setStorageClass(e.target.value)}
|
||||||
placeholder={i18n.t('留空使用默认 StorageClass')}
|
placeholder={i18n.t('k8s.pvc.scPlaceholder')}
|
||||||
className="mt-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="mt-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -68,10 +68,10 @@ export function SecretEditor({ manifest, onSave, saving, isCreate }: SecretEdito
|
|||||||
|
|
||||||
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
||||||
<div className="flex items-center justify-between mb-2 shrink-0">
|
<div className="flex items-center justify-between mb-2 shrink-0">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('加载中...')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('common.loading')} </Label>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setExpandAll(!expandAll)}>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setExpandAll(!expandAll)}>
|
||||||
{expandAll ? i18n.t('折叠全部') : i18n.t('system.expandAll')}
|
{expandAll ? i18n.t('k8s.configMap.collapseAll') : i18n.t('system.expandAll')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRow}><Plus className="h-3 w-3 mr-1" /> {i18n.t('monitor.dashboard.custom.addBtn')} </Button>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRow}><Plus className="h-3 w-3 mr-1" /> {i18n.t('monitor.dashboard.custom.addBtn')} </Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,13 +82,13 @@ export function SecretEditor({ manifest, onSave, saving, isCreate }: SecretEdito
|
|||||||
<Input
|
<Input
|
||||||
value={entry.key}
|
value={entry.key}
|
||||||
onChange={e => update(i, 'key', e.target.value)}
|
onChange={e => update(i, 'key', e.target.value)}
|
||||||
placeholder={i18n.t('键名 (如 LOG_LEVEL)')}
|
placeholder={i18n.t('k8s.rve.keyPlaceholder')}
|
||||||
className="flex-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
className="flex-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||||
/>
|
/>
|
||||||
<Textarea
|
<Textarea
|
||||||
value={entry.value}
|
value={entry.value}
|
||||||
onChange={e => update(i, 'value', e.target.value)}
|
onChange={e => update(i, 'value', e.target.value)}
|
||||||
placeholder={i18n.t('键名 (如 LOG_LEVEL)')}
|
placeholder={i18n.t('k8s.rve.keyPlaceholder')}
|
||||||
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
|
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
|
||||||
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
|
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
|
||||||
/>
|
/>
|
||||||
@@ -98,7 +98,7 @@ export function SecretEditor({ manifest, onSave, saving, isCreate }: SecretEdito
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{entries.length === 0 && (
|
{entries.length === 0 && (
|
||||||
<div className="text-center py-8 text-[var(--dg-text-muted)] text-xs"> {i18n.t('名称')} </div>
|
<div className="text-center py-8 text-[var(--dg-text-muted)] text-xs"> {i18n.t('common.name')} </div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export function ServiceEditor({ manifest, onSave, saving, isCreate }: ServiceEdi
|
|||||||
<div className="flex-1 min-h-0 overflow-y-auto mt-3 pr-1 space-y-3">
|
<div className="flex-1 min-h-0 overflow-y-auto mt-3 pr-1 space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('加载中...')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('common.loading')} </Label>
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addPort}><Plus className="h-3 w-3 mr-1" /> {i18n.t('agent.addPort')} </Button>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addPort}><Plus className="h-3 w-3 mr-1" /> {i18n.t('agent.addPort')} </Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 border border-[var(--dg-border)] rounded-lg p-2 bg-[var(--dg-bg-secondary)]">
|
<div className="space-y-2 border border-[var(--dg-border)] rounded-lg p-2 bg-[var(--dg-bg-secondary)]">
|
||||||
@@ -120,22 +120,22 @@ export function ServiceEditor({ manifest, onSave, saving, isCreate }: ServiceEdi
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{ports.length === 0 && <div className="text-center py-4 text-[var(--dg-text-muted)] text-xs"> {i18n.t('端口')} </div>}
|
{ports.length === 0 && <div className="text-center py-4 text-[var(--dg-text-muted)] text-xs"> {i18n.t('k8s.service.portLabel')} </div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Selector */}
|
{/* Selector */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('加载中...')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('common.loading')} </Label>
|
||||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addSelector}><Plus className="h-3 w-3 mr-1" /> {i18n.t('加载中...')} </Button>
|
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addSelector}><Plus className="h-3 w-3 mr-1" /> {i18n.t('common.loading')} </Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{selector.map((s, i) => (
|
{selector.map((s, i) => (
|
||||||
<div key={i} className="flex items-center gap-2 min-w-0">
|
<div key={i} className="flex items-center gap-2 min-w-0">
|
||||||
<Input value={s.key} onChange={e => updateSelector(i, 'key', e.target.value)} placeholder={i18n.t('键 (如 app)')} className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
<Input value={s.key} onChange={e => updateSelector(i, 'key', e.target.value)} placeholder={i18n.t('k8s.service.selectorKey')} className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
||||||
<span className="text-[var(--dg-text-muted)] text-xs shrink-0">=</span>
|
<span className="text-[var(--dg-text-muted)] text-xs shrink-0">=</span>
|
||||||
<Input value={s.value} onChange={e => updateSelector(i, 'value', e.target.value)} placeholder={i18n.t('值 (如 nginx)')} className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
<Input value={s.value} onChange={e => updateSelector(i, 'value', e.target.value)} placeholder={i18n.t('k8s.service.selectorValue')} className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
||||||
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 dg-text-red hover:dg-text-red" onClick={() => removeSelector(i)}><Trash2 className="h-3 w-3" /></Button>
|
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 dg-text-red hover:dg-text-red" onClick={() => removeSelector(i)}><Trash2 className="h-3 w-3" /></Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ export function UpdateStrategySelectorCards({ stats }: { stats: WorkloadStats })
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pt-0 space-y-2 text-xs">
|
<CardContent className="pt-0 space-y-2 text-xs">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-[var(--dg-text-muted)]"> {i18n.t('服务名')} </span>
|
<span className="text-[var(--dg-text-muted)]"> {i18n.t('k8s.workloadView.serviceName')} </span>
|
||||||
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{strategyType}</Badge>
|
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{strategyType}</Badge>
|
||||||
</div>
|
</div>
|
||||||
{strategyType === 'RollingUpdate' && isSts && updateStrategy.rollingUpdate && (
|
{strategyType === 'RollingUpdate' && isSts && updateStrategy.rollingUpdate && (
|
||||||
<>
|
<>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-[var(--dg-text-muted)]"> {i18n.t('Pod 数量')} </span>
|
<span className="text-[var(--dg-text-muted)]"> {i18n.t('k8s.workloadView.podCount')} </span>
|
||||||
<span className="text-[var(--dg-text-secondary)] font-mono">{updateStrategy.rollingUpdate.partition ?? 0}</span>
|
<span className="text-[var(--dg-text-secondary)] font-mono">{updateStrategy.rollingUpdate.partition ?? 0}</span>
|
||||||
</div>
|
</div>
|
||||||
{updateStrategy.rollingUpdate.maxUnavailable && (
|
{updateStrategy.rollingUpdate.maxUnavailable && (
|
||||||
@@ -47,7 +47,7 @@ export function UpdateStrategySelectorCards({ stats }: { stats: WorkloadStats })
|
|||||||
)}
|
)}
|
||||||
{updateStrategy.rollingUpdate.maxSurge && (
|
{updateStrategy.rollingUpdate.maxSurge && (
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-[var(--dg-text-muted)]"> {i18n.t('Pod 数量')} </span>
|
<span className="text-[var(--dg-text-muted)]"> {i18n.t('k8s.workloadView.podCount')} </span>
|
||||||
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxSurge === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxSurge) : updateStrategy.rollingUpdate.maxSurge}</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -60,7 +60,7 @@ export function UpdateStrategySelectorCards({ stats }: { stats: WorkloadStats })
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-[var(--dg-text-muted)]"> {i18n.t('服务名')} </span>
|
<span className="text-[var(--dg-text-muted)]"> {i18n.t('k8s.workloadView.serviceName')} </span>
|
||||||
<span className="text-[var(--dg-text-secondary)] font-mono">{spec.revisionHistoryLimit ?? 10}</span>
|
<span className="text-[var(--dg-text-secondary)] font-mono">{spec.revisionHistoryLimit ?? 10}</span>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
+247
-14
@@ -2343,7 +2343,10 @@
|
|||||||
"resourceTrend": "Resource Usage Trend (simulated)",
|
"resourceTrend": "Resource Usage Trend (simulated)",
|
||||||
"refreshMetrics": "Refresh node metrics",
|
"refreshMetrics": "Refresh node metrics",
|
||||||
"resourceDistribution": "Resource Distribution",
|
"resourceDistribution": "Resource Distribution",
|
||||||
"now": "Now"
|
"now": "Now",
|
||||||
|
"allNamespaces": "All Namespaces",
|
||||||
|
"provider": "Provider",
|
||||||
|
"region": "Region"
|
||||||
},
|
},
|
||||||
"tab": {
|
"tab": {
|
||||||
"overview": "Overview",
|
"overview": "Overview",
|
||||||
@@ -2352,7 +2355,15 @@
|
|||||||
"deployments": "Deployments",
|
"deployments": "Deployments",
|
||||||
"services": "Services",
|
"services": "Services",
|
||||||
"ingresses": "Ingresses",
|
"ingresses": "Ingresses",
|
||||||
"events": "Events"
|
"events": "Events",
|
||||||
|
"statistics": "Statistics",
|
||||||
|
"cronJobs": "CronJobs",
|
||||||
|
"configMap": "ConfigMaps",
|
||||||
|
"secret": "Secrets",
|
||||||
|
"role": "Roles",
|
||||||
|
"roleBinding": "RoleBindings",
|
||||||
|
"serviceAccount": "Service Accounts",
|
||||||
|
"pvc": "PVCs"
|
||||||
},
|
},
|
||||||
"column": {
|
"column": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@@ -2703,7 +2714,13 @@
|
|||||||
"deploymentDeleted": "{{name}} deleted",
|
"deploymentDeleted": "{{name}} deleted",
|
||||||
"confirmDeletePod": "Confirm delete Pod \"{{name}}\"? This action cannot be undone.",
|
"confirmDeletePod": "Confirm delete Pod \"{{name}}\"? This action cannot be undone.",
|
||||||
"deploymentNotFound": "Deployment not found: {{name}}",
|
"deploymentNotFound": "Deployment not found: {{name}}",
|
||||||
"imageLabel": "Image"
|
"imageLabel": "Image",
|
||||||
|
"resourceQuota": "Resource Quota",
|
||||||
|
"limitRange": "Limit Range",
|
||||||
|
"pdb": "Pod Disruption Budget",
|
||||||
|
"editYaml": "Edit YAML",
|
||||||
|
"imageUpdated": "Image updated successfully",
|
||||||
|
"hpaDeleted": "HPA deleted"
|
||||||
},
|
},
|
||||||
"podLogs": {
|
"podLogs": {
|
||||||
"noLogs": "No logs",
|
"noLogs": "No logs",
|
||||||
@@ -2726,7 +2743,11 @@
|
|||||||
"loading": "Loading…",
|
"loading": "Loading…",
|
||||||
"fetchFailed": "Failed to fetch logs",
|
"fetchFailed": "Failed to fetch logs",
|
||||||
"streamEnded": "Stream ended",
|
"streamEnded": "Stream ended",
|
||||||
"streamStopped": "Stream stopped"
|
"streamStopped": "Stream stopped",
|
||||||
|
"download": "Download Logs",
|
||||||
|
"searchPlaceholder": "Search logs...",
|
||||||
|
"previousLogs": "View previous (crashed) container logs",
|
||||||
|
"keywordFilter": "Keyword filter…"
|
||||||
},
|
},
|
||||||
"editMode": {
|
"editMode": {
|
||||||
"basic": "Basic Info",
|
"basic": "Basic Info",
|
||||||
@@ -2791,7 +2812,8 @@
|
|||||||
"minReplicas": "Min Replicas",
|
"minReplicas": "Min Replicas",
|
||||||
"maxReplicas": "Max Replicas",
|
"maxReplicas": "Max Replicas",
|
||||||
"cpuTarget": "CPU Target (%)",
|
"cpuTarget": "CPU Target (%)",
|
||||||
"memTarget": "Memory Target (%)"
|
"memTarget": "Memory Target (%)",
|
||||||
|
"targetCPU": "Target CPU Utilization"
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
"redirecting": "Redirecting..."
|
"redirecting": "Redirecting..."
|
||||||
@@ -2889,7 +2911,14 @@
|
|||||||
"colMessage": "Message",
|
"colMessage": "Message",
|
||||||
"nameLabel": "Event name:",
|
"nameLabel": "Event name:",
|
||||||
"totalCount": "Total {{count}} events",
|
"totalCount": "Total {{count}} events",
|
||||||
"virtualScroll": "virtual scroll rendering"
|
"virtualScroll": "virtual scroll rendering",
|
||||||
|
"center": "Event Center",
|
||||||
|
"clusterAlerts": "Cluster Events & Alerts",
|
||||||
|
"normal": "Normal Events",
|
||||||
|
"warning": "Warning Events",
|
||||||
|
"warningType": "Warning Type",
|
||||||
|
"resourceType": "Resource Type",
|
||||||
|
"warningReason": "Warning Reason"
|
||||||
},
|
},
|
||||||
"yamlImport": {
|
"yamlImport": {
|
||||||
"empty": "Please enter YAML",
|
"empty": "Please enter YAML",
|
||||||
@@ -3005,7 +3034,10 @@
|
|||||||
"shareProcessDesc": "Share process namespace",
|
"shareProcessDesc": "Share process namespace",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"saved": "update submitted",
|
"saved": "update submitted",
|
||||||
"secondsPlaceholder": "seconds"
|
"secondsPlaceholder": "seconds",
|
||||||
|
"maxSurge": "Max Surge",
|
||||||
|
"maxUnavailable": "Max Unavailable",
|
||||||
|
"strategyLabel": "Update Strategy"
|
||||||
},
|
},
|
||||||
"editBasic": {
|
"editBasic": {
|
||||||
"synced": "Synced",
|
"synced": "Synced",
|
||||||
@@ -3062,7 +3094,12 @@
|
|||||||
"file": "File",
|
"file": "File",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"readFailed": "Read failed",
|
"readFailed": "Read failed",
|
||||||
"uploadFailed": "Upload failed"
|
"uploadFailed": "Upload failed",
|
||||||
|
"confirmDelete": "Confirm Delete",
|
||||||
|
"scaleTo": "Scale to",
|
||||||
|
"restartConfirm": "Are you sure you want to restart this workload?",
|
||||||
|
"deleteWarning": "This action cannot be undone",
|
||||||
|
"yamlDocError": "Each YAML document must contain kind and metadata.name"
|
||||||
},
|
},
|
||||||
"namespaceResource": {
|
"namespaceResource": {
|
||||||
"delete1": "确认删除 Pod \"",
|
"delete1": "确认删除 Pod \"",
|
||||||
@@ -3085,14 +3122,17 @@
|
|||||||
"close1": "Close",
|
"close1": "Close",
|
||||||
"container1": "正在进入容器...",
|
"container1": "正在进入容器...",
|
||||||
"enterPod": "Enter Pod container to execute commands",
|
"enterPod": "Enter Pod container to execute commands",
|
||||||
"connectErrorAnsi": "\\r\\n\\x1b[31m[连接发生错误]\\x1b[0m\\r\\n"
|
"connectErrorAnsi": "\\r\\n\\x1b[31m[连接发生错误]\\x1b[0m\\r\\n",
|
||||||
|
"shell": "Shell"
|
||||||
},
|
},
|
||||||
"podMetrics": {
|
"podMetrics": {
|
||||||
"resources2": "Pod 资源用量",
|
"resources2": "Pod 资源用量",
|
||||||
"container2": "容器 CPU/内存实时指标",
|
"container2": "容器 CPU/内存实时指标",
|
||||||
"cpu": "总 CPU",
|
"cpu": "总 CPU",
|
||||||
"memory": "总内存",
|
"memory": "总内存",
|
||||||
"namespace": "所有命名空间"
|
"namespace": "所有命名空间",
|
||||||
|
"cpuUsage": "CPU Usage",
|
||||||
|
"memUsage": "Memory Usage"
|
||||||
},
|
},
|
||||||
"resourceEditor": {
|
"resourceEditor": {
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
@@ -3133,7 +3173,11 @@
|
|||||||
"servicePort": "Service Port",
|
"servicePort": "Service Port",
|
||||||
"noSelector": "(No selector)",
|
"noSelector": "(No selector)",
|
||||||
"confirmDelete": "Confirm Delete",
|
"confirmDelete": "Confirm Delete",
|
||||||
"deleteDesc": "This operation cannot be undone"
|
"deleteDesc": "This operation cannot be undone",
|
||||||
|
"portName": "Port Name",
|
||||||
|
"targetPort": "Target Port",
|
||||||
|
"protocol": "Protocol",
|
||||||
|
"serviceCreated": "Service created"
|
||||||
},
|
},
|
||||||
"editContainers": {
|
"editContainers": {
|
||||||
"noManifest": "Manifest data not loaded. Please use YAML editor to modify directly.",
|
"noManifest": "Manifest data not loaded. Please use YAML editor to modify directly.",
|
||||||
@@ -3143,7 +3187,14 @@
|
|||||||
"addNew": "Add New",
|
"addNew": "Add New",
|
||||||
"initContainers": "Init Containers",
|
"initContainers": "Init Containers",
|
||||||
"workContainers": "Work Containers",
|
"workContainers": "Work Containers",
|
||||||
"save": "Save"
|
"save": "Save",
|
||||||
|
"imageRequired": " image is required",
|
||||||
|
"nameRequired": " name is required",
|
||||||
|
"portNameRequired": "Port name is required",
|
||||||
|
"saved": "Container configuration saved",
|
||||||
|
"containerCount": "containers",
|
||||||
|
"secretPlaceholder": "Type Secret name and press Enter",
|
||||||
|
"initDesc": "Containers executed sequentially before main container starts, for environment initialization"
|
||||||
},
|
},
|
||||||
"editStorage": {
|
"editStorage": {
|
||||||
"volumes": "Volumes",
|
"volumes": "Volumes",
|
||||||
@@ -3154,13 +3205,34 @@
|
|||||||
"noContainers": "(No containers)",
|
"noContainers": "(No containers)",
|
||||||
"readOnly": "Read Only",
|
"readOnly": "Read Only",
|
||||||
"readWrite": "Read/Write",
|
"readWrite": "Read/Write",
|
||||||
"save": "Save"
|
"save": "Save",
|
||||||
|
"emptyDir": "EmptyDir (temporary)",
|
||||||
|
"volumeNameRequired": "Volume name is required",
|
||||||
|
"selectConfigMap": "Please select a ConfigMap",
|
||||||
|
"selectSecret": "Please select a Secret",
|
||||||
|
"selectPVC": "Please select a PVC",
|
||||||
|
"typeRequired": " type field is required",
|
||||||
|
"incompleteConfig": "Volume configuration incomplete",
|
||||||
|
"mountPathRequired": "Mount path is required",
|
||||||
|
"saved": "Storage configuration saved",
|
||||||
|
"namePlaceholder": "e.g. app-logs / fluent-bit-config",
|
||||||
|
"mountPathPlaceholder": "Mount path /bitcloud/app/.../logs",
|
||||||
|
"typeRequiredHint": " type required field"
|
||||||
},
|
},
|
||||||
"clusterDialog": {
|
"clusterDialog": {
|
||||||
"nameHint": "Only English letters, numbers and hyphens (-) allowed. Cannot start or end with hyphen. Name will be the cluster unique identifier.",
|
"nameHint": "Only English letters, numbers and hyphens (-) allowed. Cannot start or end with hyphen. Name will be the cluster unique identifier.",
|
||||||
"configured": "Configured (leave empty to keep)",
|
"configured": "Configured (leave empty to keep)",
|
||||||
"kubeconfigHint": "API Server address will be auto-parsed after pasting kubeconfig content",
|
"kubeconfigHint": "API Server address will be auto-parsed after pasting kubeconfig content",
|
||||||
"cancel": "Cancel"
|
"cancel": "Cancel",
|
||||||
|
"nameRule": "Only letters, digits, and hyphens (-) allowed; cannot start or end with a hyphen",
|
||||||
|
"nameInvalid": "Please enter a valid cluster name (letters/digits/hyphens)",
|
||||||
|
"saveFailed": "Failed to save cluster:",
|
||||||
|
"namePlaceholder": "e.g. prod-cluster",
|
||||||
|
"descLabel": "Cluster description",
|
||||||
|
"tokenPlaceholder": "Paste Service Account Token...",
|
||||||
|
"tokenConfigured": "Configured, enter new value to override...",
|
||||||
|
"addTitle": "Add Cluster",
|
||||||
|
"editTitle": "Edit Cluster"
|
||||||
},
|
},
|
||||||
"nodeDetail": {
|
"nodeDetail": {
|
||||||
"nodeTitle": "Node Details",
|
"nodeTitle": "Node Details",
|
||||||
@@ -3184,6 +3256,167 @@
|
|||||||
"podsOnNode": "Pods on Node",
|
"podsOnNode": "Pods on Node",
|
||||||
"noPodsOnNode": "No pods on this node",
|
"noPodsOnNode": "No pods on this node",
|
||||||
"cpuRequest": "CPU Request"
|
"cpuRequest": "CPU Request"
|
||||||
|
},
|
||||||
|
"nsResource": {
|
||||||
|
"deployDesc": "Deployment workload status",
|
||||||
|
"podDesc": "Pod instance status",
|
||||||
|
"serviceDesc": "Service networking",
|
||||||
|
"ingressDesc": "Ingress routing rules",
|
||||||
|
"configMapDesc": "Configuration maps",
|
||||||
|
"secretDesc": "Secret management (name and type only)",
|
||||||
|
"hpaDesc": "Horizontal Pod Autoscaler",
|
||||||
|
"pvcDesc": "Persistent Volume Claims",
|
||||||
|
"stsDesc": "StatefulSets",
|
||||||
|
"dsDesc": "DaemonSets",
|
||||||
|
"saDesc": "Service Accounts (RBAC identity)",
|
||||||
|
"roleDesc": "Namespace-level Roles (RBAC)",
|
||||||
|
"rbDesc": "RoleBindings (RBAC authorization)",
|
||||||
|
"viewYaml": "View YAML",
|
||||||
|
"resourceName": "Name",
|
||||||
|
"namespace": "Namespace"
|
||||||
|
},
|
||||||
|
"nsOverview": {
|
||||||
|
"workloadReplicas": "Workload replica management",
|
||||||
|
"podStatus": "Container instance status",
|
||||||
|
"cronjobManage": "CronJob management",
|
||||||
|
"serviceDiscovery": "Service discovery & load balancing",
|
||||||
|
"externalRoute": "External traffic routing rules",
|
||||||
|
"configMaps": "Configuration maps",
|
||||||
|
"secrets": "Secrets & credentials",
|
||||||
|
"hpaStrategy": "Horizontal autoscaling strategy",
|
||||||
|
"loadingStats": "Loading resource statistics..."
|
||||||
|
},
|
||||||
|
"deploymentWorkload": {
|
||||||
|
"loadFailed": "Failed to load details:",
|
||||||
|
"manifestFailed": "Failed to load manifest (edit degraded):",
|
||||||
|
"restartSent": "Restart command sent",
|
||||||
|
"restarting": "Deployment rolling restart in progress",
|
||||||
|
"scaleSuccess": "Scale successful",
|
||||||
|
"loading": "Loading Deployment...",
|
||||||
|
"create": "New Deployment",
|
||||||
|
"withService": "Includes associated Service",
|
||||||
|
"manifestLoadFailed": "Failed to load manifest (edit degraded):"
|
||||||
|
},
|
||||||
|
"deploymentView": {
|
||||||
|
"rollbackVersion": "Rollback Version",
|
||||||
|
"rollbackSent": "Rollback initiated",
|
||||||
|
"scaleSent": "Scaling submitted",
|
||||||
|
"pods": "Pods",
|
||||||
|
"access": "Access",
|
||||||
|
"scale": "Scale"
|
||||||
|
},
|
||||||
|
"rve": {
|
||||||
|
"visualUnavailable": "Visual editor unavailable",
|
||||||
|
"fallbackYaml": "Unable to parse resource structure, switched to YAML edit mode",
|
||||||
|
"saveFailed": "Save failed",
|
||||||
|
"parseFailed": "Unable to parse YAML",
|
||||||
|
"parseFailedHint": "Please check syntax and continue in YAML mode",
|
||||||
|
"downloaded": "Downloaded",
|
||||||
|
"hostPlaceholder": "Domain (e.g. api.example.com)",
|
||||||
|
"keyPlaceholder": "Key name (e.g. LOG_LEVEL)",
|
||||||
|
"parseYamlFailed": "Unable to parse YAML",
|
||||||
|
"checkSyntax": "Please check syntax and continue in YAML mode",
|
||||||
|
"fallbackToYaml": "Unable to parse resource structure, switched to YAML edit mode"
|
||||||
|
},
|
||||||
|
"workloadView": {
|
||||||
|
"restartSent": "Restart command sent",
|
||||||
|
"restartFailed": "Restart failed",
|
||||||
|
"loading": "Loading workload...",
|
||||||
|
"restarting": "Restarting...",
|
||||||
|
"currentReplicas": "Current Replicas",
|
||||||
|
"desiredSchedule": "Desired Schedule",
|
||||||
|
"serviceName": "Service Name",
|
||||||
|
"podCount": "Pod Count",
|
||||||
|
"actualSchedule": "Actual Schedule"
|
||||||
|
},
|
||||||
|
"pvc": {
|
||||||
|
"sizeLabel": "Size",
|
||||||
|
"accessMode": "Access Mode",
|
||||||
|
"storageClass": "Storage Class",
|
||||||
|
"sizePlaceholder": "e.g. 10Gi",
|
||||||
|
"scPlaceholder": "Leave empty for default StorageClass"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"portName": "Port Name",
|
||||||
|
"portNum": "Port",
|
||||||
|
"targetPort": "Target Port",
|
||||||
|
"protocol": "Protocol",
|
||||||
|
"nodePort": "Node Port",
|
||||||
|
"sessionAffinity": "Session Affinity",
|
||||||
|
"externalTrafficPolicy": "External Traffic Policy",
|
||||||
|
"portLabel": "Port",
|
||||||
|
"selectorKey": "Key (e.g. app)",
|
||||||
|
"selectorValue": "Value (e.g. nginx)"
|
||||||
|
},
|
||||||
|
"ingress": {
|
||||||
|
"host": "Host",
|
||||||
|
"path": "Path",
|
||||||
|
"pathType": "Path Type",
|
||||||
|
"backend": "Backend Service",
|
||||||
|
"backendPort": "Backend Port",
|
||||||
|
"tlsSecret": "TLS Certificate",
|
||||||
|
"pathPlaceholder": "Path /"
|
||||||
|
},
|
||||||
|
"configMap": {
|
||||||
|
"key": "Key",
|
||||||
|
"value": "Value",
|
||||||
|
"addKey": "Add Key-Value",
|
||||||
|
"collapseAll": "Collapse All"
|
||||||
|
},
|
||||||
|
"secret": {
|
||||||
|
"type": "Type",
|
||||||
|
"data": "Data",
|
||||||
|
"addKey": "Add Key",
|
||||||
|
"collapseAll": "Collapse All"
|
||||||
|
},
|
||||||
|
"nodes": {
|
||||||
|
"ready": "Ready",
|
||||||
|
"notReady": "Not Ready",
|
||||||
|
"selectCluster": "Select Cluster",
|
||||||
|
"subtitle": "Kubernetes cluster node status and resources",
|
||||||
|
"noData": "No node data"
|
||||||
|
},
|
||||||
|
"capacityPage": {
|
||||||
|
"title": "Capacity Management"
|
||||||
|
},
|
||||||
|
"nsMgmt": {
|
||||||
|
"status": "Status",
|
||||||
|
"age": "Age",
|
||||||
|
"quota": "Quota",
|
||||||
|
"labels": "Labels",
|
||||||
|
"searchPlaceholder": "Search namespaces...",
|
||||||
|
"title": "Namespace Management",
|
||||||
|
"quotaLimit": "Quota Limits",
|
||||||
|
"nameExample": "e.g. my-app"
|
||||||
|
},
|
||||||
|
"clusterOverview": {
|
||||||
|
"nodeCount": "Nodes",
|
||||||
|
"podCount": "Pods",
|
||||||
|
"serviceCount": "Services",
|
||||||
|
"notFound": "Cluster not found",
|
||||||
|
"title": "Cluster Overview"
|
||||||
|
},
|
||||||
|
"controlBar": {
|
||||||
|
"allNs": "All Namespaces",
|
||||||
|
"searchPlaceholder": "Search resources...",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"create": "Create"
|
||||||
|
},
|
||||||
|
"nsResourcePage": {
|
||||||
|
"loading": "Loading...",
|
||||||
|
"noData": "No data",
|
||||||
|
"exportCsv": "Export CSV",
|
||||||
|
"exportJson": "Export JSON",
|
||||||
|
"exportYaml": "Export namespace YAML (tar.gz)"
|
||||||
|
},
|
||||||
|
"yamlViewer": {
|
||||||
|
"copy": "Copy",
|
||||||
|
"download": "Download",
|
||||||
|
"copied": "Copied",
|
||||||
|
"fetchFailed": "Failed to fetch YAML"
|
||||||
|
},
|
||||||
|
"podFile": {
|
||||||
|
"loadFailed": "Failed to load file list"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"monitor": {
|
"monitor": {
|
||||||
|
|||||||
+247
-14
@@ -2343,7 +2343,10 @@
|
|||||||
"resourceTrend": "资源使用趋势(模拟最近变化)",
|
"resourceTrend": "资源使用趋势(模拟最近变化)",
|
||||||
"refreshMetrics": "刷新节点指标",
|
"refreshMetrics": "刷新节点指标",
|
||||||
"resourceDistribution": "资源分布",
|
"resourceDistribution": "资源分布",
|
||||||
"now": "现在"
|
"now": "现在",
|
||||||
|
"allNamespaces": "全部命名空间",
|
||||||
|
"provider": "提供商",
|
||||||
|
"region": "区域"
|
||||||
},
|
},
|
||||||
"tab": {
|
"tab": {
|
||||||
"overview": "概览",
|
"overview": "概览",
|
||||||
@@ -2352,7 +2355,15 @@
|
|||||||
"deployments": "Deployment",
|
"deployments": "Deployment",
|
||||||
"services": "Service",
|
"services": "Service",
|
||||||
"ingresses": "Ingress",
|
"ingresses": "Ingress",
|
||||||
"events": "事件"
|
"events": "事件",
|
||||||
|
"statistics": "统计",
|
||||||
|
"cronJobs": "定时任务",
|
||||||
|
"configMap": "配置字典",
|
||||||
|
"secret": "密钥",
|
||||||
|
"role": "角色",
|
||||||
|
"roleBinding": "角色绑定",
|
||||||
|
"serviceAccount": "服务账号",
|
||||||
|
"pvc": "存储卷"
|
||||||
},
|
},
|
||||||
"column": {
|
"column": {
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
@@ -2703,7 +2714,13 @@
|
|||||||
"deploymentDeleted": "{{name}} 已删除",
|
"deploymentDeleted": "{{name}} 已删除",
|
||||||
"confirmDeletePod": "确认删除 Pod \"{{name}}\"?操作不可恢复。",
|
"confirmDeletePod": "确认删除 Pod \"{{name}}\"?操作不可恢复。",
|
||||||
"deploymentNotFound": "未找到 Deployment: {{name}}",
|
"deploymentNotFound": "未找到 Deployment: {{name}}",
|
||||||
"imageLabel": "镜像"
|
"imageLabel": "镜像",
|
||||||
|
"resourceQuota": "资源配额",
|
||||||
|
"limitRange": "限制范围",
|
||||||
|
"pdb": "Pod 干扰预算",
|
||||||
|
"editYaml": "编辑 YAML",
|
||||||
|
"imageUpdated": "镜像更新成功",
|
||||||
|
"hpaDeleted": "HPA 已删除"
|
||||||
},
|
},
|
||||||
"podLogs": {
|
"podLogs": {
|
||||||
"noLogs": "无日志",
|
"noLogs": "无日志",
|
||||||
@@ -2726,7 +2743,11 @@
|
|||||||
"loading": "加载中…",
|
"loading": "加载中…",
|
||||||
"fetchFailed": "获取日志失败",
|
"fetchFailed": "获取日志失败",
|
||||||
"streamEnded": "流式传输结束",
|
"streamEnded": "流式传输结束",
|
||||||
"streamStopped": "流式传输已停止"
|
"streamStopped": "流式传输已停止",
|
||||||
|
"download": "下载日志",
|
||||||
|
"searchPlaceholder": "搜索日志...",
|
||||||
|
"previousLogs": "查看上一次(崩溃)容器的日志",
|
||||||
|
"keywordFilter": "关键字过滤…"
|
||||||
},
|
},
|
||||||
"editMode": {
|
"editMode": {
|
||||||
"basic": "基本信息",
|
"basic": "基本信息",
|
||||||
@@ -2791,7 +2812,8 @@
|
|||||||
"minReplicas": "最小副本数",
|
"minReplicas": "最小副本数",
|
||||||
"maxReplicas": "最大副本数",
|
"maxReplicas": "最大副本数",
|
||||||
"cpuTarget": "CPU 目标 (%)",
|
"cpuTarget": "CPU 目标 (%)",
|
||||||
"memTarget": "内存目标 (%)"
|
"memTarget": "内存目标 (%)",
|
||||||
|
"targetCPU": "目标 CPU 使用率"
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
"redirecting": "正在跳转..."
|
"redirecting": "正在跳转..."
|
||||||
@@ -2889,7 +2911,14 @@
|
|||||||
"colMessage": "消息",
|
"colMessage": "消息",
|
||||||
"nameLabel": "事件名:",
|
"nameLabel": "事件名:",
|
||||||
"totalCount": "共 {{count}} 条事件",
|
"totalCount": "共 {{count}} 条事件",
|
||||||
"virtualScroll": "虚拟滚动渲染"
|
"virtualScroll": "虚拟滚动渲染",
|
||||||
|
"center": "事件中心",
|
||||||
|
"clusterAlerts": "集群事件与告警",
|
||||||
|
"normal": "正常事件",
|
||||||
|
"warning": "告警事件",
|
||||||
|
"warningType": "告警类型",
|
||||||
|
"resourceType": "资源类型",
|
||||||
|
"warningReason": "告警原因"
|
||||||
},
|
},
|
||||||
"yamlImport": {
|
"yamlImport": {
|
||||||
"empty": "请输入 YAML",
|
"empty": "请输入 YAML",
|
||||||
@@ -3005,7 +3034,10 @@
|
|||||||
"shareProcessDesc": "共享进程命名空间",
|
"shareProcessDesc": "共享进程命名空间",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"saved": "更新已提交",
|
"saved": "更新已提交",
|
||||||
"secondsPlaceholder": "秒"
|
"secondsPlaceholder": "秒",
|
||||||
|
"maxSurge": "最大超出",
|
||||||
|
"maxUnavailable": "最大不可用",
|
||||||
|
"strategyLabel": "更新策略"
|
||||||
},
|
},
|
||||||
"editBasic": {
|
"editBasic": {
|
||||||
"synced": "已同步",
|
"synced": "已同步",
|
||||||
@@ -3062,7 +3094,12 @@
|
|||||||
"file": "文件",
|
"file": "文件",
|
||||||
"upload": "上传",
|
"upload": "上传",
|
||||||
"readFailed": "读取失败",
|
"readFailed": "读取失败",
|
||||||
"uploadFailed": "上传失败"
|
"uploadFailed": "上传失败",
|
||||||
|
"confirmDelete": "确认删除",
|
||||||
|
"scaleTo": "伸缩至",
|
||||||
|
"restartConfirm": "确定要重启此工作负载吗?",
|
||||||
|
"deleteWarning": "此操作不可恢复",
|
||||||
|
"yamlDocError": "每个 YAML 文档必须包含 kind 和 metadata.name"
|
||||||
},
|
},
|
||||||
"namespaceResource": {
|
"namespaceResource": {
|
||||||
"delete1": "确认删除 Pod \"",
|
"delete1": "确认删除 Pod \"",
|
||||||
@@ -3085,14 +3122,17 @@
|
|||||||
"close1": "关闭",
|
"close1": "关闭",
|
||||||
"container1": "正在进入容器...",
|
"container1": "正在进入容器...",
|
||||||
"enterPod": "进入 Pod 容器执行命令",
|
"enterPod": "进入 Pod 容器执行命令",
|
||||||
"connectErrorAnsi": "\\r\\n\\x1b[31m[连接发生错误]\\x1b[0m\\r\\n"
|
"connectErrorAnsi": "\\r\\n\\x1b[31m[连接发生错误]\\x1b[0m\\r\\n",
|
||||||
|
"shell": "Shell"
|
||||||
},
|
},
|
||||||
"podMetrics": {
|
"podMetrics": {
|
||||||
"resources2": "Pod 资源用量",
|
"resources2": "Pod 资源用量",
|
||||||
"container2": "容器 CPU/内存实时指标",
|
"container2": "容器 CPU/内存实时指标",
|
||||||
"cpu": "总 CPU",
|
"cpu": "总 CPU",
|
||||||
"memory": "总内存",
|
"memory": "总内存",
|
||||||
"namespace": "所有命名空间"
|
"namespace": "所有命名空间",
|
||||||
|
"cpuUsage": "CPU 使用",
|
||||||
|
"memUsage": "内存使用"
|
||||||
},
|
},
|
||||||
"resourceEditor": {
|
"resourceEditor": {
|
||||||
"create": "新建",
|
"create": "新建",
|
||||||
@@ -3133,7 +3173,11 @@
|
|||||||
"servicePort": "Service 端口",
|
"servicePort": "Service 端口",
|
||||||
"noSelector": "(无 selector)",
|
"noSelector": "(无 selector)",
|
||||||
"confirmDelete": "确认删除",
|
"confirmDelete": "确认删除",
|
||||||
"deleteDesc": "此操作不可撤销"
|
"deleteDesc": "此操作不可撤销",
|
||||||
|
"portName": "端口名称",
|
||||||
|
"targetPort": "目标端口",
|
||||||
|
"protocol": "协议",
|
||||||
|
"serviceCreated": "Service 已创建"
|
||||||
},
|
},
|
||||||
"editContainers": {
|
"editContainers": {
|
||||||
"noManifest": "未获取到 manifest 数据,请使用 YAML 编辑模式直接修改。",
|
"noManifest": "未获取到 manifest 数据,请使用 YAML 编辑模式直接修改。",
|
||||||
@@ -3143,7 +3187,14 @@
|
|||||||
"addNew": "新增",
|
"addNew": "新增",
|
||||||
"initContainers": "初始化容器",
|
"initContainers": "初始化容器",
|
||||||
"workContainers": "工作容器",
|
"workContainers": "工作容器",
|
||||||
"save": "保存"
|
"save": "保存",
|
||||||
|
"imageRequired": "的镜像不能为空",
|
||||||
|
"nameRequired": "的名称不能为空",
|
||||||
|
"portNameRequired": "端口名称不能为空",
|
||||||
|
"saved": "容器配置已保存",
|
||||||
|
"containerCount": "个容器",
|
||||||
|
"secretPlaceholder": "输入 Secret 名称后回车",
|
||||||
|
"initDesc": "在主容器启动前按顺序执行的容器,用于初始化环境"
|
||||||
},
|
},
|
||||||
"editStorage": {
|
"editStorage": {
|
||||||
"volumes": "数据卷",
|
"volumes": "数据卷",
|
||||||
@@ -3154,13 +3205,34 @@
|
|||||||
"noContainers": "(无容器)",
|
"noContainers": "(无容器)",
|
||||||
"readOnly": "只读",
|
"readOnly": "只读",
|
||||||
"readWrite": "读写",
|
"readWrite": "读写",
|
||||||
"save": "保存"
|
"save": "保存",
|
||||||
|
"emptyDir": "临时目录(EmptyDir)",
|
||||||
|
"volumeNameRequired": "请填写数据卷名称",
|
||||||
|
"selectConfigMap": "请选择 ConfigMap",
|
||||||
|
"selectSecret": "请选择 Secret",
|
||||||
|
"selectPVC": "请选择 PVC",
|
||||||
|
"typeRequired": "的类型必填项",
|
||||||
|
"incompleteConfig": "数据卷配置不完整",
|
||||||
|
"mountPathRequired": "请填写挂载路径",
|
||||||
|
"saved": "存储配置已保存",
|
||||||
|
"namePlaceholder": "如 app-logs / fluent-bit-config",
|
||||||
|
"mountPathPlaceholder": "挂载路径 /bitcloud/app/.../logs",
|
||||||
|
"typeRequiredHint": "的类型必填项"
|
||||||
},
|
},
|
||||||
"clusterDialog": {
|
"clusterDialog": {
|
||||||
"nameHint": "只允许英文、数字和连字符(-),首尾不能是连字符。名称将作为集群唯一标识。",
|
"nameHint": "只允许英文、数字和连字符(-),首尾不能是连字符。名称将作为集群唯一标识。",
|
||||||
"configured": "已配置(留空不修改)",
|
"configured": "已配置(留空不修改)",
|
||||||
"kubeconfigHint": "粘贴 kubeconfig 内容后将自动解析 API Server 地址",
|
"kubeconfigHint": "粘贴 kubeconfig 内容后将自动解析 API Server 地址",
|
||||||
"cancel": "取消"
|
"cancel": "取消",
|
||||||
|
"nameRule": "只允许英文、数字和连字符(-),首尾不能是连字符",
|
||||||
|
"nameInvalid": "请输入有效的集群名称(英文/数字/连字符)",
|
||||||
|
"saveFailed": "保存集群失败:",
|
||||||
|
"namePlaceholder": "如: prod-cluster",
|
||||||
|
"descLabel": "集群用途描述",
|
||||||
|
"tokenPlaceholder": "粘贴 Service Account Token...",
|
||||||
|
"tokenConfigured": "已配置,输入新值覆盖...",
|
||||||
|
"addTitle": "添加集群",
|
||||||
|
"editTitle": "编辑集群"
|
||||||
},
|
},
|
||||||
"nodeDetail": {
|
"nodeDetail": {
|
||||||
"nodeTitle": "节点详情",
|
"nodeTitle": "节点详情",
|
||||||
@@ -3184,6 +3256,167 @@
|
|||||||
"podsOnNode": "节点上的 Pod",
|
"podsOnNode": "节点上的 Pod",
|
||||||
"noPodsOnNode": "该节点上暂无 Pod",
|
"noPodsOnNode": "该节点上暂无 Pod",
|
||||||
"cpuRequest": "CPU 请求"
|
"cpuRequest": "CPU 请求"
|
||||||
|
},
|
||||||
|
"nsResource": {
|
||||||
|
"deployDesc": "Deployment 工作负载运行状态",
|
||||||
|
"podDesc": "Pod 实例运行状态",
|
||||||
|
"serviceDesc": "Service 网络服务",
|
||||||
|
"ingressDesc": "Ingress 路由规则",
|
||||||
|
"configMapDesc": "配置字典",
|
||||||
|
"secretDesc": "密钥管理(仅名称与类型)",
|
||||||
|
"hpaDesc": "水平自动伸缩",
|
||||||
|
"pvcDesc": "存储卷声明",
|
||||||
|
"stsDesc": "有状态应用",
|
||||||
|
"dsDesc": "守护进程集",
|
||||||
|
"saDesc": "服务账号(RBAC 身份标识)",
|
||||||
|
"roleDesc": "命名空间级角色权限(RBAC)",
|
||||||
|
"rbDesc": "角色绑定关系(RBAC 授权)",
|
||||||
|
"viewYaml": "查看YAML",
|
||||||
|
"resourceName": "名称",
|
||||||
|
"namespace": "命名空间"
|
||||||
|
},
|
||||||
|
"nsOverview": {
|
||||||
|
"workloadReplicas": "工作负载副本管理",
|
||||||
|
"podStatus": "容器实例运行状态",
|
||||||
|
"cronjobManage": "定时任务管理",
|
||||||
|
"serviceDiscovery": "服务发现与负载均衡",
|
||||||
|
"externalRoute": "外部流量路由规则",
|
||||||
|
"configMaps": "配置字典",
|
||||||
|
"secrets": "密钥与凭证",
|
||||||
|
"hpaStrategy": "水平自动伸缩策略",
|
||||||
|
"loadingStats": "加载资源统计..."
|
||||||
|
},
|
||||||
|
"deploymentWorkload": {
|
||||||
|
"loadFailed": "加载详情失败:",
|
||||||
|
"manifestFailed": "加载 manifest 失败(编辑功能降级):",
|
||||||
|
"restartSent": "重启指令已下发",
|
||||||
|
"restarting": "Deployment 正在滚动重启",
|
||||||
|
"scaleSuccess": "扩缩容成功",
|
||||||
|
"loading": "加载 Deployment...",
|
||||||
|
"create": "新建 Deployment",
|
||||||
|
"withService": "含关联 Service",
|
||||||
|
"manifestLoadFailed": "加载 manifest 失败(编辑功能降级):"
|
||||||
|
},
|
||||||
|
"deploymentView": {
|
||||||
|
"rollbackVersion": "回滚版本",
|
||||||
|
"rollbackSent": "回滚已下发",
|
||||||
|
"scaleSent": "伸缩已提交",
|
||||||
|
"pods": "容器组",
|
||||||
|
"access": "访问方式",
|
||||||
|
"scale": "容器伸缩"
|
||||||
|
},
|
||||||
|
"rve": {
|
||||||
|
"visualUnavailable": "可视化编辑不可用",
|
||||||
|
"fallbackYaml": "无法解析资源结构,已切换到 YAML 编辑模式",
|
||||||
|
"saveFailed": "保存失败",
|
||||||
|
"parseFailed": "无法解析 YAML",
|
||||||
|
"parseFailedHint": "请检查语法后继续用 YAML 模式",
|
||||||
|
"downloaded": "已下载",
|
||||||
|
"hostPlaceholder": "域名 (如 api.example.com)",
|
||||||
|
"keyPlaceholder": "键名 (如 LOG_LEVEL)",
|
||||||
|
"parseYamlFailed": "无法解析 YAML",
|
||||||
|
"checkSyntax": "请检查语法后继续用 YAML 模式",
|
||||||
|
"fallbackToYaml": "无法解析资源结构,已切换到 YAML 编辑模式"
|
||||||
|
},
|
||||||
|
"workloadView": {
|
||||||
|
"restartSent": "重启指令已下发",
|
||||||
|
"restartFailed": "重启失败",
|
||||||
|
"loading": "加载工作负载...",
|
||||||
|
"restarting": "重启中...",
|
||||||
|
"currentReplicas": "当前副本",
|
||||||
|
"desiredSchedule": "期望调度",
|
||||||
|
"serviceName": "服务名",
|
||||||
|
"podCount": "Pod 数量",
|
||||||
|
"actualSchedule": "当前调度"
|
||||||
|
},
|
||||||
|
"pvc": {
|
||||||
|
"sizeLabel": "容量",
|
||||||
|
"accessMode": "访问模式",
|
||||||
|
"storageClass": "存储类",
|
||||||
|
"sizePlaceholder": "如 10Gi",
|
||||||
|
"scPlaceholder": "留空使用默认 StorageClass"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"portName": "端口名称",
|
||||||
|
"portNum": "端口号",
|
||||||
|
"targetPort": "目标端口",
|
||||||
|
"protocol": "协议",
|
||||||
|
"nodePort": "Node Port",
|
||||||
|
"sessionAffinity": "会话亲和性",
|
||||||
|
"externalTrafficPolicy": "外部流量策略",
|
||||||
|
"portLabel": "端口",
|
||||||
|
"selectorKey": "键 (如 app)",
|
||||||
|
"selectorValue": "值 (如 nginx)"
|
||||||
|
},
|
||||||
|
"ingress": {
|
||||||
|
"host": "域名",
|
||||||
|
"path": "路径",
|
||||||
|
"pathType": "路径类型",
|
||||||
|
"backend": "后端服务",
|
||||||
|
"backendPort": "后端端口",
|
||||||
|
"tlsSecret": "TLS 证书",
|
||||||
|
"pathPlaceholder": "路径 /"
|
||||||
|
},
|
||||||
|
"configMap": {
|
||||||
|
"key": "键",
|
||||||
|
"value": "值",
|
||||||
|
"addKey": "添加键值对",
|
||||||
|
"collapseAll": "折叠全部"
|
||||||
|
},
|
||||||
|
"secret": {
|
||||||
|
"type": "类型",
|
||||||
|
"data": "数据",
|
||||||
|
"addKey": "添加密钥",
|
||||||
|
"collapseAll": "折叠全部"
|
||||||
|
},
|
||||||
|
"nodes": {
|
||||||
|
"ready": "就绪",
|
||||||
|
"notReady": "未就绪",
|
||||||
|
"selectCluster": "选择集群",
|
||||||
|
"subtitle": "Kubernetes 集群工作节点状态与资源",
|
||||||
|
"noData": "暂无节点数据"
|
||||||
|
},
|
||||||
|
"capacityPage": {
|
||||||
|
"title": "容量管理"
|
||||||
|
},
|
||||||
|
"nsMgmt": {
|
||||||
|
"status": "状态",
|
||||||
|
"age": "创建时间",
|
||||||
|
"quota": "配额",
|
||||||
|
"labels": "标签",
|
||||||
|
"searchPlaceholder": "搜索命名空间...",
|
||||||
|
"title": "命名空间管理",
|
||||||
|
"quotaLimit": "配额限制",
|
||||||
|
"nameExample": "例如: my-app"
|
||||||
|
},
|
||||||
|
"clusterOverview": {
|
||||||
|
"nodeCount": "节点数",
|
||||||
|
"podCount": "Pod 数",
|
||||||
|
"serviceCount": "Service 数",
|
||||||
|
"notFound": "集群未找到",
|
||||||
|
"title": "集群概览"
|
||||||
|
},
|
||||||
|
"controlBar": {
|
||||||
|
"allNs": "全部命名空间",
|
||||||
|
"searchPlaceholder": "搜索资源...",
|
||||||
|
"refresh": "刷新",
|
||||||
|
"create": "创建"
|
||||||
|
},
|
||||||
|
"nsResourcePage": {
|
||||||
|
"loading": "加载中...",
|
||||||
|
"noData": "暂无数据",
|
||||||
|
"exportCsv": "导出CSV",
|
||||||
|
"exportJson": "导出JSON",
|
||||||
|
"exportYaml": "导出命名空间 YAML(tar.gz)"
|
||||||
|
},
|
||||||
|
"yamlViewer": {
|
||||||
|
"copy": "复制",
|
||||||
|
"download": "下载",
|
||||||
|
"copied": "已复制",
|
||||||
|
"fetchFailed": "获取YAML失败"
|
||||||
|
},
|
||||||
|
"podFile": {
|
||||||
|
"loadFailed": "加载文件列表失败"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"monitor": {
|
"monitor": {
|
||||||
|
|||||||
Reference in new issue
Block a user