前言
这是一个基于 sortablejs 来实现的 el-table 的拖拽功能的基础实现
然后 这个过程中遇到的一个比较特殊的问题是, 关于 el-table-column 的 fixed 的属性, 对于 sortablejs 这边来定位目标选择列 影响的一个问题
在基础的用例中, 使用 “.el-table__body-wrapper tbody” 去定位目标元素, 然后 带 class 为 draggableClass 的元素作为可以拖拽的元素, 来实现 拖拽的交互
基础的拖拽的实现
测试用例如下
<template>
<div class="testParent">
<el-table :data="weekPlan" row-key="id">
<el-table-column label="id" prop="id" ></el-table-column>
<el-table-column label="星期" prop="name" ></el-table-column>
<el-table-column label="移动" >
<div class="draggableClass" style="width:20px; height:20px; background-color: blue; cursor:move;" ></div>
</el-table-column>
<!-- <el-table-column label="移动fixedLeft" :fixed="'left'" >-->
<!-- <div class="draggableClass" style="width:20px; height:20px; background-color: blue; cursor:move;" ></div>-->
<!-- </el-table-column>-->
<!-- <el-table-column label="移动fixedRight" :fixed="'right'" >-->
<!-- <div class="draggableClass" style="width:20px; height:20px; background-color: blue; cursor:move;" ></div>-->
<!-- </el-table-column>-->
</el-table>
</div>
</template>
<script>
import Sortable from "sortablejs"
export default {
name: "HelloTableSortable",
data() {
return {
weekPlan: [
{
id: "01",
name: "monday",
sort: 10,
},
{
id: "02",
name: "tuesday",
sort: 22,
},
{
id: "03",
name: "wednesday",
sort: 12,
}
],
}
},
computed: {},
mounted() {
this.$nextTick(() => {
setTimeout(this.handleSortable, 100)
})
// sort demo
this.weekPlan.sort((e1, e2) => e1.sort - e2.sort)
console.log(this.weekPlan)
},
created() {
},
methods: {
handleSortable() {
let _this = this
const tbody = document.querySelector(".el-table__body-wrapper tbody")
Sortable.create(tbody, {
handle: ".draggableClass",
animation: 350,
easing: 'cubic-bezier(0.34,1.56,0.64,1)',
onEnd: ({newIndex, oldIndex}) => {
let isMoveUp = newIndex < oldIndex
let oldEntry = _this.weekPlan[oldIndex]
// 2 -> 0
if(isMoveUp) {
for(let i=oldIndex; i>newIndex; i--) {
_this.weekPlan[i] = _this.weekPlan[i-1]
}
// 0 -> 2
} else {
for(let i=oldIndex; i<newIndex; i++) {
_this.weekPlan[i] = _this.weekPlan[i+1]
}
}
_this.weekPlan[newIndex] = oldEntry
console.log(_this.weekPlan.map(e => e.id))
}
})
}
},
}
</script>
<style>
</style>
效果如下, console 里面打印的是 拖拽结束之后的一个最新的顺序
具体的记录的 sort, 就是该元素的索引
从 dom 结构来看, 是一个单纯的一个 table, 然后下面是 th, 各个 tr
然后 我们业务代码中基于 “.el-table__body-wrapper tbody . draggableClass” 可以正常的定位到目标拖拽元素
目标拖拽元素在 带fixed的 el-table-column 上面的异常情况
基于上面的测试用例, 注释掉 “移动” 列, 解除注释 “移动fixedLeft”, “移动fixedRight” 列
然后 这时候 你可以发现, 拖动 这两列 都不行了, 不管是拖拽 “移动fixedLeft”列, 还是 “移动fixedRight”列
这个时候 页面 dom 结构如下
可以看到这时候 dom 树上面有三个 table, 一个在 el-table 下面, 一个在 el-table 下面的 el-table__fixed 下面, 一个在 el-table 下面的 el-table__fixed-right 下面
el-table 下面的 el-table__fixed 下面 table, 主要是这部分配置了 fixed=’left‘ 的 el-table-column 的展示交互
el-table 下面的 el-table__fixed-right 下面 table, 主要是这部分配置了 fixed=’right‘ 的 el-table-column 的展示交互
页面上 “移动fixedRight“ 选择列右键查看元素, 可以看到 在最上层的元素是 使用的是包含在 el-table 下面的 el-table__fixed-right 下面 table 下面的元素
页面上 “移动fixedLeft“ 选择列右键查看元素, 可以看到 在最上层的元素是 使用的是包含在 el-table 下面的 el-table__fixed 下面 table 下面的元素
页面上 数据中间列 选择列右键查看元素, 可以看到 在最上层的元素是 使用的是包含在 el-table 下面的 el-table__fixed 下面 table 下面的元素
然后 我们业务代码中拿到的元素是 数据中间列
然后 它下面的 draggableClass 的元素在页面上, 不是 z-index 在最上面的元素, 页面的点击, 拖拽等等事件 选中的不是该元素
const tbody = document.querySelector(".el-table__body-wrapper tbody")
所以 需要更新 Sortable.create 的时候选择的元素
假设我们需要拖拽 “移动fixedRight” 的元素, 则我们更新 tbody 的 selector 如下
const tbody = document.querySelector(".el-table__fixed-right tbody")
但是这样会存在一个问题就是, 因为 fixedLeft列 和 数据中间列 和 fixedRight列 是分开的, 然后 我这里将 fixedRight列 的 第二行 和 第一行 交换了位置
但是 fixedLeft列 和 数据中间列 的第二行 和 第一行 是没有交换位置的
这时候 就造成了数据的错位
如下就使 拖拽中, 拖拽之后 的截图, 可以看到的是 第二行的 fixedLeft列 和 数据中间列 的 第二行 和 第一行 是没有交换位置的
造成了数据的展示错误
所以再这种 el-table 中基于 sortablejs 来实现拖拽的场景下面
需要尽量避免使用 fixed=”left”, fixed=”right” 的配置, 否则 可能会造成一些 奇怪的问题
完