Element-UI ,Table组件实现拖拽效果

最近业务需求要添加table的拖拽,但是element并没有table的拖拽功能,只能自己添加了。

找了两个组价库sortablejsvuedraggable,后者是基于前者实现的更加符合vue标准的库,依赖于前者,但是项目中用的element的table, vuedraggable在这个基础上就不能使用,看官方的示例 , 所以只能使用sortablejs了,我直接安装了vuedraggable,它依赖sortablejs可以直接使用里面的特性,万一后期再有其他拖拽的功能,这个库上手还比较方便。

Sortable使用示例

<template>
<el-card v-loading="fullscreenLoading">
<el-table
:data="tableOptions.data"
border
size="small"
row-key='roleId'
ref="table"
@cell-mouse-enter.once='rowDrop'
>
<el-table-column
prop="sort"
label="拖拽区域"
>
<template slot-scope="scope">
<el-button type="text" size="small" class="handle">按住拖拽</el-button>
</template>
</el-table-column>
<el-table-column
v-for="item of column"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:width="item.width"
:fixed="item.fixed"
/>
</el-table>
<el-pagination
background
:hide-on-single-page="paginationOptions.showPage"
:layout="paginationOptions.layout"
:page-sizes="paginationOptions.pageSizes"
:total="paginationOptions.total"
:page-size="paginationOptions.pageSize"
:current-page="paginationOptions.currentPage"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
</template>
<script>
import Sortable from 'sortablejs';
import table from "@/mixins/table"

export default {
name: 'TableDraggable',
mixins: [table],
props: {},
data() {
return {
column: [
{
prop: "roleId",
label: "序号",
width: 100,
},
{
prop: 'roleName',
label: "角色名称",
},
{
prop: "createTime",
label: "创建时间",
},
{
prop: "roleId",
label: "序号",
width: 100,
},
{
prop: 'edit',
label: "编辑",
width: 180,
fixed: 'right'
},
],
fullscreenLoading: true,
paginationOptions: {
pageSizes: [10, 20, 30, 40],
},
}
},
watch: {
'tableOptions.data': {
deep: true,
handler: function (newData) {
// console.log(newData); // 可以发现每次拖拽后数据发生了改变
}
}
},
methods: {
//行拖拽
rowDrop() {
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody');
const _this = this;
Sortable.create(tbody, {
handle: '.handle',
animation: 150,
onChoose() {
//选择元素
_this.column[_this.column.length - 1].fixed = false
},
onUnchoose: function (evt) {
// 取消选择元素
_this.column[_this.column.length - 1].fixed = 'right'

},
onEnd({newIndex, oldIndex}) {
// 拖拽完成
const currRow = _this.tableOptions.data.splice(oldIndex, 1)[0]
_this.tableOptions.data.splice(newIndex, 0, currRow)
}
})
}
}
}
</script>

<style type="text/scss" lang="scss" scoped>
.handle {
cursor: move
}

::v-deep .hover-row > td {
background-color: #fff !important;
}

::v-deep .sortable-chosen > td {
// 拖动的样式
background-color: #eff2f6 !important;
}

::v-deep .el-table--enable-row-hover .el-table__body tr:hover > td {
// 修复拖拽的时候hover的不消失的问题
background-color: #fff;
}
</style>

上面的代码有一些注意的地方我一一列举下来

  1. element table务必指定row-key,row-key必须是唯一的,不然会出现排序不对的情况。
  2. 我在table里面使用了fixed,最后一列是固定在右侧,用过element的知道这是两个table的拼接成的,所以拖拽是没有反应的
    1. 以因为上面我们有指定row-key,所以拖拽后不会出现错位的情况,但是在拖拽的时候,是有很明显的错位出现。
    2. 我解决的思路是在拖拽的时候先把这个固定取消onChoose,然后拖拽完成后在把固定加上onUnchoose,具体看两个方法的代码
  3. 样式table会有鼠标滑过的效果,但是使用拖拽后,鼠标滑过后的效果不消失,甚至会出现很多个鼠标滑过的效果,解决的方式比较粗暴,我是把所有的滑过的效果全部取消了,谁有更好的方案欢迎评论区留言。
    1. 不使用fixed,可以防止2、3问题
  4. mounted生命周期调取的rowDrop方法,会出现的问题是,如果在created调取的接口量大在mounted周期可能不能更好的渲染完成,此时将获取不到tbody,所以这里我的解决办法就是使用,table组件自带的@cell-mouse-enter.once='rowDrop',hover滑过的时候完成初始化,加上once是他只需要执行一次,或者更粗暴的办法直接使用定时器。

参考文档

draggable

sortable