9.0 KiB
9.0 KiB
name, overview, todos
| name | overview | todos | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| FmsTable 内置单元格编辑 | 将当前散落在 ModulePowersPanel.vue 的“单行/单元格编辑”逻辑(row._editing 协议 + handleAreaSelectionChange + col._onEditEnd)下沉到 FmsTable 内,通过 editable: boolean prop 控制启用,组件自闭环;现有唯一调用方 ModulePowersPanel 同步改写为新 API。 |
|
Product Overview
将当前散落在 ModulePowersPanel.vue 的"单元格编辑"逻辑(row._editing 协议 + handleAreaSelectionChange + col._onEditEnd)下沉到 FmsTable 内部。组件自闭环,通过 editable: boolean prop 控制是否启用单元格编辑。
Core Features
- 新增
FmsTable.editable: booleanprop(默认false),type:"input"列在该 prop 为true时才进入可编辑态 FmsTable内部维护editingState: { rowKey, col } | null(shallowRef),不挂在 row 上,零数据污染,父组件保存前无需清理FmsTable内部监听@area-selection-change(鼠标点击 / 键盘方向键统一入口),自动把命中单元格设为编辑格- 内部 watch
editingState→ 浅拷贝内部dataSource→ 触发stk-table重渲染让customCell重新求值 - 退出编辑(Enter / Esc / Blur)时
emit("change", row, col, newValue)给父组件 - 沿用现有
createInputCell的"非编辑零 VNode / 原生 input / 自动 focus.select()" 行为 ModulePowersPanel.vue同步改写为新 API:删除handleEditEnd/handleAreaSelectionChange/ 列定义中的_onEditEnd/onChange,模板改为:editable="true"+@change="onCellChange"- 其他 7 个
FmsTable调用方(dashboard / TableDemo / i18n-type / I18nPanel / Fields / ListConfig / EditConfig / QueryConfig)完全不受影响——editable默认false,与现状一致
Tech Stack
- Vue 3
<script setup>+shallowRef+watch - 复用
stk-table-vue已有的area-selection与customCell机制 - 无新增依赖、无新增组件库、无 composable 文件
Implementation Approach
核心策略:把"编辑态"从 row 对象上的 _editing 字段迁移到 FmsTable 内部的 shallowRef;customCell 通过闭包读取 editingState;通过内部 dataSource 浅拷贝触发 stk-table 重渲染。
数据流(一次完整编辑)
stk-table area-selection-change(ranges)
↓
FmsTable.handleAreaSelectionChange(ranges)
├─ !props.editable → return
├─ ranges 为空 / 列非 input / 行越界 → editingState = null
├─ editingState 已指向同一 row+col → return(避免方向键连按重渲染)
└─ editingState = { rowKey, col } → watch → internalData = [...internalData] → 重渲染
↓
customCell({ row, cellValue }) 重跑
├─ editing 不匹配 → 返回纯文本字符串(零 VNode 开销)
└─ 匹配 → 返回原生 <input ref=focus.select()>
├─ onInput: row[col.dataIndex] = e.target.value
└─ onBlur / Enter / Esc → notifyChange(row, col, newValue)
├─ emit("change", row, col, newValue) → 父 onCellChange → emitUpdate
└─ editingState = null → watch → 重渲染 → 文本回退
关键设计决策
editable: boolean(表格级):type:"input"列在editable=true时由resolveCustomCell注入自定义customCell;false时回退到stk-table默认文本渲染,零运行时开销editingState用shallowRef,不挂 row:避免"保存前清理_editing字段"的脏数据风险;customCell 通过闭包读ctx.getEditing()- 只 emit
change(row, col, newValue):进入编辑不发事件(语义冗余);退出编辑统一把"新值"冒泡给父组件用于脏数据判定 - 内部
internalData浅拷贝触发重渲染:复用stk-table的dataSource响应式机制;editingState变更 → watch → 引用变更 → 重渲染 - 复用现有
createInputCell行为:Enter=不退出(允许多行编辑后续扩展)/ Esc=退出 / Blur=退出 / 自动focus().select() - 沿用硬编码
:area-selection(keyboard:true / ctrl:false / shift:false),不动——本次删除ModulePowersPanel上无效的覆盖赋值属清理
性能保证
- non-editing 单元格仍然"返回字符串,零 VNode"(
createInputCell早返回) editingState用shallowRef,watch 仅在状态变更时触发- 方向键连按同一目标时早 return,避免每按一次重渲染整表
checkbox/actions渲染器继续用WeakMap缓存,不受影响
Implementation Notes
props.dataSource外部引用变更(含父组件localPowers = [...localPowers])会同步触发 watch;editingState内的rowKey是字符串,与引用解耦,引用变更不影响编辑态判定props.editable由true→false动态切换时,displayColumns重算 →resolveCustomCell返回无customCell的 col →stk-table回退到默认文本渲染ModulePowersPanel的localPowersref /addRow/deleteRow/batchDelete行为不变(仍是父组件编辑副本),仅事件链路从"列onChange"改为"@change"- 删除
:area-selection="true"(被硬编码覆盖,无效)和@area-selection-change(下沉的入口)属"现状无效 prop 清理",不改变运行时行为
Directory Structure
fms-vue/src/
├── components/fms-table/
│ ├── FmsTable.vue [MODIFY] 新增 editable prop + editingState 内部状态 +
│ │ handleAreaSelectionChange + change emit,
│ │ dataSource 改为内部 shallowRef 触发重渲染
│ └── utils/
│ ├── createCellRenderer.js [MODIFY] resolveCustomCell(col, ctx) 接 ctx,
│ │ editable=false 时不注入 input customCell
│ └── createEditableCellRenderer.js [MODIFY] createInputCell(col, ctx) 通过闭包读
│ editingState;onExit→ctx.onExit(row, val)
└── views/module-management/components/
└── ModulePowersPanel.vue [MODIFY] 删 handleEditEnd/handleAreaSelectionChange;
列定义移除 _onEditEnd / onChange;
模板加 editable + @change="onCellChange",
删 :area-selection / @area-selection-change
关键代码契约
FmsTable 关键改动点
| 位置 | 改动 |
|---|---|
props (L9-22) |
新增 editable: { type: Boolean, default: false } |
defineEmits (L24) |
新增 "change" |
新增 editingState = shallowRef(null) |
编辑态内部 ref |
新增 internalData = shallowRef([]) + 两个 watch |
dataSource 同步 + 编辑态触发浅拷贝重渲染 |
displayColumns (L41-55) |
改为 props.columns.map(col => resolveCustomCell(col, ctx)),ctx 每次重算以追踪 props.editable |
新增 handleAreaSelectionChange(ranges) |
校验 editable / ranges / row / col,写入 editingState |
新增 notifyChange(row, col, val) |
emit change + 清空 editingState |
<StkTable> :data-source (L120) |
由 dataSource 改为 internalData |
utils 新签名
// createEditableCellRenderer.js
createInputCell(col, ctx)
// ctx = { getEditing, onExit(row, col, val), getRowKey }
// createCellRenderer.js
resolveCustomCell(col, ctx)
// editable=false → type:"input" 列不注入 customCell(stk-table 默认文本)
// editable=true → 注入 createInputCell(col, ctx)
ModulePowersPanel 改动要点
- 删除
handleEditEnd、handleAreaSelectionChange两个函数(约 L100-141) - 列定义移除
_onEditEnd: handleEditEnd与onChange: () => emitUpdate()(L200-203 共 4 处) - 模板
<FmsTable>加editable与@change="onCellChange",移除无效的:area-selection与@area-selection-change - 新增
function onCellChange(row, col, val) { emitUpdate() } localPowersref /addRow/deleteRow/batchDelete逻辑不变