Files
workspace/code/fms/.qoder/repowiki/zh/content/表格系统/行内编辑功能.md
T
2026-08-23 21:02:41 +08:00

17 KiB
Raw Blame History

行内编辑功能

**本文引用的文件** - [FmsTable.vue](file://fms-vue/src/components/fms-table/FmsTable.vue) - [createCellRenderer.js](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js) - [createEditableCellRenderer.js](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论
  10. 附录:编辑器配置与使用模式

简介

本技术文档聚焦 FMS 表格的行内编辑能力,系统性说明编辑状态管理、编辑器激活流程、生命周期清理、值变化检测与通知机制,以及不同编辑器类型(input、select、checkbox)的配置与使用模式。同时给出验证与错误处理的实践建议,帮助开发者在复杂场景下稳定、高效地使用行内编辑。

项目结构

行内编辑由三层协作完成:

  • 表格容器层:FmsTable.vue 负责编辑状态机、键盘导航、焦点管理与渲染开关。
  • 单元格渲染层:createCellRenderer.js 根据是否处于编辑态决定注入可编辑渲染器或走原生 td。
  • 编辑器实现层:createEditableCellRenderer.js 提供 input/select/checkbox 三种编辑器的具体渲染与事件处理。
graph TB
A["FmsTable.vue<br/>编辑状态机/导航/焦点"] --> B["createCellRenderer.js<br/>决定是否注入编辑器"]
B --> C["createEditableCellRenderer.js<br/>input/select/checkbox 渲染"]
A --> D["stk-table-vue<br/>底层表格库"]
C --> E["UI 组件 Input/Select"]

图表来源

章节来源

核心组件

  • 编辑状态存储
    • activeEditingRow:当前活动编辑行引用,用于全局唯一编辑行控制。
    • editingActive:布尔标记,驱动 displayColumns 是否注入编辑器渲染,避免整表列重建。
    • rowEditingStates:WeakMap<row, shallowRef({editing, focusCol, initialValues})>,按行维护编辑态、聚焦列与初始值快照。
  • 编辑器上下文
    • editingCtx:封装 isEditing/shouldFocus/shouldSelectOnFocus/shouldNotify/onChange,供单元格渲染器统一调用。
  • 编辑器渲染
    • createEditableCell:根据 editor.type 渲染 input/select/checkbox;checkbox 始终可交互,其他需行进入编辑态。
  • 单元格解析
    • resolveCustomCell:当存在活动编辑行或特定类型(checkbox/select)时注入 customCell,否则走原生 td 路径以提升滚动性能。

章节来源

架构总览

行内编辑的关键流程如下:

  • 用户操作触发选区变化(area-selection-change),FmsTable 计算目标行与列。
  • 根据键盘导航状态(方向键按住/离散点击)决定是否立即激活或延迟激活编辑器。
  • activateEditing 设置 activeEditingRow、editingActive 与行级 state,并记录 initialValues。
  • 渲染阶段通过 resolveCustomCell 注入 createEditableCell,内部根据 ctx.isEditing(row) 显示编辑器或只读文本。
  • 编辑器 onChange/blur 通过 shouldNotify 比较新旧值,必要时触发父级 change 回调。
  • 离开编辑或切换行时,deactivateRowEditing/clearActiveEditing 清理状态,保证内存与 UI 一致性。
sequenceDiagram
participant U as "用户"
participant T as "FmsTable.vue"
participant R as "createCellRenderer.js"
participant E as "createEditableCellRenderer.js"
U->>T : 选区变化/键盘导航
T->>T : handleAreaSelectionChange()
alt 键盘导航中
T->>T : scheduleEditActivation()/commitPendingEditing()
else 鼠标点击
T->>T : activateEditing(row, focusCol)
end
T-->>R : displayColumns 包含 editingActive
R->>E : createEditableCell(col, ctx)
E->>E : 渲染 input/select/checkbox
E-->>T : notifyChange(ctx, editor, row, col, value)
T-->>U : emit('change', row, col, newValue)

图表来源

详细组件分析

编辑状态管理机制

  • WeakMap 行级状态
    • getRowEditingState:为每行创建或复用 shallowRef 状态对象,包含 editing、focusCol、initialValues。
    • captureEditableValues:收集该行所有可编辑列的初始值,用于后续变更检测。
  • 全局编辑标志
    • activeEditingRow:当前活动编辑行引用,确保同一时间仅一行处于编辑。
    • editingActive:布尔标记,控制 displayColumns 是否注入编辑器渲染,减少非编辑态下的函数式组件数量。
  • 状态迁移
    • rebindActiveEditingRow:父级数据更新后,若新数据对象替换了旧对象,将编辑态迁移到新对象并保持 focusCol 与 initialValues。
flowchart TD
Start(["进入编辑"]) --> GetState["getRowEditingState(row)"]
GetState --> SetState{"activeEditingRow 是否已为该 row?"}
SetState -- 否 --> DeactivatePrev["deactivateRowEditing(activeEditingRow)"]
DeactivatePrev --> UpdateGlobal["activeEditingRow = row<br/>editingActive = true"]
UpdateGlobal --> InitState["state.value = { editing:true, focusCol, initialValues }"]
SetState -- 是 --> UpdateFocus["更新 focusCol 并置 editing=true"]
InitState --> End(["就绪"])
UpdateFocus --> End

图表来源

章节来源

编辑器激活流程与键盘导航

  • 选区变化处理
    • handleAreaSelectionChange:根据 ranges 确定目标行与列;若列无 editor,则不激活编辑器。
    • 键盘导航期间(按下方向键等):
      • 按住方向键重复触发(event.repeat=true):全程保持 0 编辑器,松手后再激活到最终行,避免长按滚动时反复创建组件导致卡顿。
      • 快速离散连按:防抖合并(100ms),避免频繁挂载卸载。
    • 非键盘导航(鼠标点击):立即激活;若已在同一行编辑,仅更新 focusCol,并临时禁用 select 全选以避免误选。
  • 焦点管理
    • createFocusHandler:在 vnode 挂载/更新时聚焦输入控件,支持选择全部文本(shouldSelectOnFocus)。
    • 键盘事件:Enter 选中内容,Escape 失焦提交。
sequenceDiagram
participant K as "键盘/鼠标"
participant T as "FmsTable.vue"
participant S as "状态机"
K->>T : area-selection-change / keydown
T->>T : handleKeydownCapture/handleAreaSelectionChange
alt 键盘导航中
T->>T : scheduleEditActivation()
T->>S : commitPendingEditing()
else 鼠标点击
T->>S : activateEditing(row, focusCol)
end
S-->>T : editingActive 变化
T-->>K : 渲染编辑器并聚焦

图表来源

章节来源

编辑器生命周期管理

  • 退出编辑
    • deactivateRowEditing:仅清除某行的编辑态,不触碰 activeEditingRow/editingActive,用于行间切换编辑时避免整表列重建。
    • clearActiveEditing:清空 activeEditingRow,重置 editingActive,并清理对应行状态。
  • 监听与清理
    • editable 属性变化:关闭编辑、移除导航监听、清空选区。
    • 组件卸载:取消动画帧、断开 ResizeObserver、移除窗口监听、清理定时器与按键集合。
flowchart TD
ExitStart["退出编辑入口"] --> CheckRow{"是否存在 activeEditingRow?"}
CheckRow -- 否 --> End(["结束"])
CheckRow -- 是 --> ClearGlobal["clearActiveEditing()<br/>activeEditingRow=null<br/>editingActive=false"]
ClearGlobal --> ClearState["清理行级 state.editing/focusCol/initialValues"]
ClearState --> End

图表来源

章节来源

编辑值的变化检测与通知

  • 比较策略
    • editorValuesEqual:select/checkbox 使用 Object.is 严格相等;其他类型转为字符串比较,兼容数字与空值。
  • 通知流程
    • shouldNotify:对比 initialValues 与当前值,仅在值发生变化时返回 true,并更新 initialValues。
    • notifyChange:依次调用 editor.onChange(列级)与 ctx.onChange(表格级),最终由 FmsTable 发出 change 事件。
  • 草稿机制
    • input 使用 WeakMap 草稿暂存,blur 时提交;vnode 卸载前兜底写入,防止丢失未提交修改。
flowchart TD
Change["编辑器值变化"] --> Compare["editorValuesEqual(prev, current)"]
Compare --> Equal{"值相等?"}
Equal -- 是 --> Skip["跳过通知"]
Equal -- 否 --> UpdateInit["更新 initialValues"]
UpdateInit --> Notify["notifyChange(ctx, editor, row, col, value)"]
Notify --> Parent["emit('change', row, col, newValue)"]

图表来源

章节来源

渲染路径与性能优化

  • 条件注入 customCell
    • 当 editingActive 为真或列类型为 checkbox/select 时,注入 createEditableCell;否则走原生 td,减少函数式组件数量,提升滚动性能。
  • 虚拟滚动与尺寸刷新
    • 大表格开启虚拟滚动,ResizeObserver 防抖合并尺寸变化,避免动画期间频繁重排。
  • 样式与交互
    • 通过 CSS 变量控制行高与边框;非编辑态允许文本选择;编辑态输入框自适应单元格尺寸。

章节来源

依赖关系分析

  • FmsTable.vue 依赖 stk-table-vue 提供底层表格能力(虚拟滚动、选区、拖拽等)。
  • createCellRenderer.js 依赖 UI 组件(Dropdown、Modal、Button)用于 actions 列与确认弹窗。
  • createEditableCellRenderer.js 依赖 Input、Select 组件实现编辑器渲染。
graph LR
FT["FmsTable.vue"] --> ST["stk-table-vue"]
FT --> CR["createCellRenderer.js"]
CR --> CER["createEditableCellRenderer.js"]
CER --> INP["Input"]
CER --> SEL["Select"]
CR --> MOD["Modal/Dropdown/Button"]

图表来源

章节来源

性能考量

  • 最小化渲染:非编辑态尽量走原生 td,避免每格函数式组件重渲染。
  • 键盘导航优化:长按方向键期间不创建编辑器,松手再激活;快速连按防抖合并。
  • 虚拟滚动:大数据量启用虚拟滚动,ResizeObserver 防抖合并尺寸变化。
  • 草稿机制:input 使用 WeakMap 暂存,减少不必要的响应式更新。

[本节为通用指导,不直接分析具体文件]

故障排查指南

  • 编辑态未生效
    • 检查 editable 是否为 false;editable 为 false 时会移除导航监听并清理编辑态。
    • 确认列是否配置 editor;无 editor 的列不会激活编辑器。
  • 值未提交
    • 检查 blur 是否触发;input 在 blur 时提交草稿。
    • 检查 shouldNotify 逻辑,确保值确实发生变化。
  • 焦点异常
    • 检查 shouldFocus 与 focusCol 是否正确;确认 onVnodeMounted/onVnodeUpdated 是否执行。
  • 内存泄漏
    • 确认组件卸载时清除了 ResizeObserver、窗口监听与定时器。

章节来源

结论

FMS 表格的行内编辑通过清晰的状态机、条件渲染与键盘导航优化,实现了高性能、低侵入的编辑体验。WeakMap 行级状态与 editingActive 标志有效控制了渲染范围与内存占用;editorValuesEqual 与草稿机制保障了值变化的准确性与用户体验。结合 input/select/checkbox 三类编辑器,可满足大多数业务场景。建议在复杂场景中配合自定义验证与错误提示,以获得更健壮的编辑流程。

[本节为总结性内容,不直接分析具体文件]

附录:编辑器配置与使用模式

配置项说明

  • 列级 editor
    • type:'input' | 'select' | 'checkbox'
    • inputType:'text' | 'password' | 'number' | 'textarea'(仅 input)
    • options:数组或函数,返回选项列表(仅 select)
    • allowClear:是否允许清空(仅 select)
    • emptyValue:空值时的默认值(仅 select)
    • disabled:是否禁用(可为函数)
    • onChange:列级变更回调(可选)
  • 表格级 change
    • FmsTable 暴露 change 事件,参数为 (row, col, newValue),用于统一处理数据变更。

使用示例(概念性)

  • 文本输入
    • 配置列 editor.type='input',inputType 根据需要设置为 text/password/number/textarea。
    • 在表格 change 事件中持久化数据或进行校验。
  • 下拉选择
    • 配置列 editor.type='select',options 为 [{label, value}] 或函数返回。
    • 可通过 allowClear 与 emptyValue 控制清空行为与空值语义。
  • 复选框
    • 配置列 editor.type='checkbox',值约定为 1/0;点击即切换,无需先让行进入编辑态。

验证与错误处理建议

  • 在列级 onChange 或表格级 change 中进行校验,失败时通过消息组件提示。
  • 对于必填字段,可在 shouldNotify 之前增加前置校验,阻止无效值提交。
  • 对异步校验,可在 change 中发起请求,并在结果返回后更新行数据或展示错误。

[本节为概念性指导,不直接分析具体文件]