20260823210241
This commit is contained in:
1 parent
5ff4583aeb
commit
f4c7413dc6
2766 files changed
+1836409
-94689
No files matched your search
@@ -0,0 +1,318 @@
|
||||
# 行内编辑功能
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [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)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录:编辑器配置与使用模式](#附录编辑器配置与使用模式)
|
||||
|
||||
## 简介
|
||||
本技术文档聚焦 FMS 表格的行内编辑能力,系统性说明编辑状态管理、编辑器激活流程、生命周期清理、值变化检测与通知机制,以及不同编辑器类型(input、select、checkbox)的配置与使用模式。同时给出验证与错误处理的实践建议,帮助开发者在复杂场景下稳定、高效地使用行内编辑。
|
||||
|
||||
## 项目结构
|
||||
行内编辑由三层协作完成:
|
||||
- 表格容器层:FmsTable.vue 负责编辑状态机、键盘导航、焦点管理与渲染开关。
|
||||
- 单元格渲染层:createCellRenderer.js 根据是否处于编辑态决定注入可编辑渲染器或走原生 td。
|
||||
- 编辑器实现层:createEditableCellRenderer.js 提供 input/select/checkbox 三种编辑器的具体渲染与事件处理。
|
||||
|
||||
```mermaid
|
||||
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"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:98-220](file://fms-vue/src/components/fms-table/FmsTable.vue#L98-L220)
|
||||
- [createCellRenderer.js:208-228](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L208-L228)
|
||||
- [createEditableCellRenderer.js:97-212](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L97-L212)
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:98-220](file://fms-vue/src/components/fms-table/FmsTable.vue#L98-L220)
|
||||
- [createCellRenderer.js:208-228](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L208-L228)
|
||||
- [createEditableCellRenderer.js:97-212](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L97-L212)
|
||||
|
||||
## 核心组件
|
||||
- 编辑状态存储
|
||||
- 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 路径以提升滚动性能。
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:98-220](file://fms-vue/src/components/fms-table/FmsTable.vue#L98-L220)
|
||||
- [createCellRenderer.js:208-228](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L208-L228)
|
||||
- [createEditableCellRenderer.js:195-212](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L195-L212)
|
||||
|
||||
## 架构总览
|
||||
行内编辑的关键流程如下:
|
||||
- 用户操作触发选区变化(area-selection-change),FmsTable 计算目标行与列。
|
||||
- 根据键盘导航状态(方向键按住/离散点击)决定是否立即激活或延迟激活编辑器。
|
||||
- activateEditing 设置 activeEditingRow、editingActive 与行级 state,并记录 initialValues。
|
||||
- 渲染阶段通过 resolveCustomCell 注入 createEditableCell,内部根据 ctx.isEditing(row) 显示编辑器或只读文本。
|
||||
- 编辑器 onChange/blur 通过 shouldNotify 比较新旧值,必要时触发父级 change 回调。
|
||||
- 离开编辑或切换行时,deactivateRowEditing/clearActiveEditing 清理状态,保证内存与 UI 一致性。
|
||||
|
||||
```mermaid
|
||||
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)
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:450-505](file://fms-vue/src/components/fms-table/FmsTable.vue#L450-L505)
|
||||
- [createCellRenderer.js:208-228](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L208-L228)
|
||||
- [createEditableCellRenderer.js:66-70](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L66-L70)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 编辑状态管理机制
|
||||
- WeakMap 行级状态
|
||||
- getRowEditingState:为每行创建或复用 shallowRef 状态对象,包含 editing、focusCol、initialValues。
|
||||
- captureEditableValues:收集该行所有可编辑列的初始值,用于后续变更检测。
|
||||
- 全局编辑标志
|
||||
- activeEditingRow:当前活动编辑行引用,确保同一时间仅一行处于编辑。
|
||||
- editingActive:布尔标记,控制 displayColumns 是否注入编辑器渲染,减少非编辑态下的函数式组件数量。
|
||||
- 状态迁移
|
||||
- rebindActiveEditingRow:父级数据更新后,若新数据对象替换了旧对象,将编辑态迁移到新对象并保持 focusCol 与 initialValues。
|
||||
|
||||
```mermaid
|
||||
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
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:112-196](file://fms-vue/src/components/fms-table/FmsTable.vue#L112-L196)
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:112-196](file://fms-vue/src/components/fms-table/FmsTable.vue#L112-L196)
|
||||
- [FmsTable.vue:149-174](file://fms-vue/src/components/fms-table/FmsTable.vue#L149-L174)
|
||||
|
||||
### 编辑器激活流程与键盘导航
|
||||
- 选区变化处理
|
||||
- handleAreaSelectionChange:根据 ranges 确定目标行与列;若列无 editor,则不激活编辑器。
|
||||
- 键盘导航期间(按下方向键等):
|
||||
- 按住方向键重复触发(event.repeat=true):全程保持 0 编辑器,松手后再激活到最终行,避免长按滚动时反复创建组件导致卡顿。
|
||||
- 快速离散连按:防抖合并(100ms),避免频繁挂载卸载。
|
||||
- 非键盘导航(鼠标点击):立即激活;若已在同一行编辑,仅更新 focusCol,并临时禁用 select 全选以避免误选。
|
||||
- 焦点管理
|
||||
- createFocusHandler:在 vnode 挂载/更新时聚焦输入控件,支持选择全部文本(shouldSelectOnFocus)。
|
||||
- 键盘事件:Enter 选中内容,Escape 失焦提交。
|
||||
|
||||
```mermaid
|
||||
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 : 渲染编辑器并聚焦
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:373-505](file://fms-vue/src/components/fms-table/FmsTable.vue#L373-L505)
|
||||
- [createEditableCellRenderer.js:78-95](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L78-L95)
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:373-505](file://fms-vue/src/components/fms-table/FmsTable.vue#L373-L505)
|
||||
- [createEditableCellRenderer.js:78-127](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L78-L127)
|
||||
|
||||
### 编辑器生命周期管理
|
||||
- 退出编辑
|
||||
- deactivateRowEditing:仅清除某行的编辑态,不触碰 activeEditingRow/editingActive,用于行间切换编辑时避免整表列重建。
|
||||
- clearActiveEditing:清空 activeEditingRow,重置 editingActive,并清理对应行状态。
|
||||
- 监听与清理
|
||||
- editable 属性变化:关闭编辑、移除导航监听、清空选区。
|
||||
- 组件卸载:取消动画帧、断开 ResizeObserver、移除窗口监听、清理定时器与按键集合。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
ExitStart["退出编辑入口"] --> CheckRow{"是否存在 activeEditingRow?"}
|
||||
CheckRow -- 否 --> End(["结束"])
|
||||
CheckRow -- 是 --> ClearGlobal["clearActiveEditing()<br/>activeEditingRow=null<br/>editingActive=false"]
|
||||
ClearGlobal --> ClearState["清理行级 state.editing/focusCol/initialValues"]
|
||||
ClearState --> End
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:128-147](file://fms-vue/src/components/fms-table/FmsTable.vue#L128-L147)
|
||||
- [FmsTable.vue:522-544](file://fms-vue/src/components/fms-table/FmsTable.vue#L522-L544)
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:128-147](file://fms-vue/src/components/fms-table/FmsTable.vue#L128-L147)
|
||||
- [FmsTable.vue:522-544](file://fms-vue/src/components/fms-table/FmsTable.vue#L522-L544)
|
||||
|
||||
### 编辑值的变化检测与通知
|
||||
- 比较策略
|
||||
- editorValuesEqual:select/checkbox 使用 Object.is 严格相等;其他类型转为字符串比较,兼容数字与空值。
|
||||
- 通知流程
|
||||
- shouldNotify:对比 initialValues 与当前值,仅在值发生变化时返回 true,并更新 initialValues。
|
||||
- notifyChange:依次调用 editor.onChange(列级)与 ctx.onChange(表格级),最终由 FmsTable 发出 change 事件。
|
||||
- 草稿机制
|
||||
- input 使用 WeakMap 草稿暂存,blur 时提交;vnode 卸载前兜底写入,防止丢失未提交修改。
|
||||
|
||||
```mermaid
|
||||
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)"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:198-220](file://fms-vue/src/components/fms-table/FmsTable.vue#L198-L220)
|
||||
- [createEditableCellRenderer.js:66-70](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L66-L70)
|
||||
- [createEditableCellRenderer.js:17-49](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L17-L49)
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:198-220](file://fms-vue/src/components/fms-table/FmsTable.vue#L198-L220)
|
||||
- [createEditableCellRenderer.js:66-70](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L66-L70)
|
||||
- [createEditableCellRenderer.js:17-49](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L17-L49)
|
||||
|
||||
### 渲染路径与性能优化
|
||||
- 条件注入 customCell
|
||||
- 当 editingActive 为真或列类型为 checkbox/select 时,注入 createEditableCell;否则走原生 td,减少函数式组件数量,提升滚动性能。
|
||||
- 虚拟滚动与尺寸刷新
|
||||
- 大表格开启虚拟滚动,ResizeObserver 防抖合并尺寸变化,避免动画期间频繁重排。
|
||||
- 样式与交互
|
||||
- 通过 CSS 变量控制行高与边框;非编辑态允许文本选择;编辑态输入框自适应单元格尺寸。
|
||||
|
||||
章节来源
|
||||
- [createCellRenderer.js:208-228](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L208-L228)
|
||||
- [FmsTable.vue:41-96](file://fms-vue/src/components/fms-table/FmsTable.vue#L41-L96)
|
||||
- [FmsTable.vue:600-771](file://fms-vue/src/components/fms-table/FmsTable.vue#L600-L771)
|
||||
|
||||
## 依赖关系分析
|
||||
- FmsTable.vue 依赖 stk-table-vue 提供底层表格能力(虚拟滚动、选区、拖拽等)。
|
||||
- createCellRenderer.js 依赖 UI 组件(Dropdown、Modal、Button)用于 actions 列与确认弹窗。
|
||||
- createEditableCellRenderer.js 依赖 Input、Select 组件实现编辑器渲染。
|
||||
|
||||
```mermaid
|
||||
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"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [FmsTable.vue:1-7](file://fms-vue/src/components/fms-table/FmsTable.vue#L1-L7)
|
||||
- [createCellRenderer.js:1-6](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L1-L6)
|
||||
- [createEditableCellRenderer.js:1-4](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L1-L4)
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:1-7](file://fms-vue/src/components/fms-table/FmsTable.vue#L1-L7)
|
||||
- [createCellRenderer.js:1-6](file://fms-vue/src/components/fms-table/utils/createCellRenderer.js#L1-L6)
|
||||
- [createEditableCellRenderer.js:1-4](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L1-L4)
|
||||
|
||||
## 性能考量
|
||||
- 最小化渲染:非编辑态尽量走原生 td,避免每格函数式组件重渲染。
|
||||
- 键盘导航优化:长按方向键期间不创建编辑器,松手再激活;快速连按防抖合并。
|
||||
- 虚拟滚动:大数据量启用虚拟滚动,ResizeObserver 防抖合并尺寸变化。
|
||||
- 草稿机制:input 使用 WeakMap 暂存,减少不必要的响应式更新。
|
||||
|
||||
[本节为通用指导,不直接分析具体文件]
|
||||
|
||||
## 故障排查指南
|
||||
- 编辑态未生效
|
||||
- 检查 editable 是否为 false;editable 为 false 时会移除导航监听并清理编辑态。
|
||||
- 确认列是否配置 editor;无 editor 的列不会激活编辑器。
|
||||
- 值未提交
|
||||
- 检查 blur 是否触发;input 在 blur 时提交草稿。
|
||||
- 检查 shouldNotify 逻辑,确保值确实发生变化。
|
||||
- 焦点异常
|
||||
- 检查 shouldFocus 与 focusCol 是否正确;确认 onVnodeMounted/onVnodeUpdated 是否执行。
|
||||
- 内存泄漏
|
||||
- 确认组件卸载时清除了 ResizeObserver、窗口监听与定时器。
|
||||
|
||||
章节来源
|
||||
- [FmsTable.vue:522-544](file://fms-vue/src/components/fms-table/FmsTable.vue#L522-L544)
|
||||
- [createEditableCellRenderer.js:107-127](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L107-L127)
|
||||
- [createEditableCellRenderer.js:78-95](file://fms-vue/src/components/fms-table/utils/createEditableCellRenderer.js#L78-L95)
|
||||
|
||||
## 结论
|
||||
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 中发起请求,并在结果返回后更新行数据或展示错误。
|
||||
|
||||
[本节为概念性指导,不直接分析具体文件]
|
||||
Reference in new issue
Block a user