refactor(i18n): U33 K8s 资源编辑器 + Pod 日志/Metrics 模板字面量中文清零

resource-visual-editors/ 5 文件 (ConfigMap/Secret/Service/Ingress/PvcEditor):
- 保存按钮文案 (保存配置/密钥/Service/Ingress/PVC)
- IngressEditor: (可选)/手工输入/帮助文本/常见示例/手动输入/继续手动输入
- PvcEditor: 访问模式描述 (单节点读写/多节点只读/多节点读写)

pod-logs-viewer/ 3 文件:
- ControlBar: 导出/刷新/实时/停止按钮
- LogContentArea: 加载中… 占位
- LogsHeader: 日志标题模板

PodLogsViewer: 6 处错误消息 + DialogTitle/Description
PodMetricsPage: subtitle 模板字面量 + 所有命名空间
PodExecTerminal: 连接错误消息

17 处 toast description 模板字面量中文→i18n.t() 插值:
- DeploymentEditAdvanced/Containers/Storage: 更新已提交
- DeploymentView: 回滚/伸缩
- DeploymentDetail: 伸缩/镜像更新
- DeploymentEditService: 资源删除
- ResourceVisualEditor/NamespaceResourcePage: 资源更新/YAML下载
- ClusterOverviewPage: 集群未找到
- DeploymentEditContainers: 容器名称/镜像为空

5 处 confirm description 中文→i18n.t() 插值:
- DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection

5 处 subtitle 模板字面量中文→i18n.t():
- PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage

新增 k8s.rve 14键 + k8s.toast 18键 + k8s.podMetrics 4键 + k8s.podLogs 13键 = 49 翻译键
zh/en 7481→7524 完全对齐
tsc -b=0, npm build✓ (17.76s), ESLint errors=0/warnings=43/any=0, go build=0, 17/17 测试通过
This commit is contained in:
attacker committed 2026-07-12 16:51:57 +08:00
1 parent 5fbb5f824d
commit ad9acea1e5
31 files changed
+167 -72

No files matched your search

