17 KiB
17 KiB
行内编辑功能
**本文引用的文件** - [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)目录
简介
本技术文档聚焦 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 中发起请求,并在结果返回后更新行数据或展示错误。
[本节为概念性指导,不直接分析具体文件]