diff --git a/PROJECT_EVALUATION.md b/PROJECT_EVALUATION.md index 8e0a10c..236dcaf 100644 --- a/PROJECT_EVALUATION.md +++ b/PROJECT_EVALUATION.md @@ -129,7 +129,7 @@ | ~~`api.ts` 1324 行/352 方法~~ → 已拆分为 12 个域文件 (api/kubernetes.ts, deploy.ts, monitor.ts 等) | ✅ 已解决 | 前端 | ~~P0~~ done | | 前端 **1026 处** `any` 类型 | 类型安全缺失,重构风险高 | 前端全局 | ~~P0~~ **done** (any 1076→40, unused 25→6, console 93→1, tsc --noEmit=0) | | ~~K8s 双路由体系(集群域+Kuboard 域)~~ → code 路由统一为主,ID 路由标记 deprecated 向后兼容 | ✅ 已解决 | 后端 | ~~P1~~ done | -| 25 个 ≥500 行巨型组件 | 拆分困难 | 前端 | P1 → **Q6-Q27 done: 最大组件 ResourceVisualEditor 882L→282L (-68%) + AgentCLI 814L→521L (-36%) + SystemSettingsTab 754L→173L (-77%) + NamespaceResourcePage 719L→300L (-58%) + ApprovalWorkflowPage 705L→167L (-76%) + PipelineRunDetail 678L→273L (-60%) + CloudServerImport 699L→404L (-42%,any[]→具体接口) + UnifiedQueryPanel 682L→529L (-22%) + ServerManager 652L→442L (-32%) + PipelineEditor 676L→406L (-40%) + RdpDialog 642L→442L (-31%) + DeploymentEditService 627L→394L (-37%) + TraceManager 605L→331L (-45%,修复 SpanTreeNode 内联定义反模式) + GrafanaDashboard 624L→78L (-87%,DRY 合并 Zabbix/Prometheus Tab + 移除文件级 eslint-disable + 类型安全升级) + TopologyView 596L→472L (-21%) + DeploymentEditAdvanced 590L→289L (-51%) + WorkloadViewPage 572L→348L (-39%) + CloudAccountManager 572L→308L + ExcelServerImport 560L→385L + PipelineCreateDialog 559L→498L + PodLogsViewer 553L→331L (-40%) + HostMonitor 547L→224L (-59%) + KubernetesClusterDetail 547L→130L (-76%) + **AlertCenter 542L→312L (-42%,提取 4 个受控组件: AlertsToolbar + AlertsTab + ContactsTab + ContactGroupsTab,客户端搜索过滤+删除确认逻辑下沉到子组件)** + **CloudBilling 534L→295L (-45%,提取 cloud-billing/ 3 个文件: OverviewTab + RecordsTab(含 RecordsFilterToolbar) + AlertRulesTab)** + **PromDsFormDialog 528L→294L (-44%,提取 6 个文件: CollapsibleSection 通用折叠包装器 DRY 3 个重复折叠模式 + AuthSection + HeadersSection + AlertingSection + MiscSection + TestResultBanner)**,累计提取 112 个独立组件文件 + **AuditOperationsTab 527L→275L (-48%,提取 audit-operations/ 3 个文件: types(normalizeLog 纯函数+AuditStatistics 接口) + LogDetailDialog(85L 详情弹窗+DetailRow) + StatisticsDialog(108L 统计弹窗+StatCard),清除死代码 useConfirm/_confirm/_handleShowStats/statsOpen/statistics/statsLoading)**,累计提取 115 个独立组件文件** | +| 25 个 ≥500 行巨型组件 | 拆分困难 | 前端 | P1 → **Q6-Q27 done: 最大组件 ResourceVisualEditor 882L→282L (-68%) + AgentCLI 814L→521L (-36%) + SystemSettingsTab 754L→173L (-77%) + NamespaceResourcePage 719L→300L (-58%) + ApprovalWorkflowPage 705L→167L (-76%) + PipelineRunDetail 678L→273L (-60%) + CloudServerImport 699L→404L (-42%,any[]→具体接口) + UnifiedQueryPanel 682L→529L (-22%) + ServerManager 652L→442L (-32%) + PipelineEditor 676L→406L (-40%) + RdpDialog 642L→442L (-31%) + DeploymentEditService 627L→394L (-37%) + TraceManager 605L→331L (-45%,修复 SpanTreeNode 内联定义反模式) + GrafanaDashboard 624L→78L (-87%,DRY 合并 Zabbix/Prometheus Tab + 移除文件级 eslint-disable + 类型安全升级) + TopologyView 596L→472L (-21%) + DeploymentEditAdvanced 590L→289L (-51%) + WorkloadViewPage 572L→348L (-39%) + CloudAccountManager 572L→308L + ExcelServerImport 560L→385L + PipelineCreateDialog 559L→498L + PodLogsViewer 553L→331L (-40%) + HostMonitor 547L→224L (-59%) + KubernetesClusterDetail 547L→130L (-76%) + **AlertCenter 542L→312L (-42%,提取 4 个受控组件: AlertsToolbar + AlertsTab + ContactsTab + ContactGroupsTab,客户端搜索过滤+删除确认逻辑下沉到子组件)** + **CloudBilling 534L→295L (-45%,提取 cloud-billing/ 3 个文件: OverviewTab + RecordsTab(含 RecordsFilterToolbar) + AlertRulesTab)** + **PromDsFormDialog 528L→294L (-44%,提取 6 个文件: CollapsibleSection 通用折叠包装器 DRY 3 个重复折叠模式 + AuthSection + HeadersSection + AlertingSection + MiscSection + TestResultBanner)**,累计提取 112 个独立组件文件 + **AuditOperationsTab 527L→275L (-48%,提取 audit-operations/ 3 个文件: types(normalizeLog 纯函数+AuditStatistics 接口) + LogDetailDialog(85L 详情弹窗+DetailRow) + StatisticsDialog(108L 统计弹窗+StatCard),清除死代码 useConfirm/_confirm/_handleShowStats/statsOpen/statistics/statsLoading)**,累计提取 115 个独立组件文件** + **AgentCLI 521L→399L (-23%,提取 agent-cli/ 2 个文件: event-handler.ts createWsEventHandler 工厂函数封装 8 种 WS 事件分发+高危命令确认 + ChatInputArea.tsx 受控输入区组件),累计提取 117 个独立组件文件** | ### 3.2 🟡 中优先级 diff --git a/ROADMAP.md b/ROADMAP.md index 88359a3..2db92b4 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -506,6 +506,7 @@ services/api.ts (1324L) | Q32 | 前端巨型组件拆分(AuditOperationsTab 527L→275L (-48%),提取 audit-operations/ 3 个文件 (types 43L normalizeLog 纯函数+AuditStatistics 接口 + LogDetailDialog 85L 详情弹窗+DetailRow + StatisticsDialog 108L 统计弹窗+StatCard),清除死代码 useConfirm/_confirm/_handleShowStats/statsOpen/statistics/statsLoading,ESLint errors=0/any=0,tsc -b=0,npm build✓,go build=0,17/17 测试通过) | P0 | **done** | | Q33 | 前端巨型组件拆分(DashboardRecent 518L→25L (-95%),提取 dashboard-recent/ 7 个文件 (types 48L + shared-components(StatusDot/SeverityBadge) 35L + AlertMessagesCard 72L + CertificateExpiryCard 99L + ReleaseHistoryCard 99L + OpsOverviewCard 77L + KubernetesClusterCard 97L),主文件改为 barrel re-export 保持向后兼容,tsc -b=0,npm build✓,ESLint=0/0/0,go build=0,17/17 测试通过) | P0 | **done** | | Q34 | 前端巨型组件拆分(AIOpsAssistant 505L→288L (-43%),提取 aiops-assistant/ 5 个文件 (types 39L Message/QuickAction/defaultConfig/fmtTime + context-loaders.ts 69L loadAlerts/loadMetrics/loadReport 3 个平台数据加载纯函数 + quick-actions.tsx 17L + ChatMessage 47L 消息气泡(含 streaming/thinking/error 状态) + AssistantSidebar 76L 快捷操作+能力卡片),tsc -b=0,npm build✓,ESLint=0/0/0,go build=0,17/17 测试通过) | P0 | **done** | +| 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** | --- diff --git a/frontend/src/components/aiops/AgentCLI.tsx b/frontend/src/components/aiops/AgentCLI.tsx index ab781e7..6a0815b 100644 --- a/frontend/src/components/aiops/AgentCLI.tsx +++ b/frontend/src/components/aiops/AgentCLI.tsx @@ -10,7 +10,7 @@ import { kubernetesApi } from '@/api/kubernetes'; import { buildAgentCliWsUrl, type AgentCliMode } from '@/api/aiops/agent-cli'; import { toast } from 'sonner'; import { - Send, Square, Bot, + Bot, } from 'lucide-react'; import AgentCLIHistory from './AgentCLIHistory'; import { AgentChatTimeline, type ChatItem } from './AgentCLIMessages'; @@ -20,6 +20,8 @@ import ScrollButtons from '@/components/ScrollButtons'; import type { AgentCliSession } from '@/api/aiops/agent-cli'; import { PageShell } from '@/components/layout/PageShell'; import { POD_NONE, type WsEvent } from './agent-cli-constants'; +import { createWsEventHandler } from './agent-cli/event-handler'; +import ChatInputArea from './agent-cli/ChatInputArea'; export default function AgentCLI() { const confirm = useConfirm(); @@ -58,6 +60,13 @@ export default function AgentCLI() { setItems((prev) => [...prev, { ...it, id: nextId() }]); }, []); + // WebSocket 事件处理(提取到 event-handler.ts) // Logan.Li + const { handleEvent } = createWsEventHandler({ + push, nextId, setRound, setTokens, setRunning, setCanContinue, + setItems, setHistoryRefresh, setViewMode, + confirm, wsRef, + }); + const selectedCluster = clusters.find((c) => c.code === clusterCode); const wsReady = cliMode === 'host' ? Boolean(serverId) : Boolean(clusterCode && namespace); @@ -214,123 +223,6 @@ export default function AgentCLI() { return () => ro.disconnect(); }, [items.length, updateScrollState]); - // 处理后端事件 // Logan.Li - const handleEvent = (ev: WsEvent) => { - const type = ev.type; - switch (type) { - case 'session': - push({ kind: 'info', content: ev.message || '' }); - // 仅「恢复历史会话」时进入可追问(新任务开始的 session 事件文案不同) // Logan.Li - if (ev.message?.includes('已恢复历史会话')) { - setViewMode(false); - setCanContinue(true); - } - break; - case 'progress': - if (ev.round) setRound(ev.round); - if (ev.tokens) setTokens(ev.tokens); - break; - case 'thought_delta': - setItems((prev) => { - const next = [...prev]; - const last = next[next.length - 1]; - if (last?.kind === 'thought' && last.streaming && last.round === ev.round) { - next[next.length - 1] = { ...last, content: (last.content || '') + (ev.thought || '') }; - } else { - next.push({ id: nextId(), kind: 'thought', round: ev.round, content: ev.thought || '', streaming: true }); - } - return next; - }); - break; - case 'thought': - setItems((prev) => { - const next = [...prev]; - const last = next[next.length - 1]; - if (last?.kind === 'thought' && last.streaming && last.round === ev.round) { - next[next.length - 1] = { ...last, content: ev.thought || last.content, streaming: false }; - } else { - next.push({ id: nextId(), kind: 'thought', round: ev.round, content: ev.thought || '' }); - } - return next; - }); - break; - case 'tool_call': { - const args = ev.tool_args || {}; - push({ - kind: 'tool', round: ev.round, toolName: ev.tool_name, - command: String(args.command || (args.action ? `${args.action} ${args.name || ''}` : '')), risk: ev.risk, pending: true, - }); - break; - } - case 'tool_result': - setItems((prev) => { - const next = [...prev]; - for (let i = next.length - 1; i >= 0; i--) { - if (next[i].kind === 'tool' && next[i].pending && next[i].toolName === ev.tool_name) { - next[i] = { - ...next[i], output: ev.output, success: ev.success, - duration: ev.duration, pending: false, - }; - break; - } - } - return next; - }); - break; - case 'confirm': - handleConfirm(ev); - break; - case 'done': { - const text = (ev.answer || ev.message || '').trim(); - setItems((prev) => { - const norm = (s?: string) => (s || '').trim().replace(/\s+/g, ' '); - // 移除与 done 同文的 thought,避免终局总结显示两遍 // Logan.Li - const next = prev.filter((it) => { - if (it.kind !== 'thought' || !text) return true; - return norm(it.content) !== norm(text); - }); - const last = next[next.length - 1]; - if (last?.kind === 'done') { - if (text) { - next[next.length - 1] = { ...last, content: text, round: ev.round ?? last.round }; - } - return next; - } - return [...next, { - id: nextId(), - kind: 'done', - content: text || '任务已完成,请查看上方工具执行结果。', - round: ev.round, - }]; - }); - setRunning(false); - setCanContinue(true); - setHistoryRefresh((k) => k + 1); - break; - } - case 'error': - case 'aborted': - push({ kind: type === 'aborted' ? 'info' : 'error', content: ev.message || (type === 'aborted' ? '任务已中断' : '发生错误') }); - setRunning(false); - setCanContinue(type !== 'aborted'); - setHistoryRefresh((k) => k + 1); - break; - } - }; - - // 高危命令确认(后端驱动 → 弹框 → 回传) // Logan.Li - const handleConfirm = async (ev: WsEvent) => { - const ok = await confirm({ - title: '⚠️ 确认执行高危操作', - description: `工具:${ev.tool_name}\n风险:${ev.risk}\n命令:${ev.command || '(无)'}`, - confirmText: '确认执行', - cancelText: '拒绝', - variant: 'destructive', - }); - wsRef.current?.send(JSON.stringify({ type: ok ? 'confirm' : 'deny', confirm_id: ev.confirm_id })); - if (!ok) push({ kind: 'info', content: `已拒绝执行:${ev.command || ev.tool_name}` }); - }; - // 发送任务 // Logan.Li const send = () => { const q = input.trim(); @@ -486,35 +378,21 @@ export default function AgentCLI() { /> - {/* 加宽输入区:多行 + 独立操作栏 // Logan.Li */} -