diff --git a/PROJECT_EVALUATION.md b/PROJECT_EVALUATION.md index d25f5a2..f9820bb 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,客户端搜索过滤+删除确认逻辑下沉到子组件)**,累计提取 103 个独立组件文件** | +| 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)**,累计提取 106 个独立组件文件** | ### 3.2 🟡 中优先级 diff --git a/ROADMAP.md b/ROADMAP.md index 1cfae85..9dc5dd3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -501,6 +501,41 @@ services/api.ts (1324L) | Q27 | 前端巨型组件拆分(HostMonitor 547L→224L (-59%),提取 host-monitor/tabs/ 8 个文件 (BasicTab + DiskTab + NetworkTab + PortMonitorTab + ProcessTable + FDUsageCard + ContextSwitchCard + ProcessTab),TABS 常量数组提取为模块级,PCT_REF_LINES 常量提升到 types.ts,端口/进程数组类型 JsonData→JsonData[] 类型安全升级) | P0 | **done** | | Q28 | 前端巨型组件拆分(KubernetesClusterDetail 547L→130L (-76%),提取 cluster-detail/ 8 个文件 (types(computeClusterMetrics/buildTrendData 纯函数) + OverviewTab + NodesTab + PodsTab + DeploymentsTab + ServicesTab + IngressesTab + EventsTab)) | P0 | **done** | | Q29 | 前端巨型组件拆分(AlertCenter 542L→312L (-42%),提取 4 个受控组件 (AlertsToolbar 89L 筛选工具栏 + AlertsTab 149L 告警列表表格+分页 + ContactsTab 87L 联系人卡片网格 + ContactGroupsTab 88L 联系组卡片网格),客户端搜索过滤逻辑移入 AlertsTab,删除确认逻辑下沉到子组件) | P0 | **done** | +| Q30 | 前端巨型组件拆分(CloudBilling 534L→295L (-45%),提取 cloud-billing/ 3 个文件 (OverviewTab 110L 概览统计:同步信息+成本告警横幅+趋势图+厂商分布 + RecordsTab 146L 成本记录表格+筛选工具栏 + AlertRulesTab 91L 预警规则卡片网格)) | P0 | **done** | + +--- + +## Q30: 前端巨型组件拆分 — CloudBilling [done] + +**问题**: `CloudBilling.tsx`(534 行)是 Q29 拆分后项目最大的云管理业务组件之一,包含云账单管理页面的全部逻辑:3 个 Tab(概览统计/成本记录/预警规则)、状态管理(~15 个 useState)、数据加载(loadStats/loadRecords/loadRules)、业务逻辑(同步账单/删除/切换规则)、以及 3 个 Tab 的完整内联 JSX(概览 ~93 行含同步信息横幅+成本告警横幅+趋势图+厂商分布堆叠条形图+明细网格 / 记录 ~62 行含筛选工具栏+表格 / 预警 ~60 行含规则卡片网格)。单文件过大导致维护困难。 + +**已完成 (2026-07-09)**: + +将 3 个 Tab 内容区提取为 `cloud-billing/` 子目录下的独立文件: + +| 新文件 | 行数 | 职责 | +|--------|------|------| +| `OverviewTab.tsx` | 110 | 概览统计 Tab(同步信息横幅 + 成本告警横幅 + 30 天趋势图 + 云厂商分布堆叠条形图+明细网格),受控展示组件接收 stats + statsLoading,内部用 useTranslation + formatCurrency + TrendChart + providerLabels/providerHex/ProviderDot | +| `RecordsTab.tsx` | 146 | 成本记录 Tab(含 `RecordsFilterToolbar` 导出组件: 云厂商下拉+日期范围+查询按钮 + `RecordsTab` 表格组件: 表头+表体+加载/空状态),RecordsFilterToolbar 接收 7 个 props(filter 值+setter+onQuery),RecordsTab 接收 records/loading + onEdit/onDelete/onAdd 回调 | +| `AlertRulesTab.tsx` | 91 | 预警规则 Tab(规则卡片网格: 名称+启用 Switch+厂商+指标+阈值+最近触发时间+编辑/删除按钮 + 加载/空状态),受控展示组件接收 rules/loading + onEdit/onDelete/onToggle/onAdd 回调 | + +主文件 `CloudBilling.tsx` 从 **534 行降至 295 行**(-45%),仅保留:~15 个 useState 状态声明、数据加载(loadStats/loadRecords/loadRules useCallback + useEffect 触发)、业务逻辑(handleSyncYesterday 账单同步 + handleDeleteRecord/handleDeleteRule 带 useConfirm 二次确认 + handleToggleRule)、openAdd/openEdit 回调、pageTabs 配置、PageShell 布局(StatStrip 统计条 + actions 按钮组 + RecordsFilterToolbar 条件传入 toolbar + 3 个 Tab 子组件编排 + 2 个 Dialog 挂载)。 + +**关键改进**: +- **筛选工具栏提取**: 原 `recordsToolbar` 内联 JSX(27 行: Select 下拉+2 个 date Input+查询 Button)提取为 `RecordsFilterToolbar` 独立组件,主组件通过 7 个 props(filter 值+setter+onQuery)控制,零逻辑变更。 +- **受控组件架构**: 3 个 Tab 子组件全部为受控展示组件(接收数据 + 回调 props),无独立状态。所有状态管理保留在主组件,拆分为纯机械操作(移动 JSX + 添加 props 透传),零逻辑变更。 + +**验收标准**: +- [x] CloudBilling.tsx 从 534 行降至 **295 行**(-45%) +- [x] 3 个提取的文件全部 < 150 行(最大 RecordsTab 146 行含 toolbar+table 双导出) +- [x] `tsc -b` 零错误 +- [x] `npm run build` 零错误(✓ built in 17.81s) +- [x] ESLint errors 保持 **0**(CloudBilling + cloud-billing/ 目录零 errors/warnings) +- [x] ESLint warnings 保持 **65**(无新增) +- [x] 全局 ESLint any 保持 **0** +- [x] `go build` 零错误 +- [x] 17/17 集成回归测试通过(零回归) +- [x] 云账单页功能不变(3 个 Tab:概览统计趋势图+厂商分布/成本记录筛选+表格/预警规则卡片+开关全部正常) --- diff --git a/frontend/src/components/cloud/CloudBilling.tsx b/frontend/src/components/cloud/CloudBilling.tsx index f2699f2..478856b 100644 --- a/frontend/src/components/cloud/CloudBilling.tsx +++ b/frontend/src/components/cloud/CloudBilling.tsx @@ -1,46 +1,18 @@ import { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Card, CardContent } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Switch } from '@/components/ui/switch'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { - Plus, - Trash2, - Pencil, - RefreshCw, - Wallet, - Bell, - CloudDownload, - Loader2, - Info, -} from 'lucide-react'; +import { Plus, RefreshCw, Wallet, CloudDownload, Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { billingApi } from '@/api'; -import { - formatCurrency, - TrendChart, - providerLabels, - providerHex, - ProviderDot, -} from './CloudBillingHelpers'; +import { formatCurrency } from './CloudBillingHelpers'; import { StatStrip } from '@/components/ui/stat-strip'; import { BillingRecordDialog } from './BillingRecordDialog'; import { BillingRuleDialog } from './BillingRuleDialog'; import { PageShell } from '@/components/layout/PageShell'; -import { ContentPanel } from '@/components/layout/ContentPanel'; -import { PageLoading } from '@/components/layout/PageLoading'; -import { PageEmpty } from '@/components/layout/PageEmpty'; +import { OverviewTab } from './cloud-billing/OverviewTab'; +import { RecordsTab, RecordsFilterToolbar } from './cloud-billing/RecordsTab'; +import { AlertRulesTab } from './cloud-billing/AlertRulesTab'; import type { JsonData } from '@/types/common'; export function CloudBilling() { @@ -217,34 +189,6 @@ export function CloudBilling() { { id: 'alerts', label: t('cloud.billingAlert'), badge: rules.length > 0 ? rules.length : undefined }, ]; - const recordsToolbar = ( -
{t('cloud.syncBillingInfo')}
-{t('cloud.syncBillingTiming')}
-| {t('cloud.colDate')} | -{t('cloud.vendor')} | -{t('cloud.colService')} | -{t('cloud.colRegion')} | -{t('cloud.colAccount')} | -{t('cloud.colCost')} | -{t('cloud.colDescription')} | -{t('common.actions')} | -
|---|---|---|---|---|---|---|---|
| {r.billing_date} | -
-
- |
- {r.service || '-'} | -{r.region || '-'} | -{r.account_name || '-'} | -{formatCurrency(r.cost)} | -{r.description || '-'} | -
-
-
-
-
- |
-
{t('cloud.syncBillingInfo')}
+{t('cloud.syncBillingTiming')}
+| {t('cloud.colDate')} | +{t('cloud.vendor')} | +{t('cloud.colService')} | +{t('cloud.colRegion')} | +{t('cloud.colAccount')} | +{t('cloud.colCost')} | +{t('cloud.colDescription')} | +{t('common.actions')} | +
|---|---|---|---|---|---|---|---|
| {r.billing_date} | +
+
+ |
+ {r.service || '-'} | +{r.region || '-'} | +{r.account_name || '-'} | +{formatCurrency(r.cost)} | +{r.description || '-'} | +
+
+
+
+
+ |
+