Implement table column drag and drop based on element UI

Problem Description

I encountered a need during development, that is, to implement drag and drop of table columns. However, research found that most of them were implemented based on the sorttable.js package. However, through practical application, I found that sorttable.js is not used when operating element table components. It's very comfortable, but some abnormal bugs will always appear inexplicably, so I encapsulated a table column drag component.

Summary of difficulties

①element table header slot application

②Application of drag knowledge points

③Understanding of splice() method

Demo code

<template>
  <div>
    <h1>基于element-ui table列拖拽实现</h1>
    <drag-head :head-columns="tableHead" :data="tableData"></drag-head>
  </div>
</template>
<script>
import DragHead from "./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 column drag component implementation code

<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>

Then I encountered an error while operating

[Violation ] Added non-passive event listener to ascroll- blocking 'mousewheel’event Consider marking event handler as ’ passive’to make the page more responsive. See https: com/feature/574554 3795965952

translate:

[Conflict] Added non-passive event listener to ascroll - blocking "mousewheel" events Consider marking event handlers as "passive" to make the page more responsive. See https:com/feature/574554 3795965952

Solution:

npm i default-passive-events -S
main.js import 'default-passive-events'

at last

This component currently only supports the simplest drag and drop effect. If components compatible with fixed, checkbox, expand and operation columns are developed in the future, the article will be updated in time. Interested friends can like and collect it.

references

Implementing table column drag and drop based on element UI_element table column drag and drop_indifferent_blog's blog-CSDN blog

Added non-passive event listener to ascroll- blocking 'mousewheel'event Consider marking event handle_Gentleman's Horrible Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/weixin_46413834/article/details/132449307