diff --git a/frontend/src/components/deploy/credential-manager/CredFormDialog.tsx b/frontend/src/components/deploy/credential-manager/CredFormDialog.tsx index 434a615..fff86c7 100644 --- a/frontend/src/components/deploy/credential-manager/CredFormDialog.tsx +++ b/frontend/src/components/deploy/credential-manager/CredFormDialog.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Key, Eye, EyeOff } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -19,6 +20,7 @@ export interface CredFormDialogProps { // 凭据新增/编辑弹窗 // Logan.Li export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDialogProps) { + const { t } = useTranslation(); const isEdit = !!editData; const [name, setName] = useState(''); const [varName, setVarName] = useState(''); @@ -95,7 +97,7 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia const toggleShow = (key: string) => setShowMap(prev => ({ ...prev, [key]: !prev[key] })); const handleSubmit = async () => { - if (!name.trim()) { toast.error('请填写凭据名称'); return; } + if (!name.trim()) { toast.error(t('deploy.cred.form.nameRequired')); return; } setSubmitting(true); try { const payload: Record = { @@ -141,16 +143,16 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia if (isEdit) { await credentialApi.update(editData!.id, payload); - toast.success('已更新'); + toast.success(t('deploy.cred.form.updated')); } else { await credentialApi.create(payload); - toast.success('已创建'); + toast.success(t('deploy.cred.form.created')); } onSaved(); onClose(); } catch (e: unknown) { const err = e as { response?: { data?: { message?: string } } }; - toast.error(err?.response?.data?.message || '操作失败'); + toast.error(err?.response?.data?.message || t('deploy.cred.form.operationFailed')); } finally { setSubmitting(false); } }; @@ -190,29 +192,29 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia - {isEdit ? '编辑凭据' : '新增凭据'} + {isEdit ? t('deploy.cred.form.editTitle') : t('deploy.cred.form.newTitle')} - {TI?.desc || '管理敏感凭据信息'} + {TI?.desc || t('deploy.cred.descDefault')}
{/* 基本信息 // Logan.Li */}
-
基本信息
+
{t('deploy.cred.form.basicInfo')}
- - setName(e.target.value)} placeholder="如:生产环境 SSH" + + setName(e.target.value)} placeholder={t('deploy.cred.form.credNamePlaceholder')} className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
- - setVarName(e.target.value)} placeholder="如:MYSQL_PROD" + + setVarName(e.target.value)} placeholder={t('deploy.cred.form.varAliasPlaceholder')} className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono text-sm" />
- +
- - setTags(e.target.value)} placeholder="生产环境, 阿里云" + + setTags(e.target.value)} placeholder={t('deploy.cred.form.tagsPlaceholder')} className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
- - setDescription(e.target.value)} placeholder="可选备注信息" + + setDescription(e.target.value)} placeholder={t('deploy.cred.form.descPlaceholder')} className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
@@ -242,33 +244,33 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia {/* 类型专属字段 // Logan.Li */}
- {TI?.label || secretType} 配置 - {isEdit && (仅填写需要修改的字段)} + {TI?.label || secretType} {t('deploy.cred.form.typeConfig')} + {isEdit && {t('deploy.cred.form.editHint')}}
{secretType === 'ssh' && (
- + setSshUser(e.target.value)} placeholder="root" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
- + setSshPort(e.target.value)} placeholder="22" type="number" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
- +
- +
- +
)} @@ -276,28 +278,28 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia {secretType === 'docker' && (
- + setDockerServer(e.target.value)} placeholder="registry.example.com" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono" />
- + setDockerUsername(e.target.value)} placeholder="admin" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
-
+
)} {secretType === 'tls' && (
- - - +
)} @@ -310,7 +312,7 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia
+ placeholder={t('deploy.cred.form.sshPwdPlaceholder')} fieldKey="aksk_sk" required />
)} @@ -318,7 +320,7 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia {secretType === 'database' && (
- +
- + setDbHost(e.target.value)} placeholder="10.0.0.1" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono" />
- + setDbPort(e.target.value)} placeholder="3306" type="number" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
- + setDbName(e.target.value)} placeholder="aiops" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
- + setDbUsername(e.target.value)} placeholder="root" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
-
+
)} {secretType === 'api' && (
- + setApiEndpoint(e.target.value)} placeholder="https://api.example.com" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono" />
-
)} @@ -378,15 +380,15 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono" /> + placeholder={t('deploy.cred.form.secretValuePlaceholder')} fieldKey="generic_val" required /> )} - + diff --git a/frontend/src/components/kubernetes/ClusterComparePage.tsx b/frontend/src/components/kubernetes/ClusterComparePage.tsx index 34f09a1..938f4db 100644 --- a/frontend/src/components/kubernetes/ClusterComparePage.tsx +++ b/frontend/src/components/kubernetes/ClusterComparePage.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { useNavigate, Link } from 'react-router-dom'; import { Card, CardContent } from '@/components/ui/card'; import { StatStrip } from '@/components/ui/stat-strip'; @@ -30,6 +31,7 @@ import type { JsonData } from '@/types/common'; const CHART_COLORS = ['#3B82F6', '#207060', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#14B8A6', '#F97316']; export default function ClusterComparePage() { + const { t } = useTranslation(); const navigate = useNavigate(); const [loading, setLoading] = useState(false); const [data, setData] = useState(null); @@ -41,13 +43,13 @@ export default function ClusterComparePage() { try { const res: JsonData = await kubernetesApi.getMultiClusterComparison(); setData(res || null); - } catch (e) { - setError(e?.message || '加载失败'); + } catch { + setError(t('k8s.compare.loadFailed')); setData(null); } finally { setLoading(false); } - }, []); + }, [t]); useEffect(() => { loadData(); }, [loadData]); @@ -57,7 +59,7 @@ export default function ClusterComparePage() { // 图表数据准备 const resourceBarData = clusters.map((c: JsonData) => ({ name: c.code || c.name, - 节点: c.node_count || 0, + [t('k8s.compare.chartNodes')]: c.node_count || 0, Pod: c.pod_count || 0, Deployment: c.deployment_count || 0, Service: c.service_count || 0, @@ -65,17 +67,17 @@ export default function ClusterComparePage() { const usageBarData = clusters.map((c: JsonData) => ({ name: c.code || c.name, - 'CPU %': Number((c.cpu_usage || 0).toFixed(1)), - '内存 %': Number((c.memory_usage || 0).toFixed(1)), + [t('k8s.compare.cpuPercent')]: Number((c.cpu_usage || 0).toFixed(1)), + [t('k8s.compare.memPercent')]: Number((c.memory_usage || 0).toFixed(1)), })); const radarData = clusters.length > 0 ? [ - { metric: '节点数', ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, c.node_count || 0])) }, - { metric: 'Pod数', ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, Math.min(c.pod_count || 0, 100)])) }, - { metric: '部署数', ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, c.deployment_count || 0])) }, - { metric: 'CPU使用', ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, Number((c.cpu_usage || 0).toFixed(1))])) }, - { metric: '内存使用', ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, Number((c.memory_usage || 0).toFixed(1))])) }, - { metric: 'CPU核数', ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, c.total_cpu_cores || 0])) }, + { metric: t('k8s.compare.chartNodes'), ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, c.node_count || 0])) }, + { metric: t('k8s.compare.chartPods'), ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, Math.min(c.pod_count || 0, 100)])) }, + { metric: t('k8s.compare.chartDeploy'), ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, c.deployment_count || 0])) }, + { metric: t('k8s.compare.chartCpuUsage'), ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, Number((c.cpu_usage || 0).toFixed(1))])) }, + { metric: t('k8s.compare.chartMemUsage'), ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, Number((c.memory_usage || 0).toFixed(1))])) }, + { metric: t('k8s.compare.chartCpuCores'), ...Object.fromEntries(clusters.map((c: JsonData) => [c.code || c.name, c.total_cpu_cores || 0])) }, ] : []; const nodeRolePie = clusters.flatMap((c: JsonData) => [ @@ -96,12 +98,12 @@ export default function ClusterComparePage() { }; // 格式化同步时间 - const fmtTime = (t: string | null | undefined) => { - if (!t) return '从未同步'; + const fmtTime = (ts: string | null | undefined) => { + if (!ts) return t('k8s.compare.neverSynced'); try { - const d = new Date(t); + const d = new Date(ts); return d.toLocaleString('zh-CN', { hour12: false }); - } catch { return t; } + } catch { return ts; } }; return ( @@ -111,19 +113,19 @@ export default function ClusterComparePage() {
-

