diff --git a/ROADMAP.md b/ROADMAP.md index 7472f9f..0538e5e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -476,6 +476,7 @@ services/api.ts (1324L) | U12 | 工程质量回归修复(ESLint errors + React Hooks 关键规则) | P3 | **done** | | Q4+ | 后端错误处理治理扩展(Q5: RDP/API Token/AIOps 模块 `_ =` DB 写入) | P0 | **done** | | U13 | 主机监控大屏挂载(HostMonitorDashboard 死代码复活) | P3 | **done** | +| U14 | ESLint 冗余 disable 指令清理(unused eslint-disable 6→0) | P3 | **done** | --- @@ -581,3 +582,31 @@ pending → in_progress → done / blocked - [x] 17/17 集成回归测试通过(零回归) - [x] Docker 部署 healthy - [x] PROJECT_EVALUATION.md 3.3 死代码项已清除 + +--- + +## U14: ESLint 冗余 disable 指令清理 [done] + +**问题**: U12 工程质量回归修复时,为抑制 React Hooks 关键规则(set-state-in-effect / preserve-manual-memoization)在 6 处标注了 `// eslint-disable-next-line`。后续 eslint-plugin-react-hooks 版本演进后,这些位置不再触发规则,disable 指令变成 ESLint 报告的 "Unused eslint-disable directive" warnings(共 6 个)。 + +**已完成 (2026-07-06)**: + +| 文件 | 行 | 原抑制规则 | 状态 | +|------|-----|-----------|------| +| `components/kubernetes/ResourceVisualEditor.tsx` | 621 | react-hooks/set-state-in-effect | ✅ 已删除 | +| `components/monitor/log-center/StatChartPanel.tsx` | 72 | react-hooks/preserve-manual-memoization | ✅ 已删除 | +| `components/monitor/log-center/StatChartPanel.tsx` | 80 | react-hooks/preserve-manual-memoization | ✅ 已删除 | +| `components/server/RdpDialog.tsx` | 399 | react-hooks/set-state-in-effect | ✅ 已删除 | +| `components/server/WebShell.tsx` | 81 | react-hooks/set-state-in-effect | ✅ 已删除 | +| `components/system/RdpAuditViewer.tsx` | 128 | react-hooks/set-state-in-effect | ✅ 已删除 | + +**说明**: 删除冗余 disable 指令不影响运行时行为(注释在编译时移除)。这些位置在 U12 标注时确有规则触发,但规则版本变化后不再触发——保留它们反而引入 ESLint warnings。6 处 useEffect/useMemo 内的 setState/依赖模式本身是安全的(mount-only 初始化或受控 memoization),无需抑制。 + +**验收标准**: +- [x] ESLint "Unused eslint-disable directive" 从 6 降至 **0** +- [x] ESLint errors 保持 **0**(无新增) +- [x] ESLint warnings 从 65 降至 **59**(减少 6) +- [x] `tsc -b` 零错误 +- [x] `npm run build` 零错误(✓ built in 16.93s) +- [x] 17/17 集成回归测试通过(零回归) +- [x] Docker 部署 healthy(改动不影响运行时) diff --git a/frontend/src/components/kubernetes/ResourceVisualEditor.tsx b/frontend/src/components/kubernetes/ResourceVisualEditor.tsx index 63f67d3..f3bd060 100644 --- a/frontend/src/components/kubernetes/ResourceVisualEditor.tsx +++ b/frontend/src/components/kubernetes/ResourceVisualEditor.tsx @@ -618,7 +618,6 @@ function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate }: { ma // 加载集群 IngressClass 列表 useEffect(() => { if (!clusterCode) return; - // eslint-disable-next-line react-hooks/set-state-in-effect setLoadingClasses(true); kubernetesApi.getIngressClasses(clusterCode).then((res: any) => { const list = Array.isArray(res) ? res : (res?.list || res || []); diff --git a/frontend/src/components/monitor/log-center/StatChartPanel.tsx b/frontend/src/components/monitor/log-center/StatChartPanel.tsx index 1e789e5..116ef32 100644 --- a/frontend/src/components/monitor/log-center/StatChartPanel.tsx +++ b/frontend/src/components/monitor/log-center/StatChartPanel.tsx @@ -69,7 +69,6 @@ export function StatChartPanel({ statTable, engine, query, onSaveAsAlert, onSave return samples[engine]; }, [engine]); - // eslint-disable-next-line react-hooks/preserve-manual-memoization const chartData = useMemo(() => { if (!rows.length) return []; return rows.map(r => { @@ -77,7 +76,6 @@ export function StatChartPanel({ statTable, engine, query, onSaveAsAlert, onSave columns.forEach(c => { obj[c.name] = r[c.name]; }); return obj; }); - // eslint-disable-next-line react-hooks/preserve-manual-memoization }, [rows, columns]); // 判断是否有数据 diff --git a/frontend/src/components/server/RdpDialog.tsx b/frontend/src/components/server/RdpDialog.tsx index 2ca81eb..d8bfddb 100644 --- a/frontend/src/components/server/RdpDialog.tsx +++ b/frontend/src/components/server/RdpDialog.tsx @@ -396,7 +396,6 @@ export function RdpDialog({ useUserStore.getState().token || localStorage.getItem("token") || ""; const { width, height } = calcRdpViewport(false); // 会话分辨率跟随弹窗可用空间,减少大屏下固定 1024x800 造成的左右黑边。 // Logan.Li - // eslint-disable-next-line react-hooks/set-state-in-effect setDisplaySize({ width, height }); setCanvasSize({ width, height }); setStatus(t("server.rdpDialog.creating")); diff --git a/frontend/src/components/server/WebShell.tsx b/frontend/src/components/server/WebShell.tsx index 3b88724..5a40c32 100644 --- a/frontend/src/components/server/WebShell.tsx +++ b/frontend/src/components/server/WebShell.tsx @@ -78,7 +78,6 @@ export function WebShell() { }; useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect loadServers(); }, []); diff --git a/frontend/src/components/system/RdpAuditViewer.tsx b/frontend/src/components/system/RdpAuditViewer.tsx index a22537f..efa9f13 100644 --- a/frontend/src/components/system/RdpAuditViewer.tsx +++ b/frontend/src/components/system/RdpAuditViewer.tsx @@ -125,7 +125,6 @@ export function RdpAuditViewer({ open, onOpenChange, sessionId, mode }: RdpAudit useEffect(() => { if (!open || !sessionId) return; - // eslint-disable-next-line react-hooks/set-state-in-effect setStatus(mode === 'live' ? '正在连接 RDP 实时审计...' : '正在加载 RDP 录屏...'); setLoading(true); setFrames([]);