MEMORY.md 7.9 KB

zsjz-ai 项目长期记忆

本机环境怪癖(每次会话都会遇到)

  • bash 工具基本不可用:shim 报 dirname: command not found,ls/grep/wc/cd 全废。不要用 Bash 工具。
  • PowerShell 工具的 stdout 不回显:需要看输出时一律 > 临时文件,再用 Read 读,读完删掉。
  • Remove-Item 被 safe-delete 钩子拦截 → 删文件用 [System.IO.File]::Delete('绝对路径')。
  • 文件搜索/内容搜索用 Glob / Grep 工具,别用 shell。

前端(ai-frontend/)

  • 技术栈:Vue 3 + <script setup lang="ts"> + ant-design-vue + vite + less + pnpm。
  • 常用命令(cwd 一律在 ai-frontend/):
    • 构建:pnpm build(= vite build --mode production,不做类型检查,约 2 分钟)
    • 类型:pnpm type:check(= vue-tsc --noEmit --skipLibCheck,约 25 秒)
  • pnpm type:check 本来就过不了:仓库有约 89 条既有报错,集中在 src/call/、src/trans/、src/graph/、src/otg/、src/person/ 等无关文件。 判断自己有没有写坏,看的是「报错文件里有没有自己动的文件」,不是退出码。
  • tsconfig.json:isolatedModules: true,无 verbatimModuleSyntax;noUnusedLocals: false(所以多余的 import 不报错)。
  • 路由 / 别名:@/* → src/*;路由表在 src/core/router/routes/index.ts。
  • less 全局变量靠 build/theme/modifyVars.ts 里的 hack: 'true; @import (reference) "src/core/design/var/index.less";' 注入, 所以任意 .vue style 块和任意 .less 文件都能直接用 @modal-header-bg-color 这类变量, SFC 的 style 抽成独立 .less 再 @import 回来是安全的。
  • ant-design-vue 的 Modal / Popover / Select 会 teleport 到 body,样式必须全局(非 scoped), 靠 wrapClassName + 全局类名命中。

cleaning 页拆分工程(src/case/views/data/cleaning.vue)

当前进度(2026-09-18):12029 → 944 行(−92%),30+ 新文件,type:check 0 报错 + build 通过。 依赖顺序(单向、无环):useCaseDataCleaningShared(底层状态)→ useCleaningRules / useFieldMapping / useRecognitionRules / useTemplateLibrary → useCaseDataCleaningActions(叶子动作域,放在最后所以能直接用上游产物)。 剩余(压到 ≤400):① 把顶层 watch([...]) 移进 composable(watcher 在 composable 内注册仍绑定页面实例)→ 21 个退回声明可搬走; ② 清未使用 import(匹配标识符要允许 ... 展开);③ useCaseDataCleaningActions(1310) 可按域再拆成 useFileTree / usePreviewData / useColumnOps。

  • 消环的唯一正确姿势:底层共享状态收进唯一一个 shared composable,其余域全部单向依赖它(+ 已有 handle); 叶子域一定要放在所有上游之后。不要用「多个平级 composable 互相注入」——底层状态同时被多方需要,会形成真实的环, 拓扑排序无解;破环退化成空壳 composable 就是垃圾。
  • 生成器可复用件(写这类脚本直接照搬):

    • 声明解析用「找下一个顶层起点」,顶层起点要包含 watch( / provide( / onMounted( 等非声明的顶层语句,否则上一个声明的范围会吞掉它们
    • 模板取词:{{ }} + : / v- / @ / ref 绑定值 + 标签名;表达式剥掉 '…'/"…" 但保留 ${...} 插值
    • 抽出的 .ts 补 export;跨文件相对路径按新层级重算;deps 类型显式写 interface(否则类型循环 → Object.entries(any) → [string, unknown][])
    • 接线模式:页面在 setup 中实例化 composable + 同名解构(页面代码/模板零改动),provide 放在 setup 末尾; wiring 插在「最后一个依赖声明」之后(锚点是依赖声明的最后一行)
  • 方案文档:仓库根 cleaning-refactor-plan.md。已拍板:路由不动(cleaning.vue 留作入口)、 通信走 provide/inject + 领域 composable、样式保持全局 less 非 scoped、业务逻辑一律不许动、 每阶段停下来 review。

  • 目录:cleaning/{types,constants,context}.ts + cleaning/{utils,composables,components,styles}/

  • 沟通模式(Phase 3 起确定的主力模式,后续阶段照这个来):

    • 领域逻辑放 composables/useXxx.ts,由页面在 setup 里实例化
    • 页面用同名解构接住返回值(const { a, b } = useXxx(...))→ 页面原有代码与模板零改动
    • 页面把 useXxx 实例放进 provide(CleaningContextKey, { ..., xxx }),子组件从 context 取
    • provide() 放在 setup 末尾(避开 const 声明顺序的 TDZ);composable 的调用位置 = 「最后一个依赖声明之后」
    • composable 的 deps 必须显式声明类型(写 interface),不要让 TS 从实参反推 —— 页面里有 decl 的签名依赖返回值时 会形成类型循环,导致 deps 退化成 Record<string, any>,而 Object.entries(<any>) 会产出 [string, unknown][] 引发一片 unknown 报错
  • 验证必须 type:check + build 双跑:vue-tsc 抓不到 Vue 模板标签不平衡, 抽块边界写错(多删一个 </div>)时 type:check 是 0 报错、pnpm build 才报 Element is missing end tag。 抽模板块时边界要取「配对的 </div> 行」,不要凭缩进猜(这个文件缩进本身不严格)。

  • 已踩实的坑:

    • 抽出的 .ts 必须补 export(无 import/export 的 .ts 会被 TS 当全局脚本 → TS2306)
    • 抽出文件引用原页面的相对路径 import 时,要按新层级重算(@/ 别名例外)
    • 页面级 let/var 标量不能注入:跨文件是按值快照,两边各改一份(请求序号守卫之类会失效)
    • 声明解析要用「找下一个顶层起点」,不要用括号深度(多行泛型 ref<\n Record<...> 会让深度中途归零、把声明切一半)
    • 模板取词要保留 ${...} 插值,只剥 '…' / "…"(整段 strip 会把 `is-${x}` 里的 x 抹掉)
    • 判定「setup 顶层立即求值」时只扫 wiring 位置之前的行(末尾的 provide(...) 也是顶层语句,会假阳性); watch([...refs]) 整条都算立即求值;「可搬集合 ↔ wiring 位置」要迭代到收敛
    • 算「页面还要解构哪些名字」时,扫描范围 = 整份模板 minus 被抽走的块(别漏掉保留在页面里的部分,比如面板里的匹配条/外层 wrapper)
    • 依赖若是页面里 const { x } = someComposable; 解构出来的,类型要写成 ReturnType<typeof someComposable>['x'](否则退化成 any → Object.entries(any) → unknown); 依赖类型文本里出现的类型名(Ref<TemplateTab> 里的 TemplateTab)也要补 import
  • 验证「重构没改业务逻辑」的可靠做法:拿 git show HEAD:<原文件> 当基准,和「当前入口 + 抽出目录整棵树」做 逐行多集合比对(归一化:去 export 前缀 / 折叠空白 / 去行尾 ,;,跳过注释与 import), 判定条件 = 原始每行在当前树里的出现次数 ≥ 原始次数。本项目已验证 script 丢失/被改动 = 0。 注意:扫描范围只圈自己那一棵(别把同目录其它页面算进来)、SFC 的 template 结束点是 <script 不是 <style、 取原始文件用 node execSync(...,{encoding:'buffer'})(PowerShell > 会写成 UTF-16,且 cmd /c 被拦)。

用户偏好

  • 直接干活,不要反复审计。
  • 业务逻辑不许动:重构只做搬家/抽取,逐行原样搬运。
  • 每个阶段停下来给 review,确认后才进下一阶段。
  • 大文件重构:先给方案文档(会被认真审),方案里要列清决策点。
  • 别生成一堆垃圾文档,编译/构建通过就是完成标准。
  • 不要留临时脚本、临时日志在仓库里,做完自己清理。