多集群资源对比看板

-

跨集群资源用量、节点分布、健康状态对比

+

{t('k8s.compare.title')}

+

{t('k8s.compare.subtitle')}

@@ -139,8 +141,8 @@ export default function ClusterComparePage() { {/* 概览卡片 */} -

暂无集群数据,请先添加 Kubernetes 集群并同步

+

{t('k8s.compare.noData')}

@@ -169,7 +171,7 @@ export default function ClusterComparePage() {
-

资源数量对比

+

{t('k8s.compare.resourceCompare')}

@@ -178,7 +180,7 @@ export default function ClusterComparePage() { - + @@ -191,7 +193,7 @@ export default function ClusterComparePage() {
-

CPU / 内存使用率对比

+

{t('k8s.compare.usageCompare')}

@@ -200,8 +202,8 @@ export default function ClusterComparePage() { - - + +
@@ -215,7 +217,7 @@ export default function ClusterComparePage() {
-

资源雷达对比

+

{t('k8s.compare.radarCompare')}

@@ -242,7 +244,7 @@ export default function ClusterComparePage() {
-

节点角色分布

+

{t('k8s.compare.nodeRoleDist')}

{nodeRolePie.length > 0 ? ( @@ -260,7 +262,7 @@ export default function ClusterComparePage() { ) : ( -
无节点角色数据
+
{t('k8s.compare.noNodeRoleData')}
)}
@@ -272,25 +274,25 @@ export default function ClusterComparePage() {
-

详细对比

- 点击行可进入集群概览 +

{t('k8s.compare.detailCompare')}

+ {t('k8s.compare.clickRowHint')}
- 集群 - 状态 - 节点 + {t('k8s.compare.colCluster')} + {t('common.status')} + {t('k8s.compare.colNodes')} Pod Deployment Service - CPU 核数 - 内存 - CPU 使用率 - 内存使用率 - 角色 - 同步时间 + {t('k8s.compare.colCpuCores')} + {t('k8s.compare.colMemory')} + {t('k8s.compare.colCpuUsage')} + {t('k8s.compare.colMemUsage')} + {t('k8s.compare.colRole')} + {t('k8s.compare.colSyncTime')} @@ -305,7 +307,7 @@ export default function ClusterComparePage() { - {c.status === 'healthy' ? '✓ 健康' : c.status === 'warning' ? '⚠ 警告' : '✗ 异常'} + {c.status === 'healthy' ? t('k8s.compare.statusHealthy') : c.status === 'warning' ? t('k8s.compare.statusWarning') : t('k8s.compare.statusError')} {c.node_count} diff --git a/frontend/src/locales/en-US.json b/frontend/src/locales/en-US.json index 011a3cb..acab22c 100644 --- a/frontend/src/locales/en-US.json +++ b/frontend/src/locales/en-US.json @@ -519,7 +519,10 @@ "yesterday": "Yesterday", "zone": "Zone", "zoomIn": "Zoom In", - "zoomOut": "Zoom Out" + "zoomOut": "Zoom Out", + "username": "Username", + "password": "Password", + "port": "Port" }, "menu": { "dashboard": "Dashboard", @@ -1412,7 +1415,46 @@ "selectDockerPwd": "Select Docker password credential", "selectApiCred": "Select credential (API/password etc.)", "selectCred": "Select credential", - "linkedCred": "Linked Credential" + "linkedCred": "Linked Credential", + "form": { + "editTitle": "Edit Credential", + "newTitle": "Add Credential", + "basicInfo": "Basic Information", + "credNamePlaceholder": "e.g. Production SSH", + "varAlias": "Variable Alias", + "varAliasPlaceholder": "e.g. MYSQL_PROD", + "tagsPlaceholder": "production, aliyun", + "descPlaceholder": "Optional remarks", + "typeConfig": "Configuration", + "editHint": "(only fill fields you want to change)", + "sshUser": "SSH User", + "sshPort": "SSH Port", + "sshPassword": "SSH Password", + "sshPwdPlaceholder": "Password (or leave empty to use key)", + "privateKey": "Private Key", + "privateKeyPlaceholder": "Paste SSH private key...", + "publicKey": "Public Key", + "publicKeyPlaceholder": "Paste SSH public key...", + "registryAddr": "Registry Address", + "registryPwdPlaceholder": "Registry password", + "certPem": "Certificate (PEM)", + "keyPem": "Private Key (PEM)", + "caCert": "CA Certificate", + "caCertPlaceholder": "CA certificate content...", + "dbType": "Database Type", + "host": "Host", + "dbName": "Database Name", + "dbPwdPlaceholder": "Database password", + "apiEndpoint": "API Endpoint", + "apiKeyToken": "API Key / Token", + "secretValuePlaceholder": "Secret value", + "saving": "Saving...", + "nameRequired": "Please enter credential name", + "updated": "Updated", + "created": "Created", + "operationFailed": "Operation failed", + "tagsLabel": "Tags" + } }, "release": { "title": "Application Release", @@ -2312,6 +2354,43 @@ "decreaseReplica": "Decrease replicas", "increaseReplica": "Increase replicas", "noPod": "No Pods" + }, + "compare": { + "title": "Multi-Cluster Resource Comparison", + "subtitle": "Cross-cluster resource usage, node distribution, health status comparison", + "clusterList": "Cluster List", + "noData": "No cluster data. Please add a Kubernetes cluster and sync first.", + "goAddCluster": "Add Cluster", + "resourceCompare": "Resource Count Comparison", + "usageCompare": "CPU / Memory Usage Comparison", + "radarCompare": "Resource Radar Comparison", + "nodeRoleDist": "Node Role Distribution", + "noNodeRoleData": "No node role data", + "detailCompare": "Detailed Comparison", + "clickRowHint": "Click a row to view cluster overview", + "loadFailed": "Failed to load", + "neverSynced": "Never synced", + "statusHealthy": "✓ Healthy", + "statusWarning": "⚠ Warning", + "statusError": "✗ Error", + "chartNodes": "Nodes", + "chartPods": "Pods", + "chartDeploy": "Deployments", + "chartCpuUsage": "CPU Usage", + "chartMemUsage": "Memory Usage", + "chartCpuCores": "CPU Cores", + "cpuPercent": "CPU %", + "memPercent": "Memory %", + "colCluster": "Cluster", + "colNodes": "Nodes", + "colCpuCores": "CPU Cores", + "colMemory": "Memory", + "colCpuUsage": "CPU Usage", + "colMemUsage": "Memory Usage", + "colRole": "Role", + "colSyncTime": "Sync Time", + "statCluster": "Clusters", + "statNodes": "Nodes" } }, "monitor": { diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json index 2b5572c..42da37f 100644 --- a/frontend/src/locales/zh-CN.json +++ b/frontend/src/locales/zh-CN.json @@ -519,7 +519,10 @@ "yesterday": "昨天", "zone": "可用区", "zoomIn": "放大", - "zoomOut": "缩小" + "zoomOut": "缩小", + "username": "用户名", + "password": "密码", + "port": "端口" }, "menu": { "dashboard": "仪表盘", @@ -1412,7 +1415,46 @@ "selectDockerPwd": "选择 Docker 密码凭据", "selectApiCred": "选择凭据(API/密码等)", "selectCred": "选择凭证", - "linkedCred": "关联凭证" + "linkedCred": "关联凭证", + "form": { + "editTitle": "编辑凭据", + "newTitle": "新增凭据", + "basicInfo": "基本信息", + "credNamePlaceholder": "如:生产环境 SSH", + "varAlias": "变量别名", + "varAliasPlaceholder": "如:MYSQL_PROD", + "tagsPlaceholder": "生产环境, 阿里云", + "descPlaceholder": "可选备注信息", + "typeConfig": "配置", + "editHint": "(仅填写需要修改的字段)", + "sshUser": "SSH 用户", + "sshPort": "SSH 端口", + "sshPassword": "SSH 密码", + "sshPwdPlaceholder": "密码(或留空用密钥)", + "privateKey": "私钥内容", + "privateKeyPlaceholder": "粘贴 SSH 私钥...", + "publicKey": "公钥内容", + "publicKeyPlaceholder": "粘贴 SSH 公钥...", + "registryAddr": "Registry 地址", + "registryPwdPlaceholder": "Registry 密码", + "certPem": "证书 (PEM)", + "keyPem": "私钥 (PEM)", + "caCert": "CA 证书", + "caCertPlaceholder": "CA 证书内容...", + "dbType": "数据库类型", + "host": "主机", + "dbName": "数据库名", + "dbPwdPlaceholder": "数据库密码", + "apiEndpoint": "API 端点", + "apiKeyToken": "API Key / Token", + "secretValuePlaceholder": "密钥值", + "saving": "保存中...", + "nameRequired": "请填写凭据名称", + "updated": "已更新", + "created": "已创建", + "operationFailed": "操作失败", + "tagsLabel": "标签" + } }, "release": { "title": "应用发布", @@ -2312,6 +2354,43 @@ "decreaseReplica": "减少副本", "increaseReplica": "增加副本", "noPod": "暂无 Pod" + }, + "compare": { + "title": "多集群资源对比看板", + "subtitle": "跨集群资源用量、节点分布、健康状态对比", + "clusterList": "集群列表", + "noData": "暂无集群数据,请先添加 Kubernetes 集群并同步", + "goAddCluster": "去添加集群", + "resourceCompare": "资源数量对比", + "usageCompare": "CPU / 内存使用率对比", + "radarCompare": "资源雷达对比", + "nodeRoleDist": "节点角色分布", + "noNodeRoleData": "无节点角色数据", + "detailCompare": "详细对比", + "clickRowHint": "点击行可进入集群概览", + "loadFailed": "加载失败", + "neverSynced": "从未同步", + "statusHealthy": "✓ 健康", + "statusWarning": "⚠ 警告", + "statusError": "✗ 异常", + "chartNodes": "节点", + "chartPods": "Pod数", + "chartDeploy": "部署数", + "chartCpuUsage": "CPU使用", + "chartMemUsage": "内存使用", + "chartCpuCores": "CPU核数", + "cpuPercent": "CPU %", + "memPercent": "内存 %", + "colCluster": "集群", + "colNodes": "节点", + "colCpuCores": "CPU 核数", + "colMemory": "内存", + "colCpuUsage": "CPU 使用率", + "colMemUsage": "内存使用率", + "colRole": "角色", + "colSyncTime": "同步时间", + "statCluster": "集群", + "statNodes": "节点" } }, "monitor": {