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:
1 parent
5fbb5f824d
commit
ad9acea1e5
31 files changed
+167
-72
No files matched your search
@@ -521,6 +521,7 @@ services/api.ts (1324L)
|
|||||||
| U30 | i18n 批量转换损坏调用修复(8 处损坏的 i18n.t() 调用修复 — DeploymentEditContainers.tsx:85 toast description 损坏 / DeploymentEditStorage.tsx:175,202 !manifest 空状态+数据卷副标题损坏 / AgentRuleDialog.tsx:111,157,183,195 DialogDescription+操作符/端口/持续时间 Label 损坏 / LogFieldSidebar.tsx:68 搜索 placeholder 损坏。损坏原因: 早期批量正则 i18n 转换将模板表达式/JSX 碎片错误包裹进 i18n.t() 调用。新增 6 个语义化键(agent.ruleDialog.description/operator/port/duration + kubernetes.storage.volumeDesc + monitor.lbl.searchFields),zh/en 7375→7381 +6 键对齐,tsc -b=0,npm build✓ (17.99s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** |
|
| U30 | i18n 批量转换损坏调用修复(8 处损坏的 i18n.t() 调用修复 — DeploymentEditContainers.tsx:85 toast description 损坏 / DeploymentEditStorage.tsx:175,202 !manifest 空状态+数据卷副标题损坏 / AgentRuleDialog.tsx:111,157,183,195 DialogDescription+操作符/端口/持续时间 Label 损坏 / LogFieldSidebar.tsx:68 搜索 placeholder 损坏。损坏原因: 早期批量正则 i18n 转换将模板表达式/JSX 碎片错误包裹进 i18n.t() 调用。新增 6 个语义化键(agent.ruleDialog.description/operator/port/duration + kubernetes.storage.volumeDesc + monitor.lbl.searchFields),zh/en 7375→7381 +6 键对齐,tsc -b=0,npm build✓ (17.99s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** |
|
||||||
| U31 | AIOps Agent CLI 组件 i18n 修复(AgentCLI + AgentCLIToolbar + AgentCLIHistory + AgentCLIEmptyState + ChatInputArea + agent-cli-constants.ts 6 文件 — Q7/Q35 拆分后丢失的 i18n 集成全部修复,新增 `aiops.cli.*` 命名空间 100 翻译键(8 子分类: toolbar/safety/tips/error/info/viewMode/placeholder/history/empty/input),SAFETY_MODES+TIPS_HOST+TIPS_KUBERNETES 常量数组改为 `getSafetyModes(t)`/`getTipsHost(t)`/`getTipsKubernetes(t)` 接收 TFunction 参数的工厂函数,全部 UI 硬编码中文清零,zh/en 7381→7481 +100 键对齐,tsc -b=0,npm build✓ (18.21s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** |
|
| U31 | AIOps Agent CLI 组件 i18n 修复(AgentCLI + AgentCLIToolbar + AgentCLIHistory + AgentCLIEmptyState + ChatInputArea + agent-cli-constants.ts 6 文件 — Q7/Q35 拆分后丢失的 i18n 集成全部修复,新增 `aiops.cli.*` 命名空间 100 翻译键(8 子分类: toolbar/safety/tips/error/info/viewMode/placeholder/history/empty/input),SAFETY_MODES+TIPS_HOST+TIPS_KUBERNETES 常量数组改为 `getSafetyModes(t)`/`getTipsHost(t)`/`getTipsKubernetes(t)` 接收 TFunction 参数的工厂函数,全部 UI 硬编码中文清零,zh/en 7381→7481 +100 键对齐,tsc -b=0,npm build✓ (18.21s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** |
|
||||||
| U32 | 全量 hash key 清理 + React Hooks 警告修复(151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key — monitor 58/kubernetes 42/aiops 17/agent 12/cloud 13/pipeline 6/system 3;58 个源文件 147 处 t()/i18n.t() 调用更新;3 个命名空间冲突修复(monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult 原 string → .title 子键);React Hooks 警告 53→43 (-10): exhaustive-deps 4→0 + preserve-manual-memoization 5→0 + 变量遮蔽修复(filter(t)→filter(tg));zh/en 7480=7480 完全对齐 0 hash key 残留;tsc -b=0,npm build✓ (17.59s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** |
|
| U32 | 全量 hash key 清理 + React Hooks 警告修复(151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key — monitor 58/kubernetes 42/aiops 17/agent 12/cloud 13/pipeline 6/system 3;58 个源文件 147 处 t()/i18n.t() 调用更新;3 个命名空间冲突修复(monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult 原 string → .title 子键);React Hooks 警告 53→43 (-10): exhaustive-deps 4→0 + preserve-manual-memoization 5→0 + 变量遮蔽修复(filter(t)→filter(tg));zh/en 7480=7480 完全对齐 0 hash key 残留;tsc -b=0,npm build✓ (17.59s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** |
|
||||||
|
| U33 | K8s 资源编辑器 + Pod 日志/Metrics i18n 补全(resource-visual-editors/ 5 文件: ConfigMap/Secret/Service/Ingress/PvcEditor 按钮文案+帮助文本+访问模式+IngressClass 提示全部 i18n;pod-logs-viewer/ 3 文件: ControlBar 导出/刷新/实时/停止+LogContentArea 加载中+LogsHeader 标题;PodLogsViewer 6 处错误消息+DialogTitle/Description;PodMetricsPage subtitle+表头+所有命名空间;17 处 toast description 模板字面量中文→i18n.t() 插值 (DeploymentEditAdvanced/Containers/Storage/View/Detail/Service/ResourceVisualEditor/NamespaceResourcePage/ClusterOverviewPage);5 处 confirm description (DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection);5 处 subtitle 模板字面量 (PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage);新增 k8s.rve 14键+k8s.toast 18键+k8s.podMetrics 4键+k8s.podLogs 13键 共 49 翻译键;zh/en 7481→7524 +43 对齐;tsc -b=0,npm build✓ (17.76s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default function CapacityPage() {
|
|||||||
<PageShell
|
<PageShell
|
||||||
variant="list"
|
variant="list"
|
||||||
title={i18n.t('k8s.capacity.resources')}
|
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" />}
|
icon={<BarChart3 className="h-5 w-5" />}
|
||||||
actions={(
|
actions={(
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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('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([]); }
|
} catch { setClusters([]); }
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -86,7 +86,7 @@ export default function ClusterOverviewPage() {
|
|||||||
<PageShell
|
<PageShell
|
||||||
variant="default"
|
variant="default"
|
||||||
title={clusterCode || i18n.t('k8s.clusterOverview.title')}
|
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" />}
|
icon={<Server className="h-5 w-5" />}
|
||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
<StatStrip
|
<StatStrip
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
|||||||
setActionLoading('scale');
|
setActionLoading('scale');
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.scaleDeployment(clusterCode, namespace, deploymentName, newReplicas);
|
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();
|
loadData();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('cloud.operationFailed'), description: e?.message || String(e), variant: 'destructive' });
|
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');
|
setActionLoading('image');
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.updateDeploymentImage(clusterCode, namespace, deploymentName, newImage);
|
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);
|
setShowImageUpdate(false);
|
||||||
loadData();
|
loadData();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -108,7 +108,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
|||||||
const handleHpaToggle = async (hpa: JsonData) => {
|
const handleHpaToggle = async (hpa: JsonData) => {
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: i18n.t('kubernetes.hpa.deleteHpa'),
|
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'),
|
confirmText: i18n.t('kubernetes.storage.delete2'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export function DeploymentEditAdvanced({ clusterCode, namespace, deploymentName,
|
|||||||
};
|
};
|
||||||
const base = buildAdvancedManifest(rawManifest, state);
|
const base = buildAdvancedManifest(rawManifest, state);
|
||||||
await saveDeploymentManifest(clusterCode, namespace, deploymentName, base);
|
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);
|
onSaved(base);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' });
|
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
|
// validate
|
||||||
for (const c of [...initContainers, ...containers]) {
|
for (const c of [...initContainers, ...containers]) {
|
||||||
if (!c.name.trim()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (!c.image.trim()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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('k8s.editContainers.saved'), description: `${deploymentName} 更新已提交` });
|
toast({ title: i18n.t('k8s.editContainers.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: 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' });
|
||||||
|
|||||||
@@ -222,7 +222,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('common.delete'),
|
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'),
|
confirmText: i18n.t('kubernetes.storage.delete2'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
@@ -230,7 +230,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName,
|
|||||||
try {
|
try {
|
||||||
const resource = kind === 'service' ? 'services' : 'ingresses';
|
const resource = kind === 'service' ? 'services' : 'ingresses';
|
||||||
await kubernetesApi.deleteResourceYaml(clusterCode, namespace, resource, name);
|
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();
|
load();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// 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 { 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('k8s.editStorage.saved'), description: `${deploymentName} 更新已提交` });
|
toast({ title: i18n.t('k8s.editStorage.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: 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' });
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export function DeploymentView(props: DeploymentViewProps) {
|
|||||||
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('k8s.deploymentView.rollbackVersion'),
|
title: i18n.t('k8s.deploymentView.rollbackVersion'),
|
||||||
description: `确认回滚到修订版本 #${revision}?`,
|
description: i18n.t('k8s.toast.confirmRollback', { 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('k8s.deploymentView.rollbackSent'), description: `正在回滚到 #${revision}` });
|
toast({ title: i18n.t('k8s.deploymentView.rollbackSent'), description: i18n.t('k8s.toast.rollbackToRevision', { 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('k8s.deploymentView.scaleSent'), description: `副本数 → ${scaleValue}` });
|
toast({ title: i18n.t('k8s.deploymentView.scaleSent'), description: i18n.t('k8s.toast.scaleTo', { count: 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' });
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export default function EventsPage() {
|
|||||||
<PageShell
|
<PageShell
|
||||||
variant="list"
|
variant="list"
|
||||||
title={i18n.t('k8s.event.center')}
|
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" />}
|
icon={<Bell className="h-5 w-5" />}
|
||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
<StatStrip
|
<StatStrip
|
||||||
|
|||||||
@@ -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('k8s.nodes.subtitle')}
|
subtitle={clusterLabel ? `${clusterLabel} · ${i18n.t('k8s.nodes.subtitle')}` : i18n.t('k8s.nodes.subtitle')}
|
||||||
icon={<Server className="h-5 w-5" />}
|
icon={<Server className="h-5 w-5" />}
|
||||||
actions={(
|
actions={(
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export default function NamespaceResourcePage() {
|
|||||||
if (!clusterCode || !namespace) return;
|
if (!clusterCode || !namespace) return;
|
||||||
try {
|
try {
|
||||||
await kubernetesApi.exportNamespaceResources(clusterCode, namespace);
|
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) {
|
} catch (e) {
|
||||||
toast({ title: i18n.t('kubernetes.msg.exportFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
|
toast({ title: i18n.t('kubernetes.msg.exportFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export function PodExecTerminal({
|
|||||||
console.error('[PodExec:connect] WS error event fired, readyState=', ws.readyState, 'type=', ev.type);
|
console.error('[PodExec:connect] WS error event fired, readyState=', ws.readyState, 'type=', ev.type);
|
||||||
setConnState('error');
|
setConnState('error');
|
||||||
setHasOutput(true);
|
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]);
|
}, [clusterId, namespace, podName, container, shell]);
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export function PodLogsViewer({
|
|||||||
appendLines(arr);
|
appendLines(arr);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(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' });
|
toast({ title: i18n.t('k8s.podLogs.loadFailed'), description: msg, variant: 'destructive' });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -183,14 +183,14 @@ export function PodLogsViewer({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush();
|
flush();
|
||||||
appendLines(['\x1b[33m— 流式结束 —\x1b[0m']);
|
appendLines([`\x1b[33m${i18n.t('k8s.podLogs.streamEnded')}\x1b[0m`]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errName = (e as { name?: string })?.name;
|
const errName = (e as { name?: string })?.name;
|
||||||
if (errName === 'AbortError') {
|
if (errName === 'AbortError') {
|
||||||
appendLines(['\x1b[33m— 已停止 —\x1b[0m']);
|
appendLines([`\x1b[33m${i18n.t('k8s.podLogs.streamStopped')}\x1b[0m`]);
|
||||||
} else {
|
} else {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
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 {
|
} finally {
|
||||||
setStreaming(false);
|
setStreaming(false);
|
||||||
@@ -261,8 +261,8 @@ export function PodLogsViewer({
|
|||||||
)}
|
)}
|
||||||
style={fullscreen ? { maxWidth: '100vw', width: '100vw', height: '100vh' } : { maxWidth: '1100px', width: '94vw' }}
|
style={fullscreen ? { maxWidth: '100vw', width: '100vw', height: '100vh' } : { maxWidth: '1100px', width: '94vw' }}
|
||||||
>
|
>
|
||||||
<DialogTitle className="sr-only">日志 — {podName}</DialogTitle>
|
<DialogTitle className="sr-only">{i18n.t('k8s.podLogs.logTitle', { name: podName })}</DialogTitle>
|
||||||
<DialogDescription className="sr-only">Pod 容器日志查看</DialogDescription>
|
<DialogDescription className="sr-only">{i18n.t('k8s.podLogs.logDesc')}</DialogDescription>
|
||||||
|
|
||||||
<LogsHeader
|
<LogsHeader
|
||||||
podName={podName}
|
podName={podName}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default function PodMetricsPage() {
|
|||||||
<PageShell
|
<PageShell
|
||||||
variant="list"
|
variant="list"
|
||||||
title={i18n.t('k8s.podMetrics.resources2')}
|
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" />}
|
icon={<Activity className="h-5 w-5" />}
|
||||||
metrics={!loading ? (
|
metrics={!loading ? (
|
||||||
<StatStrip
|
<StatStrip
|
||||||
@@ -110,7 +110,7 @@ export default function PodMetricsPage() {
|
|||||||
<Select value={selectedNs} onValueChange={setSelectedNs}>
|
<Select value={selectedNs} onValueChange={setSelectedNs}>
|
||||||
<SelectTrigger className="w-36 h-9 dg-input"><SelectValue placeholder={i18n.t('k8s.podMetrics.namespace')} /></SelectTrigger>
|
<SelectTrigger className="w-36 h-9 dg-input"><SelectValue placeholder={i18n.t('k8s.podMetrics.namespace')} /></SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="all">所有命名空间</SelectItem>
|
<SelectItem value="all">{i18n.t('k8s.podMetrics.allNamespaces')}</SelectItem>
|
||||||
{namespaces.map(n => <SelectItem key={n} value={n}>{n}</SelectItem>)}
|
{namespaces.map(n => <SelectItem key={n} value={n}>{n}</SelectItem>)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: `${resource}/${resName}` });
|
toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: `${resource}/${resName}` });
|
||||||
} 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: i18n.t('k8s.toast.resourceUpdated', { resource, name }) });
|
||||||
}
|
}
|
||||||
onSaved?.();
|
onSaved?.();
|
||||||
onClose();
|
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') });
|
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: i18n.t('k8s.toast.resourceUpdated', { resource, name }) });
|
||||||
}
|
}
|
||||||
setYamlEditing(false);
|
setYamlEditing(false);
|
||||||
setDisplayYaml(yamlStr);
|
setDisplayYaml(yamlStr);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export default function WorkloadViewPage() {
|
|||||||
const handleRestart = async () => {
|
const handleRestart = async () => {
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: i18n.t('kubernetes.deployment.restart'),
|
title: i18n.t('kubernetes.deployment.restart'),
|
||||||
description: `确认重启 ${kind} ${name}?将触发滚动重启。`,
|
description: i18n.t('k8s.toast.confirmRestart', { kind, name }),
|
||||||
confirmText: i18n.t('agent.agentServiceRestart'),
|
confirmText: i18n.t('agent.agentServiceRestart'),
|
||||||
});
|
});
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|||||||
@@ -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('k8s.rve.saveFailed') : `${resource}/${name} 已更新` });
|
toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: i18n.t('k8s.toast.resourceUpdated', { resource, name }),});
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
setRawYaml(yamlStr);
|
setRawYaml(yamlStr);
|
||||||
setDisplayYaml(yamlStr);
|
setDisplayYaml(yamlStr);
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
title={i18n.t('k8s.podLogs.previousLogs')}
|
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" />{i18n.t('k8s.podLogs.exportBtn')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* 刷新(一次性) */}
|
{/* 刷新(一次性) */}
|
||||||
@@ -138,7 +138,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
disabled={loading}
|
disabled={loading}
|
||||||
title={i18n.t('k8s.podLogs.previousLogs')}
|
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>
|
</Button>
|
||||||
|
|
||||||
{/* 实时跟踪 / 暂停 / 继续 */}
|
{/* 实时跟踪 / 暂停 / 继续 */}
|
||||||
@@ -150,7 +150,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
disabled={loading}
|
disabled={loading}
|
||||||
title={i18n.t('k8s.podLogs.previousLogs')}
|
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>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -169,7 +169,7 @@ export function ControlBar(props: ControlBarProps) {
|
|||||||
onClick={onStopStream}
|
onClick={onStopStream}
|
||||||
title={i18n.t('agent.agentServiceStop')}
|
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>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function LogContentArea({
|
|||||||
)}
|
)}
|
||||||
{loading && lineCount === 0 && (
|
{loading && lineCount === 0 && (
|
||||||
<div className="flex items-center justify-center h-full text-[var(--dg-text-muted)]">
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{filteredLines.map((line) => (
|
{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)]'
|
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" />
|
<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)]" />}
|
{loading && <Loader2 className="h-3 w-3 animate-spin text-[var(--dg-text-muted)]" />}
|
||||||
<span className="text-xs 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}` : ''} 行
|
{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)]">
|
<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}>
|
<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" />}
|
{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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
|
|||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-xs flex items-center gap-1">
|
<Label className="text-[var(--dg-text-secondary)] text-xs flex items-center gap-1">
|
||||||
Ingress Class
|
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>
|
</Label>
|
||||||
{/* IngressClass 下拉 + 手工输入支持 // Logan.Li */}
|
{/* IngressClass 下拉 + 手工输入支持 // Logan.Li */}
|
||||||
<div className="space-y-1 mt-1">
|
<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) && (
|
{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>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/* 总是显示输入框支持手工输入或修改 */}
|
{/* 总是显示输入框支持手工输入或修改 */}
|
||||||
<div className="text-[10px] text-[var(--dg-text-muted)] flex items-center gap-1">
|
<div className="text-[10px] text-[var(--dg-text-muted)] flex items-center gap-1">
|
||||||
手工输入:
|
{i18n.t('k8s.rve.manualInput')}
|
||||||
<Input
|
<Input
|
||||||
value={ingressClass}
|
value={ingressClass}
|
||||||
onChange={(e) => setIngressClass(e.target.value)}
|
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">
|
<div className="text-[10px] text-[var(--dg-text-muted)] leading-tight">
|
||||||
您的集群中可能有多个 IngressController,您可在此指定当前 Ingress 对应哪个 IngressController。<br />
|
{i18n.t('k8s.rve.ingressHelp1')}<br />
|
||||||
从 IngressClass 列表选择,或手工输入名称。
|
{i18n.t('k8s.rve.ingressHelp2')}
|
||||||
</div>
|
</div>
|
||||||
{/* 常见示例 */}
|
{/* 常见示例 */}
|
||||||
{(ingressClasses.length === 0 || ingressClass) && (
|
{(ingressClasses.length === 0 || ingressClass) && (
|
||||||
<div className="text-[10px] text-[var(--dg-text-muted)]">
|
<div className="text-[10px] text-[var(--dg-text-muted)]">
|
||||||
常见:<span className="font-mono">alb</span> → ingress.k8s.alibabacloud/alb <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 <span className="font-mono">nginx</span> → k8s.io/ingress-nginx
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</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)]">
|
<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}>
|
<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" />}
|
{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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="ReadWriteOnce">ReadWriteOnce (单节点读写)</SelectItem>
|
<SelectItem value="ReadWriteOnce">{i18n.t('k8s.rve.accessModeRWO')}</SelectItem>
|
||||||
<SelectItem value="ReadOnlyMany">ReadOnlyMany (多节点只读)</SelectItem>
|
<SelectItem value="ReadOnlyMany">{i18n.t('k8s.rve.accessModeROX')}</SelectItem>
|
||||||
<SelectItem value="ReadWriteMany">ReadWriteMany (多节点读写)</SelectItem>
|
<SelectItem value="ReadWriteMany">{i18n.t('k8s.rve.accessModeRWX')}</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</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)]">
|
<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}>
|
<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" />}
|
{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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</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)]">
|
<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}>
|
<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" />}
|
{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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</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)]">
|
<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}>
|
<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" />}
|
{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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2775,11 +2775,11 @@
|
|||||||
"podLogs": {
|
"podLogs": {
|
||||||
"noLogs": "No logs",
|
"noLogs": "No logs",
|
||||||
"loadFailed": "Failed to load logs",
|
"loadFailed": "Failed to load logs",
|
||||||
"streamInterrupted": "Stream connection interrupted",
|
"streamInterrupted": "[Stream connection interrupted: {{msg}}]",
|
||||||
"streaming": "Streaming...",
|
"streaming": "Streaming...",
|
||||||
"liveStream": "Live Stream",
|
"liveStream": "Live Stream",
|
||||||
"noLogContent": "No log content",
|
"noLogContent": "No log content",
|
||||||
"logTitle": "Logs",
|
"logTitle": "Logs — {{name}}",
|
||||||
"logDesc": "Pod container log viewer",
|
"logDesc": "Pod container log viewer",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"refreshLogs": "Refresh",
|
"refreshLogs": "Refresh",
|
||||||
@@ -2791,13 +2791,20 @@
|
|||||||
"bufferFull": "Buffer full",
|
"bufferFull": "Buffer full",
|
||||||
"matched": "Matched",
|
"matched": "Matched",
|
||||||
"loading": "Loading…",
|
"loading": "Loading…",
|
||||||
"fetchFailed": "Failed to fetch logs",
|
"fetchFailed": "[Failed to fetch logs: {{msg}}]",
|
||||||
"streamEnded": "Stream ended",
|
"streamEnded": "— Stream ended —",
|
||||||
"streamStopped": "Stream stopped",
|
"streamStopped": "— Stopped —",
|
||||||
"download": "Download Logs",
|
"download": "Download Logs",
|
||||||
"searchPlaceholder": "Search logs...",
|
"searchPlaceholder": "Search logs...",
|
||||||
"previousLogs": "View previous (crashed) container logs",
|
"previousLogs": "View previous (crashed) container logs",
|
||||||
"keywordFilter": "Keyword filter…"
|
"keywordFilter": "Keyword filter…",
|
||||||
|
"liveTail": "Live Tail",
|
||||||
|
"exportBtn": "Export",
|
||||||
|
"refreshBtn": "Refresh",
|
||||||
|
"liveBtn": "Live",
|
||||||
|
"stopBtn": "Stop",
|
||||||
|
"loadingDots": "Loading…",
|
||||||
|
"connError": "[Connection error]"
|
||||||
},
|
},
|
||||||
"editMode": {
|
"editMode": {
|
||||||
"basic": "Basic Info",
|
"basic": "Basic Info",
|
||||||
@@ -3182,7 +3189,11 @@
|
|||||||
"memory": "总内存",
|
"memory": "总内存",
|
||||||
"namespace": "所有命名空间",
|
"namespace": "所有命名空间",
|
||||||
"cpuUsage": "CPU Usage",
|
"cpuUsage": "CPU Usage",
|
||||||
"memUsage": "Memory Usage"
|
"memUsage": "Memory Usage",
|
||||||
|
"realtimeMetrics": "Container CPU/Memory real-time metrics",
|
||||||
|
"allNamespaces": "All Namespaces",
|
||||||
|
"colNamespace": "Namespace",
|
||||||
|
"colContainer": "Container"
|
||||||
},
|
},
|
||||||
"resourceEditor": {
|
"resourceEditor": {
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
@@ -3366,7 +3377,22 @@
|
|||||||
"keyPlaceholder": "Key name (e.g. LOG_LEVEL)",
|
"keyPlaceholder": "Key name (e.g. LOG_LEVEL)",
|
||||||
"parseYamlFailed": "Unable to parse YAML",
|
"parseYamlFailed": "Unable to parse YAML",
|
||||||
"checkSyntax": "Please check syntax and continue in YAML mode",
|
"checkSyntax": "Please check syntax and continue in YAML mode",
|
||||||
"fallbackToYaml": "Unable to parse resource structure, switched to YAML edit mode"
|
"fallbackToYaml": "Unable to parse resource structure, switched to YAML edit mode",
|
||||||
|
"saveConfigMap": "Save ConfigMap",
|
||||||
|
"saveSecret": "Save Secret",
|
||||||
|
"saveService": "Save Service",
|
||||||
|
"saveIngress": "Save Ingress",
|
||||||
|
"savePvc": "Save PVC",
|
||||||
|
"optional": "(Optional)",
|
||||||
|
"manualInput": "Manual input:",
|
||||||
|
"manualEntry": "(Manual)",
|
||||||
|
"continueManual": "—— Continue manual input ——",
|
||||||
|
"ingressHelp1": "Your cluster may have multiple IngressControllers. You can specify which IngressController this Ingress corresponds to.",
|
||||||
|
"ingressHelp2": "Select from the IngressClass list, or enter a name manually.",
|
||||||
|
"commonExamples": "Common:",
|
||||||
|
"accessModeRWO": "ReadWriteOnce (Single node read-write)",
|
||||||
|
"accessModeROX": "ReadOnlyMany (Multi-node read-only)",
|
||||||
|
"accessModeRWX": "ReadWriteMany (Multi-node read-write)"
|
||||||
},
|
},
|
||||||
"workloadView": {
|
"workloadView": {
|
||||||
"restartSent": "Restart command sent",
|
"restartSent": "Restart command sent",
|
||||||
@@ -3467,6 +3493,26 @@
|
|||||||
},
|
},
|
||||||
"podFile": {
|
"podFile": {
|
||||||
"loadFailed": "Failed to load file list"
|
"loadFailed": "Failed to load file list"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"updatedSubmitted": "{{name}} update submitted",
|
||||||
|
"yamlDownloadStarted": "{{namespace}} resource YAML download started",
|
||||||
|
"rollbackToRevision": "Rolling back to #{{revision}}",
|
||||||
|
"scaleTo": "Replicas → {{count}}",
|
||||||
|
"scaleSet": "Replicas set to {{count}}",
|
||||||
|
"imageUpdating": "Rolling update to {{image}}",
|
||||||
|
"resourceUpdated": "{{resource}}/{{name}} updated",
|
||||||
|
"resourceDeleted": "{{kind}} {{name}} deleted",
|
||||||
|
"containerNameEmpty": "Container name cannot be empty",
|
||||||
|
"containerImageEmpty": "Container {{name}} image cannot be empty",
|
||||||
|
"clusterNotFound": "Cluster {{code}} not found",
|
||||||
|
"confirmRollback": "Confirm rollback to revision #{{revision}}?",
|
||||||
|
"confirmDeleteResource": "Confirm delete {{kind}} \"{{name}}\"? This cannot be undone.",
|
||||||
|
"confirmDeleteHpa": "Delete HPA \"{{name}}\"?",
|
||||||
|
"confirmRestart": "Confirm restart {{kind}} {{name}}? This will trigger a rolling restart.",
|
||||||
|
"confirmDeleteProbe": "Delete probe \"{{name}}\"?",
|
||||||
|
"confirmDeleteMonitor": "Confirm delete \"{{name}}\"?",
|
||||||
|
"confirmDeleteTask": "Delete \"{{name}}\"?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"monitor": {
|
"monitor": {
|
||||||
|
|||||||
@@ -2775,11 +2775,11 @@
|
|||||||
"podLogs": {
|
"podLogs": {
|
||||||
"noLogs": "无日志",
|
"noLogs": "无日志",
|
||||||
"loadFailed": "获取日志失败",
|
"loadFailed": "获取日志失败",
|
||||||
"streamInterrupted": "流式连接中断",
|
"streamInterrupted": "[流式连接中断: {{msg}}]",
|
||||||
"streaming": "流中...",
|
"streaming": "流中...",
|
||||||
"liveStream": "实时流",
|
"liveStream": "实时流",
|
||||||
"noLogContent": "无日志内容",
|
"noLogContent": "无日志内容",
|
||||||
"logTitle": "日志",
|
"logTitle": "日志 — {{name}}",
|
||||||
"logDesc": "Pod 容器日志查看",
|
"logDesc": "Pod 容器日志查看",
|
||||||
"export": "导出",
|
"export": "导出",
|
||||||
"refreshLogs": "刷新",
|
"refreshLogs": "刷新",
|
||||||
@@ -2791,13 +2791,20 @@
|
|||||||
"bufferFull": "缓冲已满",
|
"bufferFull": "缓冲已满",
|
||||||
"matched": "匹配",
|
"matched": "匹配",
|
||||||
"loading": "加载中…",
|
"loading": "加载中…",
|
||||||
"fetchFailed": "获取日志失败",
|
"fetchFailed": "[获取日志失败: {{msg}}]",
|
||||||
"streamEnded": "流式传输结束",
|
"streamEnded": "— 流式结束 —",
|
||||||
"streamStopped": "流式传输已停止",
|
"streamStopped": "— 已停止 —",
|
||||||
"download": "下载日志",
|
"download": "下载日志",
|
||||||
"searchPlaceholder": "搜索日志...",
|
"searchPlaceholder": "搜索日志...",
|
||||||
"previousLogs": "查看上一次(崩溃)容器的日志",
|
"previousLogs": "查看上一次(崩溃)容器的日志",
|
||||||
"keywordFilter": "关键字过滤…"
|
"keywordFilter": "关键字过滤…",
|
||||||
|
"liveTail": "实时跟踪",
|
||||||
|
"exportBtn": "导出",
|
||||||
|
"refreshBtn": "刷新",
|
||||||
|
"liveBtn": "实时",
|
||||||
|
"stopBtn": "停止",
|
||||||
|
"loadingDots": "加载中…",
|
||||||
|
"connError": "[连接发生错误]"
|
||||||
},
|
},
|
||||||
"editMode": {
|
"editMode": {
|
||||||
"basic": "基本信息",
|
"basic": "基本信息",
|
||||||
@@ -3182,7 +3189,11 @@
|
|||||||
"memory": "总内存",
|
"memory": "总内存",
|
||||||
"namespace": "所有命名空间",
|
"namespace": "所有命名空间",
|
||||||
"cpuUsage": "CPU 使用",
|
"cpuUsage": "CPU 使用",
|
||||||
"memUsage": "内存使用"
|
"memUsage": "内存使用",
|
||||||
|
"realtimeMetrics": "容器 CPU/内存实时指标",
|
||||||
|
"allNamespaces": "所有命名空间",
|
||||||
|
"colNamespace": "命名空间",
|
||||||
|
"colContainer": "容器"
|
||||||
},
|
},
|
||||||
"resourceEditor": {
|
"resourceEditor": {
|
||||||
"create": "新建",
|
"create": "新建",
|
||||||
@@ -3366,7 +3377,22 @@
|
|||||||
"keyPlaceholder": "键名 (如 LOG_LEVEL)",
|
"keyPlaceholder": "键名 (如 LOG_LEVEL)",
|
||||||
"parseYamlFailed": "无法解析 YAML",
|
"parseYamlFailed": "无法解析 YAML",
|
||||||
"checkSyntax": "请检查语法后继续用 YAML 模式",
|
"checkSyntax": "请检查语法后继续用 YAML 模式",
|
||||||
"fallbackToYaml": "无法解析资源结构,已切换到 YAML 编辑模式"
|
"fallbackToYaml": "无法解析资源结构,已切换到 YAML 编辑模式",
|
||||||
|
"saveConfigMap": "保存配置",
|
||||||
|
"saveSecret": "保存密钥",
|
||||||
|
"saveService": "保存 Service",
|
||||||
|
"saveIngress": "保存 Ingress",
|
||||||
|
"savePvc": "保存 PVC",
|
||||||
|
"optional": "(可选)",
|
||||||
|
"manualInput": "手工输入:",
|
||||||
|
"manualEntry": "(手动输入)",
|
||||||
|
"continueManual": "—— 继续手动输入 ——",
|
||||||
|
"ingressHelp1": "您的集群中可能有多个 IngressController,您可在此指定当前 Ingress 对应哪个 IngressController。",
|
||||||
|
"ingressHelp2": "从 IngressClass 列表选择,或手工输入名称。",
|
||||||
|
"commonExamples": "常见:",
|
||||||
|
"accessModeRWO": "ReadWriteOnce (单节点读写)",
|
||||||
|
"accessModeROX": "ReadOnlyMany (多节点只读)",
|
||||||
|
"accessModeRWX": "ReadWriteMany (多节点读写)"
|
||||||
},
|
},
|
||||||
"workloadView": {
|
"workloadView": {
|
||||||
"restartSent": "重启指令已下发",
|
"restartSent": "重启指令已下发",
|
||||||
@@ -3467,6 +3493,26 @@
|
|||||||
},
|
},
|
||||||
"podFile": {
|
"podFile": {
|
||||||
"loadFailed": "加载文件列表失败"
|
"loadFailed": "加载文件列表失败"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"updatedSubmitted": "{{name}} 更新已提交",
|
||||||
|
"yamlDownloadStarted": "{{namespace}} 资源 YAML 已开始下载",
|
||||||
|
"rollbackToRevision": "正在回滚到 #{{revision}}",
|
||||||
|
"scaleTo": "副本数 → {{count}}",
|
||||||
|
"scaleSet": "副本数已设置为 {{count}}",
|
||||||
|
"imageUpdating": "正在滚动更新为 {{image}}",
|
||||||
|
"resourceUpdated": "{{resource}}/{{name}} 已更新",
|
||||||
|
"resourceDeleted": "{{kind}} {{name}} 已删除",
|
||||||
|
"containerNameEmpty": "容器名称不能为空",
|
||||||
|
"containerImageEmpty": "容器 {{name}} 的镜像不能为空",
|
||||||
|
"clusterNotFound": "找不到集群 {{code}}",
|
||||||
|
"confirmRollback": "确认回滚到修订版本 #{{revision}}?",
|
||||||
|
"confirmDeleteResource": "确认删除 {{kind}} 「{{name}}」?此操作不可撤销。",
|
||||||
|
"confirmDeleteHpa": "确定删除 HPA \"{{name}}\"?",
|
||||||
|
"confirmRestart": "确认重启 {{kind}} {{name}}?将触发滚动重启。",
|
||||||
|
"confirmDeleteProbe": "确定删除探针「{{name}}」?",
|
||||||
|
"confirmDeleteMonitor": "确认删除 \"{{name}}\"?",
|
||||||
|
"confirmDeleteTask": "确定删除「{{name}}」吗?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"monitor": {
|
"monitor": {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { MiddlewareDialog } from './middleware-monitor/MiddlewareDialog';
|
|||||||
import type { MwTarget } from './middleware-monitor/helpers';
|
import type { MwTarget } from './middleware-monitor/helpers';
|
||||||
import { REDIS_CHARTS, MYSQL_CHARTS, evalMwHealth, worstHealthLevel } from './middleware-monitor/helpers';
|
import { REDIS_CHARTS, MYSQL_CHARTS, evalMwHealth, worstHealthLevel } from './middleware-monitor/helpers';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import i18n from '@/i18n';
|
||||||
|
|
||||||
type TabKey = 'redis' | 'mysql';
|
type TabKey = 'redis' | 'mysql';
|
||||||
const REFRESH_INTERVAL_MS = 30_000;
|
const REFRESH_INTERVAL_MS = 30_000;
|
||||||
@@ -87,7 +88,7 @@ export function MiddlewareMonitor() {
|
|||||||
const warnCount = targets.filter((t) => worstHealthLevel(evalMwHealth(t)) !== 'ok').length;
|
const warnCount = targets.filter((t) => worstHealthLevel(evalMwHealth(t)) !== 'ok').length;
|
||||||
|
|
||||||
const handleDelete = async (target: MwTarget) => {
|
const handleDelete = async (target: MwTarget) => {
|
||||||
const ok = await confirm({ title: t('monitor.lbl.deleteMonitor'), description: `确认删除 "${target.name}"?`, confirmText: t('kubernetes.storage.delete2') });
|
const ok = await confirm({ title: t('monitor.lbl.deleteMonitor'), description: i18n.t('k8s.toast.confirmDeleteMonitor', { name: target.name }), confirmText: t('kubernetes.storage.delete2') });
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
try {
|
try {
|
||||||
await middlewareMonitorApi.delete(target.id);
|
await middlewareMonitorApi.delete(target.id);
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export function SiteMonitor() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (task: SiteTask) => {
|
const handleDelete = async (task: SiteTask) => {
|
||||||
const ok = await _confirm({ title: t('monitor.deleteProbe'), description: `确定删除「${task.name}」吗?`, variant: 'destructive' });
|
const ok = await _confirm({ title: t('monitor.deleteProbe'), description: i18n.t('k8s.toast.confirmDeleteTask', { name: task.name }), variant: 'destructive' });
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
try {
|
try {
|
||||||
await siteApi.deleteTask(task.id);
|
await siteApi.deleteTask(task.id);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { DEFAULT_PROBE_FORM, type ProbeForm } from './types';
|
|||||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import i18n from '@/i18n';
|
||||||
|
|
||||||
interface HealthProbeSectionProps {
|
interface HealthProbeSectionProps {
|
||||||
selectedHost: string;
|
selectedHost: string;
|
||||||
@@ -77,7 +78,7 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) {
|
|||||||
const handleDeleteProbe = async (p: Record<string, unknown>) => {
|
const handleDeleteProbe = async (p: Record<string, unknown>) => {
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: t('monitor.lbl.deleteProbe'),
|
title: t('monitor.lbl.deleteProbe'),
|
||||||
description: `确定删除探针「${p.name}」?`,
|
description: i18n.t('k8s.toast.confirmDeleteProbe', { name: p.name }),
|
||||||
confirmText: t('kubernetes.storage.delete2'),
|
confirmText: t('kubernetes.storage.delete2'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user