为了在多个页面复用表格拖动功能,我们可以将其封装成以下两种形式的公共方法:
方案一:封装为 Vue 指令(推荐)
1. 创建指令文件 src/directives/tableDrag.js
import interact from 'interactjs';
export default {
inserted(el, binding) {
const tableBody = el.querySelector('.el-table__body-wrapper');
if (!tableBody) return;
// 设置初始样式
tableBody.style.cursor = 'grab';
tableBody.style.userSelect = 'none';
// 初始化交互
interact(tableBody).draggable({
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent'
})
],
listeners: {
start: () => {
tableBody.style.cursor = 'grabbing';
},
move: (event) => {
tableBody.scrollLeft -= event.dx * (binding.value?.damping || 1);
},
end: () => {
tableBody.style.cursor = 'grab';
}
},
inertia: binding.value?.inertia || true,
autoScroll: binding.value?.autoScroll || true
});
},
unbind(el) {
const tableBody = el.querySelector('.el-table__body-wrapper');
if (tableBody) {
interact(tableBody).unset();
}
}
};
2. 全局注册指令 src/main.js
import tableDrag from '@/directives/tableDrag';
Vue.directive('table-drag', tableDrag);
3. 在组件中使用
<template>
<el-table
v-table-drag="{ damping: 0.8, inertia: true }"
:data="tableData"
style="width: max-content"
border
>
<!-- 表格列 -->
</el-table>
</template>
方案二:封装为混合 (Mixin)
1. 创建混入文件 src/mixins/tableDrag.js
import interact from 'interactjs';
export default {
methods: {
initTableDrag(tableRef, options = {}) {
const tableEl = this.$refs[tableRef]?.$el;
if (!tableEl) return;
const tableBody = tableEl.querySelector('.el-table__body-wrapper');
if (!tableBody) return;
// 设置样式
tableBody.style.cursor = 'grab';
tableBody.style.userSelect = 'none';
// 初始化交互
this.tableDragInteract = interact(tableBody).draggable({
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent'
})
],
listeners: {
start: () => {
tableBody.style.cursor = 'grabbing';
},
move: (event) => {
tableBody.scrollLeft -= event.dx * (options.damping || 1);
},
end: () => {
tableBody.style.cursor = 'grab';
}
},
inertia: options.inertia !== false,
autoScroll: options.autoScroll !== false
});
},
destroyTableDrag(tableRef) {
const tableEl = this.$refs[tableRef]?.$el;
if (!tableEl) return;
const tableBody = tableEl.querySelector('.el-table__body-wrapper');
if (tableBody && this.tableDragInteract) {
this.tableDragInteract.unset();
}
}
},
beforeDestroy() {
if (this.tableDragInteract) {
this.tableDragInteract.unset();
}
}
};
2. 在组件中使用
<template>
<el-table
ref="myTable"
:data="tableData"
style="width: max-content"
border
>
<!-- 表格列 -->
</el-table>
</template>
<script>
import tableDragMixin from '@/mixins/tableDrag';
export default {
mixins: [tableDragMixin],
mounted() {
this.initTableDrag('myTable', {
damping: 0.7,
inertia: true
});
}
};
</script>
方案三:封装为高阶组件
1. 创建高阶组件 src/components/DraggableTable.vue
<template>
<div class="table-container">
<el-table
ref="table"
v-bind="$attrs"
v-on="$listeners"
style="width: max-content"
>
<slot></slot>
</el-table>
</div>
</template>
<script>
import interact from 'interactjs';
export default {
name: 'DraggableTable',
props: {
damping: {
type: Number,
default: 1
},
inertia: {
type: Boolean,
default: true
}
},
mounted() {
this.initDrag();
},
beforeDestroy() {
this.destroyDrag();
},
methods: {
initDrag() {
const tableBody = this.$refs.table?.$el.querySelector('.el-table__body-wrapper');
if (!tableBody) return;
tableBody.style.cursor = 'grab';
tableBody.style.userSelect = 'none';
this.interactInstance = interact(tableBody).draggable({
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent'
})
],
listeners: {
start: () => {
tableBody.style.cursor = 'grabbing';
},
move: (event) => {
tableBody.scrollLeft -= event.dx * this.damping;
},
end: () => {
tableBody.style.cursor = 'grab';
}
},
inertia: this.inertia
});
},
destroyDrag() {
if (this.interactInstance) {
this.interactInstance.unset();
}
}
}
};
</script>
<style scoped>
.table-container {
width: 100%;
overflow: hidden;
}
</style>
2. 在组件中使用
<template>
<draggable-table
:data="tableData"
:damping="0.8"
border
>
<el-table-column prop="date" label="日期" width="180"></el-table-column>
<!-- 其他列 -->
</draggable-table>
</template>
<script>
import DraggableTable from '@/components/DraggableTable';
export default {
components: {
DraggableTable
},
data() {
return {
tableData: []
}
}
};
</script>
对比三种方案
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Vue 指令 | 使用简单,全局可用 | 配置选项较少 | 简单拖动需求 |
Mixin | 灵活性高,可配置性强 | 需要在组件中调用方法 | 需要不同配置的多表格场景 |
高阶组件 | 封装彻底,使用最简洁 | 需要修改现有表格组件结构 | 新项目或可接受组件替换的场景 |
推荐按项目需求选择合适的方案,对于大多数项目,Vue 指令方案是最简单实用的选择。