Files
workspace/code/fms/.codebuddy/plans/fix-tree-component_1ea98de2.md
2026-08-16 22:01:32 +08:00

114 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: fix-tree-component
overview: 修复 fms-vue 中 Tree 组件的多个缺陷:致命运行时错误(filterExpand 未定义)、受控键模型遗漏、过滤高亮缺失、键盘焦点不同步、展开交互混乱与拖拽视觉残留,并同步完善 demo。
todos:
- id: fix-critical-errors
content: 修复 tree.vue 中 filterExpand 未定义导致的渲染崩溃,并补充缺失的 defaultLoadedKeys prop
status: completed
- id: refactor-expand-select
content: 重构展开/选中交互:加回 switcher 箭头、toggleExpand 改用用户展开集合、行点击只选中并同步键盘焦点,参考 [skill:antdv-next] 交互规范
status: completed
dependencies:
- fix-critical-errors
- id: filter-highlight
content: 在 tree-node.vue 实现过滤关键词高亮渲染,命中片段包裹 tree-highlight
status: completed
dependencies:
- fix-critical-errors
- id: fix-drag-cleanup
content: 修复 onDragLeave 未清理 drop 状态导致的指示线残留,并清理 index.scss 无用样式
status: completed
dependencies:
- fix-critical-errors
- id: fix-demo
content: 修正 demo/tree.vue 中 check-strictly 示例绑定与联动文案错乱问题
status: completed
- id: verify-build
content: 运行 pnpm lint、fmt:check 与 build 验证修复无回归
status: completed
dependencies:
- refactor-expand-select
- filter-highlight
- fix-drag-cleanup
- fix-demo
---
## 用户需求
用户反馈 `fms-vue/src/components/ui/tree` 组件由上一个 AI 编写,存在较多问题,要求自检问题并修复。经过代码自检,已确认以下问题:
## 核心问题
- **致命错误**:`tree.vue` 的 `effectiveExpanded` computed 引用了未定义的 `filterExpand` 变量,组件渲染即抛 `ReferenceError`,Tree 完全不可用
- **Props 缺失**:`useKeyModel('loadedKeys', 'defaultLoadedKeys', ...)` 引用了不存在的 `defaultLoadedKeys` prop
- **过滤高亮缺失**:`filterTree` 返回的命中集合与 `.tree-highlight` 样式均未用于渲染关键词高亮
- **展开交互缺陷**:展开/收起箭头被移除,无可展开视觉提示;行点击同时触发展开与选中造成误触;`toggleExpand` 基于 `effectiveExpanded` 会把过滤临时展开的祖先固化进用户展开集合
- **键盘导航缺陷**:点击节点后未同步 `activeKey` 与焦点,键盘导航起点错乱
- **拖拽视觉残留**:`onDragLeave` 未清理 `dropKey`/`dropPosition`,指示线残留
- **样式残留**:`.tree-node.is-danger`、`.tree-leaf-icon` 等无用样式
- **Demo 错乱**:check-strictly 示例未绑定 checkedKeys,联动文案错标在严格模式示例下
## 修复范围
仅修复内部实现与 demo 演示,保持组件既有 API(props/emits/slot 名称)不变,不引入新依赖,样式沿用现有 `--fms-*` token。
## 技术栈
- Vue 3.5(`<script setup>`)+ SCSS,项目现有技术栈,无需变更
- 图标使用项目已依赖的 `@lucide/vue`(现有导出 `ChevronRight`、`Loader`、`LoaderCircle`)
- 验证命令:`pnpm lint`(oxlint)、`pnpm fmt:check`(oxfmt)、`pnpm build`(vite build)
## 实现方案
### 1. 修复致命错误与模型缺失(tree.vue)
- 删除 `effectiveExpanded` 中对未定义 `filterExpand` 的引用,改为从 `filterResult.value.expand` 派生过滤需展开的祖先集合(computed 缓存,判空处理)
- props 补充声明 `defaultLoadedKeys: { type: Array, default: () => [] }`,与其余受控键的 default* 声明保持一致
### 2. 重构展开/选中交互(tree.vue + tree-node.vue + index.scss)
- **加回 switcher 箭头**:`tree-node.vue` 行首渲染 `ChevronRight`(展开时旋转 90°),懒加载中渲染 `Loader`/`LoaderCircle` 转圈;箭头 `@click.stop` 只负责 `toggleExpand`,避免与行选中互相干扰;利用现有 `.tree-node-switcher`/`.tree-switcher-icon` 样式(已存在于 index.scss,仅模板缺失)
- **`toggleExpand` 基准改为 `expandedSet`**:基于用户展开集合增删,不再把过滤自动展开的祖先路径写回 `expandedKeys`,清除过滤后展开状态可正确还原
- **行点击只选中**:`onContentClick` 移除隐式 `toggleExpand` 调用(展开交给箭头),消除误触
- **点击同步键盘焦点**:`onContentClick` 中更新 `activeKey.value = key.value` 并 `rowRef.value.focus()`,保证 roving tabindex 与键盘导航起点正确
- 键盘导航 `ArrowRight/ArrowLeft` 保持调用 `toggleExpand` 不变
### 3. 实现过滤关键词高亮(tree-node.vue)
- 默认文本标题渲染时,依据 `ctx.filter` 与 `filterVisible` 中 matched 集合,用 `split` 匹配关键词片段,命中部分用 `<span class="tree-highlight">` 包裹(大小写不敏感,与 `filterTree` 一致);自定义 `#title` 插槽场景不干预
- 高亮逻辑抽为组件内方法,避免重复计算;每次渲染仅处理当前节点标题,开销 O(title 长度),无性能问题
### 4. 修复拖拽残留与样式清理(tree.vue + index.scss)
- `onDragLeave` 中清空 `dropKey`/`dropPosition`(仍 emit `dragleave`);`onDragEnd` 已有兜底清理保留
- 清理 index.scss 中未使用的 `.tree-node.is-danger`、`.tree-leaf-icon` 规则(switcher 相关样式因加回箭头而保留复用)
### 5. 修复 Demo(demo/tree.vue)
- check-strictly 示例补充 `v-model:checked-keys` 绑定与输出展示;"checkedKeys(联动)"文案移回联动示例下方,修正演示语义
### 性能与可靠性
- 全部派生状态为 computed 缓存,`effectiveExpanded` 修复后只依赖 `expandedSet` + `filterResult`,无重复遍历
- 修改仅限 tree 组件目录与 demo 文件,不影响其他组件;无新依赖,无构建体积变化
## 目录结构
```
fms-vue/src/components/ui/tree/
├── tree.vue # [MODIFY] 修复 filterExpand 未定义;补 defaultLoadedKeys;toggleExpand 基准改为 expandedSet;
│ # onDragLeave 清理 drop 状态;onContentClick 同步 activeKey/focus
├── tree-node.vue # [MODIFY] 加回 switcher 箭头(ChevronRight/Loader,@click.stop);行点击只选中;
│ # 实现过滤关键词高亮渲染
├── index.scss # [MODIFY] 清理 .is-danger/.tree-leaf-icon 无用规则;switcher 样式保留复用
└── utils.js # [不变] 纯函数逻辑正确,无需修改
fms-vue/src/components/ui/demo/tree.vue # [MODIFY] 修正 check-strictly 示例绑定与文案
```
## Agent Extensions
### Skill
- **antdv-next**
- 用途:在重构展开/选中交互时,参考 Ant Design Tree 的 switcher 箭头交互、行点击与箭头职责分离、键盘导航规范,确保修复后的交互符合成熟组件库惯例
- 预期结果:展开箭头点击展开/收起、行点击仅选中、键盘 ArrowRight/ArrowLeft 行为与成熟组件库一致,避免修复后再次出现交互职责混淆