---
theme: nico
---
# 前言
项目中一个看起来简单的功能需求点,描述也简单:表格数据支持拖动上下移,支持通过选择表格行数据点击按钮上下移。效果大概如下(模拟数据共7条,去除了关联的业务操作)
![1.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5d6fcfa0c7fb4c0092a041f1861ab50e~tplv-k3u1fbpfcp-watermark.image?)
原型评审的时候表格上下移所在的项目模块业务功能操作比较复杂,因为内容比较多产品经理讲的时候这里上下移的功能时一句话就带过了,原型功能描述也简单,做的时候发现,这里面原来有坑
# 思路梳理
抛开业务复杂度,只分析这一小部分的上下移功能技术层的需求实现思路,上下移功能分为两种操作方式,一种是表格数据行拖动上下移;一种是复选框选中数据后,点击上下移按钮实现表格数据行移动
分析到这里的时候发现需求描述里面对于开发人员来讲,功能描述不完善
- 问题1: 表格数据行拖动上下移,是单行拖动,还是多行拖动?
- 问题2: 通过按钮操作上移的时候,支持多选吗? 单行移动还是几行移动?
- 问题3: 如果表格支持多选后上下移,那么不连续选中后数据怎么处理?如图
![2.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9886354778ea4f31b21695de07c5a35f~tplv-k3u1fbpfcp-watermark.image?)
- 问题4: 如果多选的数据,非连续选中情况下,有数据已经是最下方了,如何处理边界数据移动问题?如图
![3.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0acec6fe069842eaaf209be531445730~tplv-k3u1fbpfcp-watermark.image?)
- 问题5:如果选中的数据已经是最上或最下了,怎么移动呢?
- 问题6:如果表格数据全选,怎么移动?
## 这么多细节问题,产品经理咋不讲清楚写明白呢,喷他!
带着问题找产品经理确认,结果产品经理说他也没想到还有这么好些情况
![17434C27.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/67782740cc004f01898a931adfd97d83~tplv-k3u1fbpfcp-watermark.image?)
## 和产品经理一波 battle 后的结果
- 问题1:表格数据单行拖动上下移
- 问题2:按钮操作上下移,支持数据行多选,单行移动
- 问题3:表格数据不连续选中后,数据先合并,再以最靠上或靠下的数据为基础,向上或向下移动
- 根据选中的ID举例,默认表格ID排序为 1234567, 如果选中ID为 346,向下移动, 在数据层先把这三个数据截出来,整合,然后再插入到 7 的后面,整个表格数据ID排序就是 1257346。
- 问题4:如果多选的数据,非连续选中情况下,有数据已经是最下方了,数据整合后,再基于最上或最下的数据为基础向上或下移动
- 问题5:如果选中的数据已经是最上或最下了,直接上下移,不用提示
- 问题6:如果表格数据全选上下移直接提示 "不能选择全部数据上下移"
# 问题清楚了开始复制粘贴,啊呸!来让我们开始改变世界
确认完所有细节问题后,发现这表格数据移动功能实现逻辑代码还是不少的,为什么标题说这个上下移需求不简单呢?
主要这一部分功能评审的时候基本没算开发时间,和其他几个功能整个一起评审才给1天,这时候对于我们项目组已经排好工期的状态是不加工时的,开发人员自己加班赶进度保证项目整体进度,哎......
![057C00AE.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c21fd554eef41e9a041479c089e5f13~tplv-k3u1fbpfcp-watermark.image?)
## 表格数据行拖动上下移
表格数据行拖动这个原生实现就不考虑了,时间代价有点高,一般找个 npm 库就行,目前不错的拖拽库有 [Sortable.js](https://github.com/RubaXa/Sortable) 官方有 Vue 版 [Vue.Draggable](https://github.com/SortableJS/Vue.Draggable) ,因为拖动需求比较简单,发现一个更好用的库,我用的这个 [el-table-draggable](https://github.com/mizuka-wu/el-table-draggable) ,这个库的作者基于 `sortablejs` 封装了一下,在 Vue Element table 中使用更方便,只需要 `<el-table-draggable>` 包裹一下 `el-table` 就行了
~~~
import ElTableDraggable from 'el-table-draggable'
components: {
ElTableDraggable
}
<el-table-draggable>
<el-table
row-key="id"
ref="multipleTable"
:data="tableData"
tooltip-effect="dark"
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55"> </el-table-column>
<el-table-column label="ID" width="60">
<template slot-scope="scope">{{ scope.row.id }}</template>
</el-table-column>
<el-table-column label="日期" width="120">
<template slot-scope="scope">{{ scope.row.date }}</template>
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址" show-overflow-tooltip>
</el-table-column>
</el-table>
</el-table-draggable>
~~~
> 注意! `el-table` 中的 `row-key` 必须设置,否则无法拖拽。
如果只是简单实现一下表格数据行拖拽,读者以后功能开发可以优先考虑 `el-table-draggable` 这个库, 能够快速实现业务需求, 省下来的时间学习一会儿,看会儿掘金社区不香吗? `^-^`
## 表格数据选中点击按钮上下移
分析点击移动数据这里的实现逻辑,整个上下移动需要3个核心步骤:定位、删除、移动。这里直接上代码
### 一. 基于选中数据定位需要移动到的位置
~~~
// confirm slotIndex
let slotIndex = -1
this.tableData.forEach((item, index) => {
if (this.multipleSelection.length > 1) {
this.multipleSelection.forEach((v, k) => {
if (item.id === v.id) {
if (slotIndex === -1) {
slotIndex = index
} else {
if (slotIndex < index) {
slotIndex = index
}
}
}
})
} else {
this.multipleSelection.forEach((v, k) => {
if (item.id === v.id) {
slotIndex = index + 1
}
})
}
})
~~~
### 二. 删除表格中的选中数据
~~~
// delete selectedata
this.multipleSelection.forEach((item, index) => {
this.tableData.forEach((v, k) => {
if (item.id === v.id) {
this.tableData.splice(k, 1)
}
})
})
~~~
### 三. 将选中的数据移入指定位置
~~~
this.tableData.splice(slotIndex, 0, this.multipleSelection)
this.tableData = this.tableData.flat()
~~~
执行完这三个步骤后,表格数据就能按需移动了。在我们的实际项目场景中,由于表格数据内容太多,数据又都比较像,因此需要移动后的数据继续回显,这样用户能更直观,在实际项目业务表格由于数据内容太多了,也关联了其他业务功能操作,表格数据上下移动后选中回显渲染是异常的,需要使用 `setTimeout` `$nextTick()` 处理一下
~~~
setTimeout(async () => {
await this.$nextTick()
selectionTableData.forEach(row => {
this.$refs.table2.toggleRowSelection(row)
})
}, 10)
~~~
> 注意!
>
> 表格中数据选中,选中数据的数组是按照选中操作的顺序排列的,例如选中操作顺序为 321, 对于页面来讲,选中的数据是 123,这时候要按照页面的ID顺序进行整体移动,还是按照选中数据的顺序移动,最终效果不一样,目前我们的功能实现是按照选中顺序进行移动的
# Element Table 上下移完整功能演示
![screencast-localhost_8080-2022.07.16-12_21_47.webp](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/73ef752143074981995a17335bc2abe7~tplv-k3u1fbpfcp-watermark.image?)
## 技术栈版本
- "el-table-draggable": "^1.4.12",
- "vue": "^2.6.11",
- "element-ui": "^2.15.8",
# 演示代码地址
https://github.com/gywgithub/vue-admin-element/blob/main/src/views/Table2.vue
# 写在最后
表格数据移动也有其他的实现方案,欢迎大家评论区讨论交流,一起学习共同进步 `^-^`
我正在参与掘金技术社区创作者签约计划招募活动,[点击链接报名投稿](https://juejin.cn/post/7112770927082864653 "https://juejin.cn/post/7112770927082864653")。