可编辑表格ReEditTable
组件实现基于
Vue3+Element Plus+Typescript,同时引用vueUse+lodash-es+tailwindCss(不影响功能,可忽略)
基于自封的分页表格组件ReTable进行的功能扩展,在
ReTableColumn配置的基础上,结合ReForm一些相同的表单配置项,一同实现可编辑功能。支持多种编辑表格交互方式,如单个单元格编辑,整行编辑,或者全部行编辑;支持单元格校验,使用ElForm相同校验规则的配置项进行校验;同时,表格也支持滚动、分页功能。
校验方式支持整个表格校验,自动跳转首行校验失败的位置(分页也支持),也支持单行数据校验、单个单元格数据校验这三种校验方式。
由于行渲染表单控件未使用ElForm进行包裹,无法直接通过ElForm自带的校验功能进行数据校验,主要基于自己写的async-validate方法进行数据校验。目前只针对常见的rules配置规则进行遍历校验,如果不满足您的需求,你可以直接自己实现校验方法后替换方法即可。
可编辑表格不仅支持全量数据直接进入编辑,还支持单元格单独编辑或者单行单独编辑。【推荐】单行编辑,加上分页功能,能够解决大部分可编辑表格输入卡顿问题。
思路
基于ReTableColumn的配置项扩展,增加了一些同ReFormItem相似的配置项,利用ElTableColumn的formatter或者renderCell自定义渲染方式,动态渲染表单控件,因此需要基于配置以及编辑状态进行自定义渲染函数的构造。
增加了required、comp、childComp、rules、props等渲染表单控件的字段即可,原先已经支持options了(同ReFormItem用法一致,可以用于渲染一些下拉、单选钮组、多选框组),自带的slot插槽也可用于自定义表单控件渲染,同时还将当前行的编辑状态通过作用域插槽返回,可以用于控制自定义展示内容。
处理完表单控件渲染,需要区分一些交互方式,目前支持整个表格可编辑、单行编辑、单个单元格编辑等控制,同时单行编辑还区分自定义控制还是直接双击控制,单元格编辑只支持双击控制。
目前单行编辑(edit-trigger=row)、单个单元格编辑(edit-trigger=cell)采用鼠标左键双击开启编辑/关闭编辑的交互方式,如果使用自定义单行编辑(edit-trigger=custom),需要使用操作列来控制,可以直接使用自带的操作列也可以自定义,提供的expose方法足够自己控制编辑状态。
需要按列配置收集校验规格,通过数据逐行遍历校验,行数据基于列配置收集的校验规格逐个字段进行校验,因此可以清晰的构造三种校验方式:全量数据、单行、单个单元格。
除了上述最基本的功能处理外,还需要处理各种状态缓存,如编辑状态(区分按行、还是按单元格)、原始数据缓存(用于还原)、校验结果缓存(用于展示校验失败信息),既要支持增加、删除、判断是否存在等功能,还需要增加索引更新操作(如果删除某一行之后,需要更新受影响的缓存数据,否则会行渲染会出错)
其他一些功能点:自定义操作列的定义渲染,内置新增按钮的展示(支持页脚和头部两种方式)
难点
- 索引的理解:如果是滚动表格,ElTable返回的索引是实际的数据索引,但如果是分页表格,ElTable返回的索引实际是当前页的索引,要正确数据响应,需要进行索引的转换,因此源码中存在大量的索引转换处理(normalizeIndex)。
- 各种状态缓存维护
- 单元格的自定义渲染函数构造
- 提供rules规则校验方法
特殊点
- ElTable双击事件,未返回索引信息,因此需要自行获取索引信息,目前采用两种方式:如果配置rowKey,则通过rowKey从数据中获取数据索引,如果是分页情况需要根据页码进行当前页的索引转换;如果没有rowKey,则通过DOM操作获取当前tr所在父节点的索引位置,注意,DOM获得的索引是渲染的索引(当前页的索引),需要转换成数据索引。
基础
基于ReTableColumn扩展了ReEditTableColumn,补充了一些类似ReFormItem的字段,可以方便理解配置,开启 editable 直接按可编辑状态进行行渲染,trigger将会失效,同时内置的操作列只会存在一个删除操作。
查看 /demo/table/edit-table-basic.md
:::warning
少量数据型的可编辑表格可以开启 editable 使得所有行可编辑,否则建议走按钮切换或者双击开启编辑交互。
:::
整行编辑
设置 edit-trigger="row" 开启行编辑交互,需要通过鼠标左键双击行进入编辑,编辑完成后双击行空白地方进行行校验,校验成功自动关闭,否则给出校验提示。默认会渲染操作列,也支持开启行编辑,如果不想要的话可以通过 render-action="false" 属性关闭。
查看 /demo/table/edit-table-row.md
自定义整行编辑
设置 edit-trigger="row" 或 edit-trigger="custom" 开启行编辑交互,可以通过 render-action="false" 属性关闭默认的操作列渲染,通过类配置自己构建操作列进行自定义行编辑。
查看 /demo/table/edit-table-custom-row.md
单元格编辑
设置 edit-trigger="cell" 开启单元格编辑交互,需要通过鼠标左键双击单元格进入编辑,编辑完成后双击单元格空白地方进行单元格校验,校验成功后自动关闭,否则给出校验提示。
查看 /demo/table/edit-table-cell.md
自定义控件
同ReFormItem配置类似,需要指定slot插槽,在通过插槽进行自定义控件渲染。插槽增加 editable 区分单元格是否处于编辑状态,cellValue 表示单元格数据值,handler 表示单元格数据更新事件处理器
查看 /demo/table/edit-table-slot.md
ReEditTable属性
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| size | 可编辑表格尺寸 | "large" | "default" | "small" | "default" |
| stripe | 条纹表格 | boolean | false |
| createNew | 提供返回新的一行创建函数,默认自动收集列配置中的defaultValue | () => ReTableRow | - |
| showAddBtn | 是否展示新增按钮 | boolean | true |
| addBtnPosition | 新增按钮展示位置 | "top" | "bottom" | "bottom" |
| addBtnProps | 新增按钮属性 | Partial<ButtonProps> | - |
| addBtnDisabled | 新增按钮是否禁用 | boolean | false |
| according | 是否限制每次只有一行/或一个单元格可以编辑 | boolean | false |
| pageSize | 页大小 | number | 10 |
| pagination | 是否开启分页 | boolean | false |
| disabled | 可编辑表格是否禁用 | boolean | false |
| editable | 可编辑表格是否所有单元格都直接进入编辑状态 | boolean | false |
| confirmBeforeDelete | 删除行是否进行二次确认 | boolean | true |
| confirmMessage | 删除行进行二次确认提示文本 | string | - |
| editTrigger | 触发编辑的交互方式 | "cell" | "row" | "custom" | "custom" |
| renderAction | 是否采用内置的操作列 | boolean | true |
| columns | 必填,可编辑表格列配置 | ReEditTableColumn[] | - |
| rowKey | 数据主键,如果有建议配置,提供性能 | string | - |
| ignoreCellValid | 是否忽略单元格编辑时触发校验,直接对整个编辑表格进行一次校验 | boolean | false |
| scrollToError | 表格校验失败是否自动滚动到第一个校验错误行 | boolean | true |
| scrollIntoViewOptions | 滚动行为配置 | boolean | false |
| maxHeight | 表格高度 | number | - |
除了上述属性之外,ReTable属性均支持,默认会被ReTable实例继承
:::warning 建议设置rowKey,可以提供组件渲染性能。组件实现过程中需要基于rowKey进行搜索,如果未提供,将会通过DOM操作的方式进行查找,会降低性能。 :::
ReEditTableColumn字段
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| required | 字段是否必填 | boolean | false |
| editable | 字段是否可编辑 | boolean | false |
| defaultValue | 表单字段默认值 | string | - |
| defaultText | 表单字段默认展示占位符 | string | "-" |
| comp | 表单控件 | string | - |
| childComp | 子组件所使用组件 | string | - |
| props | 表单控件属性配置 | Record<string, any> | - |
| events | 表单控件事件监听 | Record<string, Function> | - |
| rules | 表单字段校验规则 | Arrayable<FormItemRule> | - |
| modelProp | 表单字段控件v-model关联属性 | string | "modelValue" |
| modelEvent | 表单字段控件v-model关联事件 | string | "update:modelValue" |
除了上述字段之外,ReTableColumn字段均支持
ReEditTable事件
| 事件名 | 说明 | 格式 |
|---|---|---|
| cell-dblclick | 鼠标左键双击某个单元格时触发 | (row: any, column: any, cell: HTMLTableCellElement, event: Event) => void |
| cell-contextmenu | 鼠标右键点击某个单元格时触发 | (row: any, column: any, cell: HTMLTableCellElement, event: Event) => void |
| row-dblclick | 鼠标左键双击某行时触发 | (row: any, column: any, event: Event) => void |
| row-contextmenu | 鼠标右键点击某行时触发 | (row: any, column: any, event: Event) => void |
| cell-change | 可编辑的单元格数据更新时触发 | (row: any, column: any, value: any, index: number) => void |
| scroll-to | 表格校验失败自动滚动到第一个校验失败的行时触发 | (index: number, callback?: Function) => void |
| update:data | 绑定数据更新时触发 | (data: Record<string, any>[]) => void |
除了上述事件之外,ReTable支持的事件也支持,默认会被ReTable实例继承
ReEditTable插槽
| 插槽名 | 说明 |
|---|---|
| empty | 空状态展示 |
| toolbox-left | 页头工具栏左侧内容插槽,可用于自定义新增按钮 |
| toolbox-right | 页头工具栏右侧内容插槽,可用于自定义附加按钮 |
| add-bottom-button | 页脚新增按钮插槽,用于自定义页脚的新增按钮,只有在 showAddBtn 且 addBtnPosition="bottom" 时有效 |
除了上述插槽之外,列配置项配置的插槽也支持。如果是自定义控件,列配置需要指定插槽
ReEditTable Expose
| 字段 | 说明 | 类型 |
|---|---|---|
| reTableRef | ReTable组件实例 | InstanceType<typeof ReTable> |
| editData | 编辑表格数据 | Record<string, any>[] |
| normalizeIndex | 获取数据集索引,自定义操作列需要 | ($index: number) => number |
| removeEditRows | 移除指定行编辑状态,自定义操作列需要 | ($index: number) => void |
| removeRowCache | 移除指定行编辑缓存,自定义操作列需要 | ($index: number) => void |
| existIndexEditCells | 判断数据行是否处在某个单元格处在编辑状态 | ($index: number) => boolean |
| existIndexEditRows | 判断数据行是否处在编辑状态 | ($index: number) => boolean |
| reset | 重置所有编辑状态 | () => void |
| toAdd | 增加一行 | () => void |
| toDelete | 删除指定行 | ($index: number) => void |
| toEdit | 编辑指定行 | ($index: number) => void |
| toEditCell | 编辑指定单元格 | ($index: number, prop: string) => void |
| cancelEditRow | 取消指定行编辑状态 | ($index: number) => void |
| cancelEditCell | 取消指定单元格编辑状态 | ($index: number, prop: string) => void |
| validate | 整个编辑表格校验 | (callback?: (valid: boolean) => void) => void |
| validateRow | 指定行校验 | (index: number, callback?: (valid: boolean) => void) => void |
| validateCell | 指定单元格校验 | (index: number, prop: string, callback?: (valid: boolean) => void) => void |
:::warning $index:表示当前行在展示的数据的索引,如果是分页,即为当前页的索引;index:表示当前行在整个数据中的索引,如果是分页需要自行从数据中获得索引 :::
源代码
/components/ReTable/src/editor.vue
可以通过查看具体实现,如果遇到问题可以留言或者提出issue。