描述:使用vue3 + element plus实现树状表格的拖拽排序功能 index.vue
<el-table
ref="tableRef"
:data="tableData"
row-key="id"
style="width: 100%"
:tree-props="tableTreeProps"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column
fixed="right"
label="操作"
width="204"
className="operationCloumn">
<template v-slot="scope">
<span @click="deleteClick(scope.row)">编辑</span>
<span @click="deleteClick(scope.row)">{{scope.row.enable ? '停用' :'启用'}}</span>
<span @click="deleteClick(scope.row)">删除</span>
<i class="moveIcon iconfont icon-yidong-16px"></i>
</template>
</el-table-column>
</el-table>
使用:传入表格tbody的dom元素和对应的表格数据
import { useTableDragSort } from '@/hooks/common'
onMounted(() => {
const tableBodyDom = document.querySelector('.el-table .el-table__body-wrapper .el-scrollbar__wrap tbody') as HTMLElement
useTableDragSort(tableBodyDom, tableData.value)
})
watch(tableData.value, () => {
console.log('数据改变')
})
common.ts 封装拖拽的实现方法
const lightLine = document.createElement('div')
lightLine.style.cssText += ';height: 1px; position: absolute; top: 0; left: 0; right: 0; background-color: skyblue; z-index: 999; display: none'
export async function useTableDragSort (tableBodyDom: HTMLElement, tableData: any[]) {
setTimeout(() => {
const trList = tableBodyDom.querySelectorAll<HTMLTableRowElement>('tr')
const trHeight = trList[0].clientHeight
const dragEnterTextList = tableBodyDom.querySelectorAll<HTMLElement>('tr .dragEnterText')
const moveIconDoms = tableBodyDom.querySelectorAll<HTMLElement>('.moveIcon')
tableBodyDom.appendChild(lightLine)
const flatData = getChildrenItem(tableData, [])
let currentDragDom: HTMLTableRowElement
let currentDragIndex: number
for (let i = 0; i < trList.length; i++) {
moveIconDoms[i].onmousedown = () => {
trList[i].setAttribute('draggable', 'true')
currentDragDom = trList[i]
currentDragIndex = i
}
moveIconDoms[i].onmouseup = () => {
trList[i].removeAttribute('draggable')
}
trList[i].ondragenter = (e: DragEvent) => {
if (notDragSonNode(flatData[currentDragIndex], flatData[i])) {
lightLine.style.cssText += ';display: none'
return
}
const dragEnterText = trList[i].querySelector('.dragEnterText') as HTMLElement
const parentDom = dragEnterText.offsetParent as HTMLElement
const left = dragEnterText.offsetLeft + parentDom.offsetLeft
const top = Math.max(Math.abs(Math.round(e.offsetY / trHeight)) * trHeight + trList[i].offsetTop - 1, 0)
lightLine.style.cssText += `;left: ${left}px; top: ${top}px; display: block`
}
trList[i].ondragover = (e: Event) => {
e.preventDefault()
}
trList[i].ondrop = (e: DragEvent) => {
currentDragDom.removeAttribute('draggable')
if (notDragSonNode(flatData[currentDragIndex], flatData[i])) return
const positionValue = Math.abs(Math.round(e.offsetY / trHeight))
changeData(tableData, flatData[currentDragIndex], flatData[i], positionValue)
}
trList[i].ondragend = () => {
lightLine.style.cssText += ';display: none'
}
dragEnterTextList[i].ondrop = (e: Event) => {
if (currentDragIndex === i) return
e.stopPropagation()
console.log('text:', dragEnterTextList[i], i)
}
}
}, 0)
function getChildrenItem (arr: any[], res: any[]) {
if (!arr) return res
arr.forEach(checkItem => {
res.push({
...checkItem
})
getChildrenItem(checkItem.children, res)
})
return res
}
function notDragSonNode (dragData: any, enterData: any) {
if (dragData.id === enterData.id) return true
if (!dragData.children) return false
let children = JSON.parse(JSON.stringify(dragData.children))
while (children.length > 0) {
let cur = children.pop()
if (cur.id === enterData.id) {
return true
} else if (cur.children){
children.push(...cur.children)
}
}
return false
}
function changeData (parentData: any[], dragData: any, enterData: any, positionValue: number) {
const stack = [parentData]
let addOk = false
let removeOk = false
while (stack.length > 0) {
const cur = stack.pop() || []
const ids = cur.map((item: any) => item.id)
let dragIndex = ids.indexOf(dragData.id)
let enterIndex = ids.indexOf(enterData.id)
if (dragIndex >= 0 && enterIndex >= 0) {
if ((dragIndex - enterIndex === -1 && positionValue === 0)) return
if ((dragIndex - enterIndex === 1 && positionValue === 1)) return
}
if (dragIndex >= 0 && !removeOk) {
cur.splice(dragIndex, 1)
removeOk = true
}
if (enterIndex >= 0 && !addOk) {
addOk = true
if (positionValue === 0) {
cur.splice(enterIndex, 0, dragData)
} else {
cur.splice(enterIndex + 1, 0, dragData)
}
useTableDragSort(tableBodyDom, tableData)
}
if (!addOk || !removeOk) {
cur.forEach((item:any) => stack.push(item.children))
}
}
}
}
css 这里由于在表格中多添加了蓝色线条,所以body要添加position: relation的定位属性
tbody {
position: relative;
}
|