目标文件:
ai-frontend/src/case/views/data/cleaning.vue(12029 行) 本文只做方案,不改代码。审核通过后按「六、分阶段实施计划」执行。
| 区块 | 行号 | 行数 | 占比 |
|---|---|---|---|
<template> |
1–1502 | 1502 | 12.5% |
<script setup lang="ts"> |
1504–8209 | 6706 | 55.7% |
<style lang="less"> |
8211–12029 | 3819 | 31.8% |
ref / computed / function / type / interface)约 380+ 个.jeesite-case-data-cleaning.case-data-cleaning-page 一层就 2708 行定性结论:这不是"某个组件写胖了",而是一个文件里塞了 6 个业务域 + 4 个弹窗 + 3 栏骨架 + 全局样式。因此拆分必须按业务域切,不能按段落切。
| 行号 | 块 | 说明 |
|---|---|---|
| 3–16 | 页头 | 标题 / 返回 / 收起左侧 / 导入数据 |
| 18–78 | 三栏骨架 | main-layout + panel-resizer |
| 19–76 | 左栏 left-panel |
BasicTree 文件树 + 图例 |
| 80–453 | 中栏 middle-panel |
模板匹配条 → 字段映射卡(98–228) → 识别规则(230–395) → 子模板(396–408) → 空态(413–452) |
| 457–1162 | 右栏 right-panel |
列操作工具条 → 数据预览表(487–576) → 清洗规则面板(577–1173) |
| 1165–1268 | Modal | 新增 / 编辑列 |
| 1269–1324 | Modal | 拆分列 |
| 1325–1328 | Modal | 删除列(4 行,不单独拆) |
| 1329–1502 | Modal | 模板库选择 |
清洗规则面板内部再分四栏:规则库(604–619) / 操作箭头(620–624) / 已选规则(625–644) / 规则参数配置(646–1158)。
| 行号 | 业务域 | 行数 |
|---|---|---|
| 1504–1675 | import + 类型定义 | 172 |
| 1676–2110 | 通用 normalize / 规则 flag 解析 / 模板字段 meta | 435 |
| 2010–2340 | mock 数据 + 文件树构建·排序·查找 | 330 |
| 2342–2643 | 列头与目标字段、规则常量、分组列草稿 | 300 |
| 2644–3045 | 节点级状态缓存(nodeStateCache / analyzingNodeKeys) |
400 |
| 3046–3176 | 预览数据 & 快照 | 130 |
| 3177–3737 | 规则序列化 / 反序列化(写回树) | 560 |
| 3738–3990 | tableRule 构建 / 模板字段 meta / 规则面板 computed | 250 |
| 3991–4324 | 格式校验(日期·时间·数字·手机·卡号·身份证·借贷标志·通信类别) | 334 |
| 4325–4560 | 目标格式校验 map + 匹配状态 | 236 |
| 4565–4723 | 表头标签构建 / 规则重映射 | 160 |
| 4724–4935 | 表头行应用 / 预览表格辅助 | 210 |
| 4936–5485 | 列操作(增·拆·删)+ 映射浮层交互 | 550 |
| 5486–5585 | 子模板命名校验 | 100 |
| 5586–6168 | 识别规则(方向配置 / 基本信息 / 正负开关) | 583 |
| 6168–6285 | 自动字段匹配 | 117 |
| 6285–6890 | 模板库 Modal 逻辑 | 605 |
| 6890–7103 | 页面级 computed + 面板拖拽 | 215 |
| 7104–7596 | 列操作 Modal 逻辑 + 生成列重建 | 493 |
| 7597–7996 | 清洗规则交互 + 13 条规则的 apply 逻辑 | 400 |
| 7996–8209 | 导入 / 树加载 / 生命周期 | 214 |
| 行号 | 选择器 | 行数 |
|---|---|---|
| 8214–10922 | .jeesite-case-data-cleaning.case-data-cleaning-page(巨型嵌套块) |
2708 |
| 10925–10996 | .cleaning-config-modal / .cleaning-modal-title |
71 |
| 10998–11073 | .split-column-modal / .split-column-form |
75 |
| 11075–11366 | .add-column-modal / .add-col-dialog |
291 |
| 11368–11467 | .origin-field-popover |
100 |
| 11469–12018 | .select-template-modal |
550 |
| 12020–12028 | @keyframes |
9 |
8214–10922 内部二级块的行数分布,直接决定每个组件自带多少样式:
| 子块 | 行号 | 行数 | 归属 |
|---|---|---|---|
| page-header / 布局 / resizer | 8229–8331 | 103 | index.vue |
| tree-header / tree-wrap / legend | 8332–8519 | 188 | FileTreePanel |
| panel-header / right-panel-body | 8520–8550 | 31 | index.vue |
| template-match-bar | 8551–8654 | 104 | MiddlePanel |
| middle-empty-* | 8655–8860 | 206 | MiddleEmptyState |
| mapping-* | 8861–9256 | 396 | FieldMappingCard |
| recognition-* | 9257–9842 | 586 | RecognitionRulePanel |
| child-template-* | 9843–9880 | 38 | ChildTemplatePanel |
| cleaning-rule-panel | 9881–10479 | 599 | CleaningRulePanel |
| table-mock | 10480–10810 | 331 | DataPreviewTable |
| @media | 10811–10921 | 111 | index.vue |
utils/,不依赖 Vue 响应式。composables/,每个域一个文件。keep-alive 名称、sessionStorage key、接口调用、交互流程全部不动。pnpm type:check(vue-tsc --noEmit --skipLibCheck)零错误pnpm build 通过cleaning/index.vue ≤ 400 行;其余单文件 ≤ 900 行cleanProgress.vue / upload.vue 等兄弟页面src/case/views/data/
├── cleaning/ # 新目录(原 cleaning.vue 迁移为 index.vue)
│ ├── index.vue # 页面容器:三栏 + 拖拽 + 编排 ~350
│ ├── types.ts # 全部类型定义 ~260
│ ├── constants.ts # 规则名/示例/选项/存储 key 等常量 ~300
│ ├── context.ts # provide/inject key + 上下文类型 ~80
│ │
│ ├── utils/ # 纯函数层(零 Vue 依赖)
│ │ ├── treeUtils.ts # 文件树构建/排序/查找 ~300
│ │ ├── formatValidate.ts # 8 类格式校验(日期…身份证/借贷标志) ~340
│ │ ├── ruleSerialize.ts # 规则的存储序列化/反序列化 ~700
│ │ ├── tableRuleBuild.ts # tableRuleList 构建 + 字段 meta ~500
│ │ ├── headerRow.ts # 表头行解析与应用 ~230
│ │ └── domScroll.ts # 滚动定位(rAF 重试逻辑) ~90
│ │
│ ├── composables/ # 领域状态 + 逻辑层
│ │ ├── usePanelResize.ts # 三栏拖拽调宽 ~80
│ │ ├── useFileTree.ts # 树数据 + 选中 + 节点切换 ~250
│ │ ├── useNodeStateCache.ts # 节点状态快照缓存 + 分析中态 ~400
│ │ ├── usePreviewData.ts # 预览数据集 + 列快照 + 网格宽度 ~400
│ │ ├── useTemplateLibrary.ts # 模板库(列表/分类/详情/选中) ~900
│ │ ├── useFieldMapping.ts # 字段映射 + 自动匹配 + 浮层交互 ~520
│ │ ├── useRecognitionRules.ts # 识别规则(方向/基本信息/正负) ~580
│ │ ├── useCleaningRules.ts # 规则库 + 已选规则 + 参数配置 ~750
│ │ ├── useColumnOps.ts # 新增/拆分/删除列 + 生成列重建 ~800
│ │ └── useImportFlow.ts # 导入数据流程 ~130
│ │
│ ├── components/
│ │ ├── FileTreePanel.vue # 左栏文件树 ~400
│ │ ├── MiddlePanel.vue # 中栏容器(匹配条+子块调度) ~150
│ │ ├── FieldMappingCard.vue # 字段映射卡 ~600
│ │ ├── OriginFieldPopover.vue # 原文列选择浮层(Popover 内容) ~250
│ │ ├── RecognitionRulePanel.vue # 识别规则设置 ~750
│ │ ├── ChildTemplatePanel.vue # 创建子模板 ~150
│ │ ├── MiddleEmptyState.vue # 中栏空态引导 ~240
│ │ ├── RightPanel.vue # 右栏容器(工具条+预览+规则) ~120
│ │ ├── DataPreviewTable.vue # 数据预览表(表头行/目标字段/数据行) ~400
│ │ ├── CleaningRulePanel.vue # 规则面板容器(tabs+三栏) ~700
│ │ ├── CleaningRuleLibrary.vue # 规则库列表 ~40
│ │ ├── SelectedRuleList.vue # 已选规则列表 ~150
│ │ ├── RuleConfigForm.vue # 规则参数配置(含示例区) ~300
│ │ ├── configs/ # 13 类规则的参数表单(按需拆分) 各 30–90
│ │ │ ├── IgnoreLineConfig.vue
│ │ │ ├── DateTimeConfig.vue
│ │ │ ├── ExtractDataConfig.vue
│ │ │ ├── ExtractSegmentConfig.vue
│ │ │ ├── TimestampConfig.vue
│ │ │ ├── RemoveSpecialConfig.vue
│ │ │ ├── AbsoluteValueConfig.vue
│ │ │ ├── DecimalConvertConfig.vue
│ │ │ ├── ReplaceConfig.vue
│ │ │ ├── GroupingColumnsConfig.vue
│ │ │ ├── MagnificationConfig.vue
│ │ │ ├── YearCompleteConfig.vue
│ │ │ └── UsePrevRowConfig.vue
│ │ ├── AddColumnModal.vue # 新增/编辑列 ~500
│ │ ├── SplitColumnModal.vue # 拆分列 ~300
│ │ └── TemplateSelectModal.vue # 模板库选择 ~800
│ │
│ └── styles/ # 全局(非 scoped)样式
│ ├── variables.less # prefix-cls / 断点 / 公共 mixin
│ ├── page.less # 三栏骨架 + 页头 + 空态
│ ├── panels.less # 各面板细粒度样式
│ ├── modals.less # 挂 body 的 Modal / Popover 样式
│ └── index.less # 汇总入口
│
└── cleaning.vue # ← 删除(路由改为指向 cleaning/index.vue)
行数核算:index.vue 350 + context 80,其余按域分摊,最大单文件 900(useTemplateLibrary.ts / TemplateSelectModal.vue),不再出现 4 位数文件。
props + emits(教科书做法)页面持有全部状态,逐层下发。
currentColumn、selectedRowIndex、headerRowIndex、rightTableHeaders、tablePreviewRows、fieldMapping、selectedRules …),全改 props 会产出 60+ 个 props / 40+ 个 emit,接口面比原文件还难维护,且改造过程中极易漏传导致行为漂移provide/inject + 领域 composable(推荐)// context.ts
export const CleaningContextKey: InjectionKey<CleaningContext> = Symbol('CleaningContext');
// index.vue
const ctx = {
tree: useFileTree(),
preview: usePreviewData(),
template: useTemplateLibrary(),
mapping: useFieldMapping(),
recognize: useRecognitionRules(),
rules: useCleaningRules(),
columns: useColumnOps(),
import: useImportFlow(),
};
provide(CleaningContextKey, ctx);
// FieldMappingCard.vue
const { mapping, preview, template } = useCleaningContext();
const { a, b } = useXxx() 解构出来,模板几乎可以整块剪切粘贴,改名风险最小建议采用方案 B,并在 context.ts 里用 TypeScript 约束每个域暴露哪些字段,避免"隐式却无类型"。
补充:
usePanelResize/useNodeStateCache/useImportFlow属于页面自身职责,不进 context,直接在index.vue使用。
<style scoped>。原因:
.jeesite-case-data-cleaning 前缀下,改成 scoped 需要重写全部选择器,风险大、收益小Modal / Popover / Select 默认 teleport 到 body,scoped 样式根本打不进去。wrapClassName="cleaning-config-modal add-column-modal"、.origin-field-popover、.select-template-modal 这三块必须是全局样式styles/ 下 4 个 less 文件按域组织,各组件不写 <style>,统一由 styles/index.less 引入| # | 风险 | 说明与对策 |
|---|---|---|
| 1 | keep-alive 依赖组件名 |
路由用 name: 'CaseCleaning',SFC 用 name="ViewsDataCleaning"(vite-plugin-vue-setup-extend)。迁移后 index.vue 必须保留 name="ViewsDataCleaning",否则缓存失效 |
| 2 | Modal/Popover 样式失效 | 见「四」样式方案,相关样式一律留全局 less |
| 3 | document 级 DOM 查询 |
8158: document.querySelector('[data-...]')、5239/5258: querySelectorAll('[data-origin-target-key]')。拆组件后只要 data-* 属性还在即可工作;需逐条核对,不能丢属性 |
| 4 | document.body 副作用 |
7062/7100/8206 拖拽时加 is-resizing-layout class 与 cursor。usePanelResize 拆出后必须保证 onBeforeUnmount 也清理 |
| 5 | tableMockRef 跨组件 |
table-mock 拆为 DataPreviewTable 后,ref 需通过 defineExpose 暴露或用 data-* 选择器;mockGridTemplate 由 composable 提供 |
| 6 | sessionStorage key 不能变 | case_clean_file_infos / case_clean_import_file_infos(2008–2009)。与 importProgressSession.ts 有联动,key 放 constants.ts 并保持字面值不变 |
| 7 | prefixCls 获取 |
只 useDesign('case-data-cleaning') 一次并把 prefixCls 放进 context;子组件不要各自 useDesign 出不同 scope,导致 class 前缀不一致 |
| 8 | 规则 key 白名单 | disabledCleaningRuleKeySet(2532)含 groupingColumns/yearComplete/removeSpecialCharacters/usePreviousRowData 4 个被禁用的规则,但规则表单和示例仍在。禁止"顺手清理" |
| 9 | 循环依赖 | utils/* 之间禁止互相 import;composables/* 之间允许单向依赖(如 useFieldMapping → usePreviewData),但不允许回环。拆分时按依赖方向排 |
| 10 | @ 别名与相对路径 |
现有文件用 ./importProgressSession、../../types/enum 等相对路径。迁到 cleaning/ 子目录后层级 +1,所有相对路径必须同步改为 ../types/enum 等,这是最容易漏的地方 → 用 @/ 绝对路径统一 |
| 11 | 13 条规则的 apply 逻辑分散 | applyIgnoreLineRule → applyYearCompleteRule(7893–7996)与模板里的 config 段一一对应。拆 configs/*.vue 时按规则 key 一一映射,不合并 |
原则:每一阶段结束都能
pnpm build通过、页面功能完整、可独立回滚。不做"一次性大重构"。
cleaning.vue → cleaning/index.vue(内容原样搬运,仅改相对路径为 @/)src/core/router/routes/index.ts:108 的 import 路径styles/ 骨架,把 8211–12029 整体搬入 styles/index.less,index.vue 里 @import './styles/index.less'types.ts(1543–1675 + 1710–1717 + 2376–2415 + 2644–2694 等)constants.ts(1720–1727 / 2320–2327 / 2374 / 2416–2537 / 2605–2618 / 3991–4002 / 4244–4285 / 5044–5054 / 6304–6309 / 2008–2010)utils/treeUtils.ts、utils/formatValidate.ts、utils/ruleSerialize.ts、utils/tableRuleBuild.ts、utils/headerRow.tsTemplateSelectModal.vue(template 1329–1502 / script 6285–6890 / style 11469–12018)AddColumnModal.vue(template 1165–1268 / script 7104–7472 / style 10925–11366)SplitColumnModal.vue(template 1269–1324 / script 4983–5068 + 7124–7148 + 7473–7556 / style 10998–11073)CleaningRulePanel.vue + CleaningRuleLibrary.vue + SelectedRuleList.vue + RuleConfigForm.vueconfigs/*.vue 13 个规则表单(可先只做外壳、规则表单逐步迁,保证每步可编译)composables/useCleaningRules.tsFieldMappingCard.vue + OriginFieldPopover.vue + composables/useFieldMapping.tsRecognitionRulePanel.vue + composables/useRecognitionRules.tsChildTemplatePanel.vue、MiddleEmptyState.vue、composables/useTemplateLibrary.tsFileTreePanel.vue + composables/useFileTree.tsDataPreviewTable.vue + composables/usePreviewData.tscomposables/useNodeStateCache.ts、composables/useColumnOps.ts、composables/useImportFlow.tsindex.vue 清理为骨架 + provide(context)lint:eslint 扫)styles/ 4 个 less 文件按域重新分段注释pnpm lint:allindex.vue ≤ 400 行| 阶段 | index.vue | 目标文件最大 |
|---|---|---|
| 现在 | 12029 | 12029 |
| Phase 0 后 | ~12029(仅搬家) | 12029 |
| Phase 1 后 | ~9500 | 9500 |
| Phase 2 后 | ~7200 | 7200 |
| Phase 3 后 | ~5600 | 5600 |
| Phase 4 后 | ~2400 | 1500 |
| Phase 5 后 | ~700 | 900 |
| Phase 6 后 | ≤400 | ≤900 |
每阶段结束都要走的冒烟测试(建议 Phase 2 起每步都跑):
左栏
focusKey 进入时正确选中中栏
右栏
> / < / 双击)、全部删除、启用/禁用切换groupingColumns/yearComplete/removeSpecialCharacters/usePreviousRowData 在 UI 中被禁用,需确认禁用状态保持一致)布局与流程
is-resizing-layout class / cursorcleanProgresskeep-alive 缓存生效(切走再切回状态保持)cleaning.vue → cleaning/index.vue 需要改一行路由 import,是否接受?(若不想动路由,可保留 cleaning.vue 作为壳,子件放 cleaning/ 目录)<style scoped>(工作量翻倍,且 Modal/Popover 必须留全局)?configs/*.vue:13 个规则表单拆成独立文件(推荐),还是先在 RuleConfigForm.vue 里用 v-if 分支保留(改动更小、单文件约 800 行)?