将 Element UI 表格拖动功能提取为公共方法

发布于:2025-05-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

为了在多个页面复用表格拖动功能,我们可以将其封装成以下两种形式的公共方法:

方案一:封装为 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 指令方案是最简单实用的选择。


网站公告

今日签到

点亮在社区的每一天
去签到