实现功能
1、树形表格每次只展开一行,新的一行展开,旧行就需要收起(手风琴效果)
2、一级行能够拖拽排序,所有子级行不能拖拽
3、树形表格需要实现懒加载
UI包和sortablejs包
代码
html代码
<template>
<el-table
v-loading="loading"
ref="tableRef"
:data="tableData"
style="width: 100%"
row-key="id"
border
lazy
: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: string
date: string
name: string
address: string
hasChildren?: boolean
children?: 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 HTMLElement
Sortable.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} = evt
if (oldIndex === newIndex) return
nextTick(() => {
// 下次渲染更新数据
loading.value = true
const currRow = tableData.value.splice(oldIndex, 1)[0]
setTimeout(() => {
tableData.value.splice(newIndex, 0, currRow) //排序后的数据
loading.value = false
}, 200)
})
}
})
}
onMounted(() => {
// 首次加载获取数据
loading.value = true
setTimeout(() => {
tableData.value = data
loading.value = false
}, 3000)
// 拖拽处理
document.body.ondrop = function (event) {
event.preventDefault()
event.stopPropagation()
}
// 拖拽功能与配置
rowDrop()
})
</script>