平时业务开发中可能会遇到Table组件行拖动排序的需求,把排序后的数据给后端。ElTable组件暂时不支持,那来看看我是如果扩展这个功能的。
1. 先看下扩展后的基本使用姿势
<template>
<el-draggable-table
row-key="id"
:data="tableData"
style="width: 100%"
@sort-data="handleSortData"
>
<el-table-column prop="date" label="日期" width="180" />
<el-table-column prop="name" label="姓名" width="180" />
<el-table-column prop="address" label="地址" />
</el-draggable-table>
</template>
<script setup>
const tableData = [
{
date: '2024-08-01',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 1,
},
{
date: '2024-08-02',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 2,
},
{
date: '2024-08-03',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 3,
},
{
date: '2024-08-04',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 4,
},
{
date: '2024-08-05',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 5,
},
]
const handleSortData = (data) => {
console.log('排序后的数据', data)
}
</script>
嗯,使用姿势相比el-table只是把el-table -> el-draggable-table,这样就结束了。 看下运行效果
2. 指定某列拖动
<template>
<el-draggable-table
border
row-key="id"
:data="tableData"
style="width: 100%"
:draggable-props="{
handle: '.move',
}"
@sort-data="handleSortData"
>
<el-table-column prop="drag" label="" width="40" class-name="move">
<el-icon><Rank /></el-icon>
</el-table-column>
<el-table-column prop="date" label="日期" width="180" />
<el-table-column prop="name" label="姓名" width="180" />
<el-table-column prop="address" label="地址" />
</el-draggable-table>
</template>
<script setup>
import { Rank } from '@element-plus/icons-vue'
const tableData = [
{
date: '2024-08-01',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 1,
},
{
date: '2024-08-02',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 2,
},
{
date: '2024-08-03',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 3,
},
{
date: '2024-08-04',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 4,
},
{
date: '2024-08-05',
name: 'Summer',
address: 'No. 189, Grove St, Los Angeles',
id: 5,
},
]
const handleSortData = (data) => {
console.log('排序后的数据', data)
}
</script>
可以看的出来,只需给指定需要拖动的列,加上一个类名,注意这个类名属性class-name本身就是el-table-column组件自带的,然后给el-draggable-table组件指定handle: '.move'属性即可,这样整行拖动就改成了指定某列拖动了,看下效果
3. 可扩展
有了上述功能后且保留了ElTable组件的所有功能,那么就可以基于该组件封装其它组件了,比如下面的这个,大家可能也遇到过类似的需求
这个demo的代码就不黏贴了,因为不属于本次帖子的主题,想看的同学可以点这个文档地址
4. ElDraggableTable组件实现
直接贴代码
import { defineComponent, h, nextTick } from 'vue'
import { ElTable, useNamespace } from 'element-plus'
import { UseSortable } from '@vueuse/integrations/useSortable/component'
import { moveArrayElement } from '@vueuse/integrations/useSortable'
import { isArray } from '@element-plus/utils'
import { draggableTableProps } from './draggable-table'
import type { VNode } from 'vue'
const ElTableComp: any = { ...ElTable }
const SORT_DATA_EVENT = 'sort-data'
// 重写 TableBody render 函数
const TableBody = {
...ElTableComp.components.TableBody,
render() {
const { wrappedRowRender, store } = this
const data = store.states.data.value || []
const tableIns = this.context
const { draggableProps } = tableIns.props
const options = {
animation: 300,
...draggableProps,
onUpdate: (e: any) => {
const { oldIndex, newIndex } = e
// update list
if (isArray(store.states.data.value)) {
moveArrayElement(store.states.data, oldIndex, newIndex, e)
nextTick(() => {
const newData = store.states.data.value
store.commit('setData', newData)
tableIns.emit(SORT_DATA_EVENT, newData, { e })
})
}
},
}
return h(
UseSortable,
{ options, tag: 'tbody', tabIndex: -1, modelValue: [] },
{
default: () =>
data.reduce((acc: VNode[], row: any) => {
return acc.concat(wrappedRowRender(row, acc.length))
}, []),
}
)
},
}
const ElTableClone = {
...ElTableComp,
name: 'ElTableClone',
components: { ...ElTableComp.components, TableBody },
emits: [...ElTableComp.emits, SORT_DATA_EVENT],
props: {
...ElTableComp.props,
...draggableTableProps,
},
}
const ElDraggableTable = defineComponent({
name: 'ElDraggableTable',
setup(props, { slots, attrs }) {
const ns = useNamespace('draggable-table')
return () =>
h(
ElTableClone,
{
...attrs,
class: ns.b(),
},
slots
)
},
})
export default ElDraggableTable
- 需要熟悉vue render函数,不太熟悉的同学可以去vue官网学习下
- 需要了解el-table组件的内部实现(大概看看即可,不必深究,深究的话当然更好)
- 接下来就是上面重写 TableBody render 函数即可
- 因为是修改ElTable组件的render,整个组件并不涉及css
喜欢的同学,希望可以给个小星星,谢谢