From 34050f5188c40af3847acc5dc55e7df1f3a1be02 Mon Sep 17 00:00:00 2001 From: attacker Date: Fri, 10 Jul 2026 20:39:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor(quality):=20Q37=20k8s-yaml-utils.ts=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=96=87=E4=BB=B6=E6=8B=86=E5=88=86=20?= =?UTF-8?q?=E2=80=94=20504L=E2=86=92451L=20+=20k8s-helpers.ts=2067L=20?= =?UTF-8?q?=E6=96=B0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 提取 7 个通用工具函数到 k8s-helpers.ts (parseScalar/STATUS_COLORS/statusColor/aggregateByImage/formatK8sTime/elapsedSeconds/formatDuration),barrel re-export 保持 8 个消费者零修改,tsc -b=0,npm build✓ (17.49s),ESLint=0/66/0,go build=0,17/17 测试通过 --- ROADMAP.md | 1 + .../src/components/kubernetes/k8s-helpers.ts | 67 +++++++++++++++++ .../components/kubernetes/k8s-yaml-utils.ts | 75 +++---------------- 3 files changed, 79 insertions(+), 64 deletions(-) create mode 100644 frontend/src/components/kubernetes/k8s-helpers.ts diff --git a/ROADMAP.md b/ROADMAP.md index 6cca4e8..41e93ab 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -509,6 +509,7 @@ services/api.ts (1324L) | Q35 | 前端巨型组件拆分(AgentCLI 521L→399L (-23%),提取 agent-cli/ 2 个文件 (event-handler.ts 149L createWsEventHandler 工厂函数封装 8 种 WS 事件分发+高危命令确认 + ChatInputArea.tsx 44L 受控输入区组件),导出 ConfirmOptions 接口供跨模块引用,tsc -b=0,npm build✓,ESLint=0/66/0,go build=0,17/17 测试通过。最后一个 ≥500L 业务组件拆分完成,剩余最大业务组件 UnifiedQueryPanel 529L(已从 682L 拆分)) | P0 | **done** | | Q36 | 前端巨型工具文件拆分(predictUtils.ts 716L→49L barrel re-export (-93%),提取 predict-types.ts 140L 类型+常量 + capacity-predict.ts 194L 容量预测函数(linearRegression+computePredictions+buildCapacityInsightPrompt) + cost-predict.ts 434L 云成本预测函数(computeCostPredictions+buildCostInsightPrompt),3 个消费者 import 路径无需修改,tsc -b=0,npm build✓ (17.26s),ESLint=0/66/0,go build=0,17/17 测试通过) | P0 | **done** | | U15 | ApiAccessKeySection i18n 迁移(清理 56 个 apiaccesskey.s{hash} 乱码键为语义化 user.apiKey.* 键 + 组件全量接入 useTranslation,UI 中文 57→0,zh/en 7556=7556 对齐,零 hash 键残留) | P3 | **done** | +| Q37 | 前端巨型工具文件拆分(k8s-yaml-utils.ts 504L→451L + k8s-helpers.ts 67L 新文件,提取 7 个通用工具函数 parseScalar/STATUS_COLORS/statusColor/aggregateByImage/formatK8sTime/elapsedSeconds/formatDuration,barrel re-export 保持 8 个消费者零修改) | P0 | **done** | --- diff --git a/frontend/src/components/kubernetes/k8s-helpers.ts b/frontend/src/components/kubernetes/k8s-helpers.ts new file mode 100644 index 0000000..61953b0 --- /dev/null +++ b/frontend/src/components/kubernetes/k8s-helpers.ts @@ -0,0 +1,67 @@ +// k8s-helpers.ts — K8s 通用工具函数(状态色/时间格式化/镜像聚合) +// 从 k8s-yaml-utils.ts 提取,供 K8s 模块各组件复用 + +/** 解析简单 "key: value" 行 */ +export function parseScalar(line: string): { key: string; value: string } | null { + const m = line.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/); + if (!m) return null; + return { key: m[1], value: m[2].trim().replace(/^["']|["']$/g, '') }; +} + +/** 状态色 */ +export const STATUS_COLORS = { + green: '#10b981', + orange: '#f59e0b', + red: '#ef4444', + blue: '#326CE5', +} as const; + +export function statusColor(status: string | undefined): string { + if (!status) return '#64748b'; + const s = status.toLowerCase(); + if (['running', 'available', 'ready', 'active', 'completed', 'succeeded'].includes(s)) return STATUS_COLORS.green; + if (['pending', 'updating', 'progressing', 'containercreating'].includes(s)) return STATUS_COLORS.orange; + if (['failed', 'error', 'crashloopbackoff', 'imagepullbackoff', 'errimageneverpull'].includes(s)) return STATUS_COLORS.red; + return STATUS_COLORS.blue; +} + +/** 按镜像聚合 */ +export function aggregateByImage(items: T[]): Array { + const map = new Map(); + for (const it of items) { + const img = it.image || (Array.isArray(it.images) ? it.images[0] : '') || 'unknown'; + const existing = map.get(img); + if (existing) { + existing._count += 1; + } else { + map.set(img, { ...it, _count: 1 }); + } + } + return Array.from(map.values()); +} + +/** 格式化 K8s 时间戳 */ +export function formatK8sTime(ts: string | undefined | null): string { + if (!ts) return '-'; + const d = new Date(ts); + if (isNaN(d.getTime())) return ts; + const pad = (n: number) => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; +} + +export function elapsedSeconds(start?: string | null, end?: string | null): number | null { + if (!start || !end) return null; + const s = new Date(start).getTime(); + const e = new Date(end).getTime(); + if (isNaN(s) || isNaN(e) || e < s) return null; + return Math.max(0, Math.round((e - s) / 1000)); +} + +export function formatDuration(sec: number | null | undefined): string { + if (sec == null) return '-'; + if (sec < 60) return `${sec}s`; + if (sec < 3600) return `${Math.floor(sec / 60)}m${sec % 60}s`; + const h = Math.floor(sec / 3600); + const m = Math.floor((sec % 3600) / 60); + return `${h}h${m}m`; +} diff --git a/frontend/src/components/kubernetes/k8s-yaml-utils.ts b/frontend/src/components/kubernetes/k8s-yaml-utils.ts index 54d3cc6..a63a215 100644 --- a/frontend/src/components/kubernetes/k8s-yaml-utils.ts +++ b/frontend/src/components/kubernetes/k8s-yaml-utils.ts @@ -438,67 +438,14 @@ function isSystemMountPath(line: string): boolean { ); } -/** 解析简单 "key: value" 行 */ -export function parseScalar(line: string): { key: string; value: string } | null { - const m = line.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/); - if (!m) return null; - return { key: m[1], value: m[2].trim().replace(/^["']|["']$/g, '') }; -} - -/** 状态色 */ -export const STATUS_COLORS = { - green: '#10b981', - orange: '#f59e0b', - red: '#ef4444', - blue: '#326CE5', -} as const; - -export function statusColor(status: string | undefined): string { - if (!status) return '#64748b'; - const s = status.toLowerCase(); - if (['running', 'available', 'ready', 'active', 'completed', 'succeeded'].includes(s)) return STATUS_COLORS.green; - if (['pending', 'updating', 'progressing', 'containercreating'].includes(s)) return STATUS_COLORS.orange; - if (['failed', 'error', 'crashloopbackoff', 'imagepullbackoff', 'errimageneverpull'].includes(s)) return STATUS_COLORS.red; - return STATUS_COLORS.blue; -} - -/** 按镜像聚合 */ -export function aggregateByImage(items: T[]): Array { - const map = new Map(); - for (const it of items) { - const img = it.image || (Array.isArray(it.images) ? it.images[0] : '') || 'unknown'; - const existing = map.get(img); - if (existing) { - existing._count += 1; - } else { - map.set(img, { ...it, _count: 1 }); - } - } - return Array.from(map.values()); -} - -/** 格式化 K8s 时间戳 */ -export function formatK8sTime(ts: string | undefined | null): string { - if (!ts) return '-'; - const d = new Date(ts); - if (isNaN(d.getTime())) return ts; - const pad = (n: number) => String(n).padStart(2, '0'); - return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; -} - -export function elapsedSeconds(start?: string | null, end?: string | null): number | null { - if (!start || !end) return null; - const s = new Date(start).getTime(); - const e = new Date(end).getTime(); - if (isNaN(s) || isNaN(e) || e < s) return null; - return Math.max(0, Math.round((e - s) / 1000)); -} - -export function formatDuration(sec: number | null | undefined): string { - if (sec == null) return '-'; - if (sec < 60) return `${sec}s`; - if (sec < 3600) return `${Math.floor(sec / 60)}m${sec % 60}s`; - const h = Math.floor(sec / 3600); - const m = Math.floor((sec % 3600) / 60); - return `${h}h${m}m`; -} +// Barrel re-export: 通用工具函数已提取到 k8s-helpers.ts +// 保留 re-export 以兼容现有 import 路径(8 个消费者无需修改) +export { + parseScalar, + STATUS_COLORS, + statusColor, + aggregateByImage, + formatK8sTime, + elapsedSeconds, + formatDuration, +} from './k8s-helpers';