<>问题描述

在开发中遇到一个需求,即实现table列的拖拽,但是调研发现,大部分是基于sorttable.js这个包实现的,但是通过实际应用,发现sorttable.js用在操作element
table 组件中并不是很舒服,总会莫名其妙的冒出一些异常bug,于是自行封装一个table 列拖拽组件。

<>效果演示:

具体效果如下:

<>难点概括

①element table header插槽应用

②drag知识点应用

③splice()方法理解

<>演示代码
<template> <div> <h1>基于element-ui table列拖拽实现</h1> <drag-head :head-columns=
"tableHead" :data="tableData"></drag-head> </div> </template> <script> import
DragHeadfrom "./dragHead.vue"; export default { name: "DragHeadCase", components
: { DragHead }, data() { return { tableHead: [ { label: "零零", prop: "v0", width:
150, }, { label: "一一", prop: "v1", width: 150, }, { label: "二二", prop: "v2",
width: 150, }, { label: "三三", prop: "v3", width: 150, }, { label: "四四", prop:
"v4", width: 300, }, { label: "五五", prop: "v5", }, { label: "六六", prop: "v6", },
{ label: "七七", prop: "v7", }, { label: "八八", prop: "v8", }, { label: "九九", prop:
"v9", }, ], tableData: [ { v0: "2016-05-02", v1: "王小虎", v2: "上海", v3: "普陀区", v4:
"上海市普陀区金沙江路 1518 弄", v5: 200333, v6: "2016-05-02", v7: "王小虎", v8: "上海", v9:
"普陀区", }, { v0: "2016-05-02", v1: "王小虎", v2: "上海", v3: "普陀区", v4: "上海市普陀区金沙江路
1518 弄", v5: 200333, v6: "2016-05-02", v7: "王小虎", v8: "上海", v9: "普陀区", }, { v0:
"2016-05-02", v1: "王小虎", v2: "上海", v3: "普陀区", v4: "上海市普陀区金沙江路 1518 弄", v5:
200333, v6: "2016-05-02", v7: "王小虎", v8: "上海", v9: "普陀区", }, { v0: "2016-05-02",
v1: "王小虎", v2: "上海", v3: "普陀区", v4: "上海市普陀区金沙江路 1518 弄", v5: 200333, v6:
"2016-05-02", v7: "王小虎", v8: "上海", v9: "普陀区", }, { v0: "2016-05-02", v1: "王小虎",
v2: "上海", v3: "普陀区", v4: "上海市普陀区金沙江路 1518 弄", v5: 200333, v6: "2016-05-02", v7:
"王小虎", v8: "上海", v9: "普陀区", }, ], }; }, }; </script>
<>table 列 拖拽组件实现代码
<template> <div style="width: 1000px"> <el-table ref="elTable" border style=
"width: 100%" v-bind="$attrs" v-on="$listeners" :key="headKey" :cell-class-name=
"cellClassName" :header-cell-class-name="cellClassName" > <!-- 循环表头 --> <
template v-for="(col, index) in tableHead"> <el-table-column :key="index" :prop=
"col.prop" :align="col.align || 'center'" :width="col.width || 100" > <!--
通过插槽为表头绑定mousedown和dragover方法--> <template slot="header" slot-scope="{ column,
$index }"> <span @mousedown="handleMounseDown($event, column, $index)" @dragover
="handleDragover($event, column, $index)" > {{ col.label }} </span> </template>
</el-table-column> </template> </el-table> </div> </template> <script> export
default { props: { headColumns: Array, }, mounted() { /**
备用操作(如果需要对headColumns数组操作) */ this.tableHead = this.headColumns; }, data() {
return { tableHead: [], // 拖拽状态 dragState: { start: -3, // 起始元素的 index
防止初始化cellStyle时序号、展开等默认样式改变,最好小于-3 end: -3, // 移动鼠标时所覆盖的元素 index dragging: false
, // 是否正在拖动 direction: undefined, // 拖动方向 }, headKey: "dragHead", //
表头数组变换位置时,重绘table(不更新该值,表头数组变化时,页面不会改变) scrollX: 0, // 初始x轴scroll位置(用于定位X轴滚动条) }
; }, methods: { /** 鼠标摁下触发 */ handleMounseDown(e, column, $index) { this.
dragState.dragging = true; this.dragState.start = parseInt($index - 0); //
添加鼠标抬起事件 消除鼠标摁下立刻松开问题 document.addEventListener("mouseup", this.handleMouseUp);
// 添加拖拽结束事件 document.addEventListener("dragend", this.handleMouseUp); //
对选中的表头允许其拖拽 const dragclass = ".el-table__header-wrapper ." + column.id; const
dragDom= document.querySelectorAll(dragclass); dragDom.forEach((dom) => { //
允许表头块可以被拖拽 draggable 属性 不允许拖拽dragover等相关拖拽事件无法触发 dom.setAttribute("draggable",
true); }); }, /** 鼠标在拖拽移动时触发 */ handleDragover(e, column, $index) { if (this.
dragState.dragging) { // 获取当前滚动条的位置 const scrollDom = this.$refs.elTable.
bodyWrapper; this.scrollX = scrollDom.scrollLeft; const index = parseInt($index
- 0); // 记录起始列 /** 实时更改鼠标处于表头的位置 */ if (index - this.dragState.start !== 0) {
this.dragState.direction = index - this.dragState.start < 0 ? "left" : "right";
// 判断拖动方向 this.dragState.end = parseInt($index - 0); } else { this.dragState.end
= this.dragState.start; this.dragState.direction = null; } } }, /** 鼠标抬起或拖拽结束触发
*/ handleMouseUp() { // 更新拖拽后的表头 this.headDraged(this.dragState); const { end }
= this.dragState; // 初始化拖动状态 this.dragState = { start: end, //记录最后拖动的位置 end: -9,
dragging: false, direction: undefined, }; document.removeEventListener("mouseup"
, this.handleMouseUp); document.removeEventListener("dragend", this.
handleMouseUp); setTimeout(() => { // 重置拖拽状态 this.dragState.start = -9; }, 500);
}, // 更新拖拽后的表头 headDraged({ start, end, direction }) { if (direction) { const
originColumn= this.tableHead[start]; // 有位置交换时,原先位置的元素删除,再在目标处插入 this.tableHead.
splice(start, 1); this.tableHead.splice(end, 0, originColumn); this.headKey =
new Date().getTime() + ""; // 更新table key值 this.$nextTick(() => { //
因为表头重绘后滚动条会移到最左端初始位置,因此如果是在中间部分拖拽,还需要把滚动条在定位到该位置 this.$refs.elTable.bodyWrapper.
scrollLeft= this.scrollX; }); } }, // 拖动虚线样式设置 cellClassName({ columnIndex }) {
const { start, end, direction } = this.dragState; const target = columnIndex - 0
; if (target === start) { // 被移动的元素 return "drag_start"; } else if (target ===
end) { // 要移动的位置 return `drag_end_${direction}`; } return ""; }, }, }; </script>
<style lang="scss" scoped> ::v-deep .el-table { .drag_start { opacity: 0.8;
background-color: rgba(0, 0, 0, 0.938); color: #f3e8e8fd; transition: all 0.3s
cubic-bezier(0.23, 1, 0.32, 1); } .drag_end_left { border-left: 2px dotted rgba(
0, 0, 0, 0.938); } .drag_end_right { border-right: 2px dotted rgba(0, 0, 0,
0.938); } } </style>
<>最后

该组件目前仅支持最简单的拖拽效果,后续开发出兼容fixed、checkbox、expand 以及操作列的组件会及时更新文章,有兴趣的朋友可以点赞收藏。

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信