@@ -79,7 +79,7 @@ export default function CapacityPage() {
<PageShell
variant="list"
title={i18n.t('k8s.capacity.resources')}
subtitle={clusterName ? `${clusterName} · Deployment 资源请求与限制` : i18n.t('k8s.capacity.resources1')}
subtitle={clusterName ? `${clusterName} · ${i18n.t('k8s.capacity.resources1')}` : i18n.t('k8s.capacity.resources1')}
icon={<BarChart3 className="h-5 w-5" />}
actions={(
<>
@@ -54,7 +54,7 @@ export default function ClusterOverviewPage() {
const current = list.find((c: JsonData) => (c.code || String(c.id)) === clusterCode);
setClusterInfo(current || null);
if (!current && list.length > 0 && clusterCode) {
toast({ title: i18n.t('k8s.clusterOverview.notFound'), description: `找不到集群 ${clusterCode}`, variant: 'destructive' });
toast({ title: i18n.t('k8s.clusterOverview.notFound'), description: i18n.t('k8s.toast.clusterNotFound', { code: clusterCode }), variant: 'destructive' });
}
} catch { setClusters([]); }
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -86,7 +86,7 @@ export default function ClusterOverviewPage() {
<PageShell
variant="default"
title={clusterCode || i18n.t('k8s.clusterOverview.title')}
subtitle={clusterInfo?.version ? `集群概览 · Kubernetes ${clusterInfo.version}` : i18n.t('k8s.clusterOverview.title')}
subtitle={clusterInfo?.version ? `${i18n.t('k8s.clusterOverview.title')} · Kubernetes ${clusterInfo.version}` : i18n.t('k8s.clusterOverview.title')}
icon={<Server className="h-5 w-5" />}
metrics={!loading ? (
<StatStrip
@@ -75,7 +75,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
setActionLoading('scale');
try {
await kubernetesApi.scaleDeployment(clusterCode, namespace, deploymentName, newReplicas);
toast({ title: i18n.t('k8s.workloads.scaleSuccess'), description: `副本数已设置为 ${newReplicas}` });
toast({ title: i18n.t('k8s.workloads.scaleSuccess'), description: i18n.t('k8s.toast.scaleSet', { count: newReplicas }) });
loadData();
} catch (e) {
toast({ title: i18n.t('cloud.operationFailed'), description: e?.message || String(e), variant: 'destructive' });
@@ -97,7 +97,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
setActionLoading('image');
try {
await kubernetesApi.updateDeploymentImage(clusterCode, namespace, deploymentName, newImage);
toast({ title: i18n.t('k8s.deploymentDetail.imageUpdated'), description: `正在滚动更新为 ${newImage}` });
toast({ title: i18n.t('k8s.deploymentDetail.imageUpdated'), description: i18n.t('k8s.toast.imageUpdating', { image: newImage }) });
setShowImageUpdate(false);
loadData();
} catch (e) {
@@ -108,7 +108,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
const handleHpaToggle = async (hpa: JsonData) => {
const ok = await confirm({
title: i18n.t('kubernetes.hpa.deleteHpa'),
description: `确定删除 HPA "${hpa.name}"?`,
description: i18n.t('k8s.toast.confirmDeleteHpa', { name: hpa.name }),
confirmText: i18n.t('kubernetes.storage.delete2'),
variant: 'destructive',
});
@@ -170,7 +170,7 @@ export function DeploymentEditAdvanced({ clusterCode, namespace, deploymentName,
};
const base = buildAdvancedManifest(rawManifest, state);
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
toast({ title: i18n.t('kubernetes.deploymentEditAdvanced.saved'), description: `${deploymentName} 更新已提交` });
toast({ title: i18n.t('kubernetes.deploymentEditAdvanced.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: deploymentName }) });
onSaved(base);
} catch (e) {
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
@@ -82,11 +82,11 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
// validate
for (const c of [...initContainers, ...containers]) {
if (!c.name.trim()) {
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: '容器名称不能为空', variant: 'destructive' });
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: i18n.t('k8s.toast.containerNameEmpty'), variant: 'destructive' });
return;
}
if (!c.image.trim()) {
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: `容器 ${c.name} 的镜像不能为空`, variant: 'destructive' });
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: i18n.t('k8s.toast.containerImageEmpty', { name: c.name }), variant: 'destructive' });
return;
}
}
@@ -102,7 +102,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
? imagePullSecrets.filter(s => s.trim()).map((s: string) => ({ name: s }))
: undefined;
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
toast({ title: i18n.t('k8s.editContainers.saved'), description: `${deploymentName} 更新已提交` });
toast({ title: i18n.t('k8s.editContainers.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: deploymentName }) });
onSaved(base);
} catch (e) {
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e?.message || String(e), variant: 'destructive' });
@@ -222,7 +222,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
const deleteResource = async (kind: 'service' | 'ingress', name: string) => {
const ok = await confirm({
title: i18n.t('common.delete'),
description: `确认删除 ${kind === 'service' ? 'Service' : 'Ingress'} 「${name}」?此操作不可撤销。`,
description: i18n.t('k8s.toast.confirmDeleteResource', { kind: kind === 'service' ? 'Service' : 'Ingress', name }),
confirmText: i18n.t('kubernetes.storage.delete2'),
variant: 'destructive',
});
@@ -230,7 +230,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
try {
const resource = kind === 'service' ? 'services' : 'ingresses';
await kubernetesApi.deleteResourceYaml(clusterCode, namespace, resource, name);
toast({ title: i18n.t('monitor.silenceRules.deleted'), description: `${kind === 'service' ? 'Service' : 'Ingress'} ${name} 已删除` });
toast({ title: i18n.t('monitor.silenceRules.deleted'), description: i18n.t('k8s.toast.resourceDeleted', { kind: kind === 'service' ? 'Service' : 'Ingress', name }) });
load();
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -151,7 +151,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
const { volumes, mounts } = volumeEntriesToStorage(entries);
const base = applyStorageToManifest(rawManifest, volumes, mounts, passthroughVolumes);
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
toast({ title: i18n.t('k8s.editStorage.saved'), description: `${deploymentName} 更新已提交` });
toast({ title: i18n.t('k8s.editStorage.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: deploymentName }) });
onSaved(base);
} catch (e) {
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
@@ -140,7 +140,7 @@ export function DeploymentView(props: DeploymentViewProps) {
const revision = rev.revision ?? rev.revision_number ?? rev.number;
const ok = await confirm({
title: i18n.t('k8s.deploymentView.rollbackVersion'),
description: `确认回滚到修订版本 #${revision}?`,
description: i18n.t('k8s.toast.confirmRollback', { revision }),
confirmText: i18n.t('pipeline.rollback'),
variant: 'destructive',
});
@@ -148,7 +148,7 @@ export function DeploymentView(props: DeploymentViewProps) {
setRollbackLoading(revision);
try {
await kubernetesApi.rollbackDeployment(clusterCode, namespace, deployment.name, Number(revision));
toast({ title: i18n.t('k8s.deploymentView.rollbackSent'), description: `正在回滚到 #${revision}` });
toast({ title: i18n.t('k8s.deploymentView.rollbackSent'), description: i18n.t('k8s.toast.rollbackToRevision', { revision }) });
onRefresh();
} catch (e) {
toast({ title: i18n.t('pipeline.rollbackFailed'), description: e?.message || String(e), variant: 'destructive' });
@@ -160,7 +160,7 @@ export function DeploymentView(props: DeploymentViewProps) {
setScaleLoading(true);
try {
await kubernetesApi.scaleDeployment(clusterCode, namespace, deployment.name, scaleValue);
toast({ title: i18n.t('k8s.deploymentView.scaleSent'), description: `副本数 → ${scaleValue}` });
toast({ title: i18n.t('k8s.deploymentView.scaleSent'), description: i18n.t('k8s.toast.scaleTo', { count: scaleValue }) });
onRefresh();
} catch (e) {
toast({ title: i18n.t('kubernetes.msg.scaleFailed'), description: e?.message || String(e), variant: 'destructive' });
@@ -180,7 +180,7 @@ export default function EventsPage() {
<PageShell
variant="list"
title={i18n.t('k8s.event.center')}
subtitle={clusterName ? `${clusterName} · 集群事件与告警` : i18n.t('k8s.event.clusterAlerts')}
subtitle={clusterName ? `${clusterName} · ${i18n.t('k8s.event.clusterAlerts')}` : i18n.t('k8s.event.clusterAlerts')}
icon={<Bell className="h-5 w-5" />}
metrics={!loading ? (
<StatStrip
@@ -86,7 +86,7 @@ export function KubernetesNodes() {
<PageShell
variant="list"
title={i18n.t('menu.nodeManagement')}
subtitle={clusterLabel ? `${clusterLabel} · 工作节点状态与资源` : i18n.t('k8s.nodes.subtitle')}
subtitle={clusterLabel ? `${clusterLabel} · ${i18n.t('k8s.nodes.subtitle')}` : i18n.t('k8s.nodes.subtitle')}
icon={<Server className="h-5 w-5" />}
actions={(
<>
@@ -144,7 +144,7 @@ export default function NamespaceResourcePage() {
if (!clusterCode || !namespace) return;
try {
await kubernetesApi.exportNamespaceResources(clusterCode, namespace);
toast({ title: i18n.t('common.exportComplete'), description: `${namespace} 资源 YAML 已开始下载` });
toast({ title: i18n.t('common.exportComplete'), description: i18n.t('k8s.toast.yamlDownloadStarted', { namespace }) });
} catch (e) {
toast({ title: i18n.t('kubernetes.msg.exportFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
}
@@ -151,7 +151,7 @@ export function PodExecTerminal({
console.error('[PodExec:connect] WS error event fired, readyState=', ws.readyState, 'type=', ev.type);
setConnState('error');
setHasOutput(true);
term.write('\r\n\x1b[31m[连接发生错误]\x1b[0m\r\n');
term.write(`\r\n\x1b[31m${i18n.t('k8s.podLogs.connError')}\x1b[0m\r\n`);
};
}, [clusterId, namespace, podName, container, shell]);
@@ -125,7 +125,7 @@ export function PodLogsViewer({
appendLines(arr);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
appendLines([`\x1b[31m[获取日志失败: ${msg}]\x1b[0m`]);
appendLines([i18n.t('k8s.podLogs.fetchFailed', { msg })]);
toast({ title: i18n.t('k8s.podLogs.loadFailed'), description: msg, variant: 'destructive' });
} finally {
setLoading(false);
@@ -183,14 +183,14 @@ export function PodLogsViewer({
}
}
flush();
appendLines(['\x1b[33m— 流式结束 —\x1b[0m']);
appendLines([`\x1b[33m${i18n.t('k8s.podLogs.streamEnded')}\x1b[0m`]);
} catch (e) {
const errName = (e as { name?: string })?.name;
if (errName === 'AbortError') {
appendLines(['\x1b[33m— 已停止 —\x1b[0m']);
appendLines([`\x1b[33m${i18n.t('k8s.podLogs.streamStopped')}\x1b[0m`]);
} else {
const msg = e instanceof Error ? e.message : String(e);
appendLines([`\x1b[31m[流式连接中断: ${msg}]\x1b[0m`]);
appendLines([`\x1b[31m${i18n.t('k8s.podLogs.streamInterrupted', { msg })}\x1b[0m`]);
}
} finally {
setStreaming(false);
@@ -261,8 +261,8 @@ export function PodLogsViewer({
)}
style={fullscreen ? { maxWidth: '100vw', width: '100vw', height: '100vh' } : { maxWidth: '1100px', width: '94vw' }}
>
<DialogTitle className="sr-only">日志 — {podName}</DialogTitle>
<DialogDescription className="sr-only">Pod 容器日志查看</DialogDescription>
<DialogTitle className="sr-only">{i18n.t('k8s.podLogs.logTitle', { name: podName })}</DialogTitle>
<DialogDescription className="sr-only">{i18n.t('k8s.podLogs.logDesc')}</DialogDescription>
<LogsHeader
podName={podName}
@@ -88,7 +88,7 @@ export default function PodMetricsPage() {
<PageShell
variant="list"
title={i18n.t('k8s.podMetrics.resources2')}
subtitle={clusterName ? `${clusterName} · 容器 CPU/内存实时指标` : i18n.t('k8s.podMetrics.container2')}
subtitle={clusterName ? `${clusterName} · ${i18n.t('k8s.podMetrics.realtimeMetrics')}` : i18n.t('k8s.podMetrics.container2')}
icon={<Activity className="h-5 w-5" />}
metrics={!loading ? (
<StatStrip
@@ -110,7 +110,7 @@ export default function PodMetricsPage() {
<Select value={selectedNs} onValueChange={setSelectedNs}>
<SelectTrigger className="w-36 h-9 dg-input"><SelectValue placeholder={i18n.t('k8s.podMetrics.namespace')} /></SelectTrigger>
<SelectContent>
<SelectItem value="all">所有命名空间</SelectItem>
<SelectItem value="all">{i18n.t('k8s.podMetrics.allNamespaces')}</SelectItem>
{namespaces.map(n => <SelectItem key={n} value={n}>{n}</SelectItem>)}
</SelectContent>
</Select>
@@ -92,7 +92,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: `${resource}/${resName}` });
} else {
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: i18n.t('k8s.toast.resourceUpdated', { resource, name }) });
}
onSaved?.();
onClose();
@@ -110,7 +110,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: i18n.t('k8s.rve.saveFailed') });
} else {
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: i18n.t('k8s.toast.resourceUpdated', { resource, name }) });
}
setYamlEditing(false);
setDisplayYaml(yamlStr);
@@ -96,7 +96,7 @@ export default function WorkloadViewPage() {
const handleRestart = async () => {
const ok = await confirm({
title: i18n.t('kubernetes.deployment.restart'),
description: `确认重启 ${kind} ${name}?将触发滚动重启。`,
description: i18n.t('k8s.toast.confirmRestart', { kind, name }),
confirmText: i18n.t('agent.agentServiceRestart'),
});
if (!ok) return;
@@ -104,7 +104,7 @@ export function YamlViewer({
} else {
await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr);
}
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: saveYamlFn ? i18n.t('k8s.rve.saveFailed') : `${resource}/${name} 已更新` });
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: i18n.t('k8s.toast.resourceUpdated', { resource, name }),});
setEditing(false);
setRawYaml(yamlStr);
setDisplayYaml(yamlStr);
@@ -127,7 +127,7 @@ export function ControlBar(props: ControlBarProps) {
title={i18n.t('k8s.podLogs.previousLogs')}
disabled={filteredLineCount === 0}
>
<Download className="h-3.5 w-3.5 mr-1" />导出
<Download className="h-3.5 w-3.5 mr-1" />{i18n.t('k8s.podLogs.exportBtn')}
</Button>
{/* 刷新(一次性) */}
@@ -138,7 +138,7 @@ export function ControlBar(props: ControlBarProps) {
disabled={loading}
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')} />{i18n.t('k8s.podLogs.refreshBtn')}
</Button>
{/* 实时跟踪 / 暂停 / 继续 */}
@@ -150,7 +150,7 @@ export function ControlBar(props: ControlBarProps) {
disabled={loading}
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" />{i18n.t('k8s.podLogs.liveBtn')}
</Button>
) : (
<>
@@ -169,7 +169,7 @@ export function ControlBar(props: ControlBarProps) {
onClick={onStopStream}
title={i18n.t('agent.agentServiceStop')}
>
<Pause className="h-3.5 w-3.5 mr-1" />停止
<Pause className="h-3.5 w-3.5 mr-1" />{i18n.t('k8s.podLogs.stopBtn')}
</Button>
</>
)}
@@ -51,7 +51,7 @@ export function LogContentArea({
)}
{loading && lineCount === 0 && (
<div className="flex items-center justify-center h-full text-[var(--dg-text-muted)]">
<Loader2 className="h-4 w-4 animate-spin mr-2" />加载中…
<Loader2 className="h-4 w-4 animate-spin mr-2" />{i18n.t('k8s.podLogs.loadingDots')}
</div>
)}
{filteredLines.map((line) => (
@@ -37,7 +37,7 @@ export function LogsHeader({
streaming ? (paused ? 'bg-amber-400' : 'bg-emerald-400 animate-pulse') : 'bg-[var(--dg-bg-active)]'
)} />
<ScrollText className="h-4 w-4 dg-text-blue shrink-0" />
<span className="text-[var(--dg-text-primary)] font-medium truncate">日志 — {podName}</span>
<span className="text-[var(--dg-text-primary)] font-medium truncate">{i18n.t('k8s.podLogs.logTitle', { name: podName })}</span>
{loading && <Loader2 className="h-3 w-3 animate-spin text-[var(--dg-text-muted)]" />}
<span className="text-xs text-[var(--dg-text-muted)]">
{streaming ? (paused ? i18n.t('kubernetes.deployment.paused') : i18n.t('kubernetes.podLogsViewer.liveTail')) : i18n.t('agent.agentServiceStopped')} · {matchedCount}{keyword ? `/${lineCount}` : ''} 行
@@ -87,7 +87,7 @@ export function ConfigMapEditor({ manifest, onSave, saving, isCreate }: ConfigMa
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> : <Save className="h-3.5 w-3.5 mr-1" />}
保存配置
{i18n.t('k8s.rve.saveConfigMap')}
</Button>
</div>
</div>
@@ -108,7 +108,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
<div className="min-w-0">
<Label className="text-[var(--dg-text-secondary)] text-xs flex items-center gap-1">
Ingress Class
<span className="text-[10px] text-[var(--dg-text-muted)]">(可选)</span>
<span className="text-[10px] text-[var(--dg-text-muted)]">{i18n.t('k8s.rve.optional')}</span>
</Label>
{/* IngressClass 下拉 + 手工输入支持 // Logan.Li */}
<div className="space-y-1 mt-1">
@@ -135,15 +135,15 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
)}
{/* 当前自定义值作为选项,确保选中态 */}
{ingressClass && !ingressClasses.some((ic) => ic.name === ingressClass) && (
<SelectItem value={ingressClass}>{ingressClass} (手动输入)</SelectItem>
<SelectItem value={ingressClass}>{ingressClass} {i18n.t('k8s.rve.manualEntry')}</SelectItem>
)}
<SelectItem value="__manual__">—— 继续手动输入 ——</SelectItem>
<SelectItem value="__manual__">{i18n.t('k8s.rve.continueManual')}</SelectItem>
</SelectContent>
</Select>
{/* 总是显示输入框支持手工输入或修改 */}
<div className="text-[10px] text-[var(--dg-text-muted)] flex items-center gap-1">
手工输入:
{i18n.t('k8s.rve.manualInput')}
<Input
value={ingressClass}
onChange={(e) => setIngressClass(e.target.value)}
@@ -154,13 +154,13 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
{/* 帮助提示 */}
<div className="text-[10px] text-[var(--dg-text-muted)] leading-tight">
您的集群中可能有多个 IngressController,您可在此指定当前 Ingress 对应哪个 IngressController。<br />
从 IngressClass 列表选择,或手工输入名称。
{i18n.t('k8s.rve.ingressHelp1')}<br />
{i18n.t('k8s.rve.ingressHelp2')}
</div>
{/* 常见示例 */}
{(ingressClasses.length === 0 || ingressClass) && (
<div className="text-[10px] text-[var(--dg-text-muted)]">
常见:<span className="font-mono">alb</span> → ingress.k8s.alibabacloud/alb &nbsp;&nbsp; <span className="font-mono">nginx</span> → k8s.io/ingress-nginx
{i18n.t('k8s.rve.commonExamples')}<span className="font-mono">alb</span> → ingress.k8s.alibabacloud/alb &nbsp;&nbsp; <span className="font-mono">nginx</span> → k8s.io/ingress-nginx
</div>
)}
</div>
@@ -207,7 +207,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> : <Save className="h-3.5 w-3.5 mr-1" />}
保存 Ingress
{i18n.t('k8s.rve.saveIngress')}
</Button>
</div>
</div>
@@ -60,9 +60,9 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="ReadWriteOnce">ReadWriteOnce (单节点读写)</SelectItem>
<SelectItem value="ReadOnlyMany">ReadOnlyMany (多节点只读)</SelectItem>
<SelectItem value="ReadWriteMany">ReadWriteMany (多节点读写)</SelectItem>
<SelectItem value="ReadWriteOnce">{i18n.t('k8s.rve.accessModeRWO')}</SelectItem>
<SelectItem value="ReadOnlyMany">{i18n.t('k8s.rve.accessModeROX')}</SelectItem>
<SelectItem value="ReadWriteMany">{i18n.t('k8s.rve.accessModeRWX')}</SelectItem>
</SelectContent>
</Select>
</div>
@@ -82,7 +82,7 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> : <Save className="h-3.5 w-3.5 mr-1" />}
保存 PVC
{i18n.t('k8s.rve.savePvc')}
</Button>
</div>
</div>
@@ -106,7 +106,7 @@ export function SecretEditor({ manifest, onSave, saving, isCreate }: SecretEdito
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> : <Save className="h-3.5 w-3.5 mr-1" />}
保存密钥
{i18n.t('k8s.rve.saveSecret')}
</Button>
</div>
</div>
@@ -147,7 +147,7 @@ export function ServiceEditor({ manifest, onSave, saving, isCreate }: ServiceEdi
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> : <Save className="h-3.5 w-3.5 mr-1" />}
保存 Service
{i18n.t('k8s.rve.saveService')}
</Button>
</div>
</div>