vue3 + elementplus + sortablejs实现树形表格拖拽排序
实现功能
1、树形表格每次只展开一行,新的一行展开,旧行就需要收起(手风琴效果)
2、一级行能够拖拽排序,所有子级行不能拖拽
3、树形表格需要实现懒加载
UI包和sortablejs包
element plus
sortable
代码
html代码
<template><el-tablev-loading="loading"ref="tableRef":data="tableData"style="width: 100%"row-key="id"borderlazy:load="load":tree-props="{ children: 'children', hasChildren: 'hasChildren' }" //指定那些行包含子节点@expand-change="handleExpand":expand-row-keys="expandRowKeys":row-class-name="rowClassName"><el-table-column prop="date" label="Date" /><el-table-column prop="name" label="Name" /><el-table-column prop="address" label="Address" /></el-table>
</template>
JS代码
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
import Sortable from 'sortablejs'interface User {id: stringdate: stringname: stringaddress: stringhasChildren?: booleanchildren?: User[]isFirst?: boolean //是否一级节点
}const tableData = ref<User[]>([])
//设置 Table 目前的展开行
const expandRowKeys = ref<string[]>([])
const loading = ref<boolean>(false)
const tableRef = ref()const load = (row: User, _treeNode: unknown, resolve: ((date: User[]) => void) | undefined) => {// 模拟接口返回的数据setTimeout(() => {const loadData = [{id: `${row.id}-1`,date: '2024-09-01',name: `${row.id}-name`,address: 'china beijing'},{id: `${row.id}-2`,date: '2024-09-02',name: `${row.id}-name`,address: 'china beijing'}]resolve?.(loadData)}, 1000)
}// 遍历一级数据手动添加 isFirst 字段,用于添加类名,用于排序,和设置expand-row-keys (展开行的key)
const data: User[] = [{id: '1',date: '2024-09-01',name: 'zhangsan',address: 'china beijing',isFirst: true},{id: '2',date: '2024-09-02',name: 'zhangsan',hasChildren: true,address: 'china beijing',isFirst: true},{id: '3',date: '2024-09-09',name: 'zhangsan',hasChildren: true,address: 'china beijing',isFirst: true},{id: '4',date: '2024-09-11',name: 'zhangsan',hasChildren: true,address: 'china beijing',isFirst: true}
]const rowClassName = ({ row }: { row: User }) => {// 给第一级行 设置类名 用于sortable的draggable字段,指定类可排序if (row?.isFirst) {return 'sortItem'}return ''
}const handleExpand = (row: User, expanded: boolean) => {// 一级行实现手风琴if (row?.isFirst) {expandRowKeys.value = expanded ? [row.id] : []}
}const rowDrop = () => {const tbody = document.querySelector('.el-table__body-wrapper tbody') as HTMLElementSortable.create(tbody, {draggable: '.sortItem', // 拥有sortItem类名的行才能排序onStart: function () {// 拖拽开始收起所有张开的行expandRowKeys.value = []},onEnd(evt: any) {// newDraggableIndex , oldDraggableIndex 是可拖拽元素的下标,更具这两个下表获取id,传递给后端排序,重新获取数据//const { newDraggableIndex, oldDraggableIndex } = evt// const fromId = tableData.value[evt.oldDraggableIndex].id// const toId = tableData.value[evt.newDraggableIndex].id// 清空数据,重新渲染node,处理数据节点复用,视图未更新的问题const {newIndex, oldIndex} = evtif (oldIndex === newIndex) returnnextTick(() => {// 下次渲染更新数据loading.value = trueconst currRow = tableData.value.splice(oldIndex, 1)[0]setTimeout(() => {tableData.value.splice(newIndex, 0, currRow) //排序后的数据loading.value = false}, 200)})}})
}onMounted(() => {// 首次加载获取数据loading.value = truesetTimeout(() => {tableData.value = dataloading.value = false}, 3000)// 拖拽处理document.body.ondrop = function (event) {event.preventDefault()event.stopPropagation()}// 拖拽功能与配置rowDrop()
})
</script>