前言
这是一个基于 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 demothis.weekPlan.sort((e1, e2) => e1.sort - e2.sort)console.log(this.weekPlan)},created() {},methods: {handleSortable() {let _this = thisconst 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 < oldIndexlet oldEntry = _this.weekPlan[oldIndex]// 2 -> 0if(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] = oldEntryconsole.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” 的配置, 否则 可能会造成一些 奇怪的问题
完