Element表格支持单元格拖拽

3,344 阅读3分钟

Element表格支持单元格拖拽

前言

最近项目要实现一个拖拽效果,表格拖拽,网上有比较多的现成方案,但都是对原生的表格进行拖拽的,但我们的项目的表格已经基于Element表格去实现的,但是Element的API并没有实现单元格拖拽。

那怎么办呢?如果用原生表格去实现,样式和方法要重写,不利于日后的维护和开发。那来看看拖拽的原理是怎样的。

了解拖拽

经了解,HTML5提供专门的拖拽与拖放的API,基本的实现就是:

A元素加上draggable="true"属性,并且监听其拖拽到上面元素;而B元素监听其拖拽的开始、结束等一系列的流程,如此就可以实现A拖拽到B了。

详细可以看张鑫旭的-拖拽与拖放简介

Sortable.js库实现单元格拖拽

但是想了想,如果一个单元格就写一个拖拽,即使用遍历的写法,也是很难维护的。再查询一番,发现Sortable.js这个插件就能实现。

Sortable.js作用是实现列表的拖拽,其大概的原理是遍历子元素去添加拖拽属性的继而实现列表内的拖拽、不同列表的拖拽。

那不难想象,只要我们给Element表格的每一行(tr)通过Sortable.js去实现列表间的拖拽,那么就实现到功能了

思路分析到这里,直接上代码(注释已在代码中):

<template>
  <div class="container">
    <h1>计划表-支持拖拽</h1>
    <el-table class="drag-table" :data="schedule" border :cell-class-name="getCellClassName">
      <el-table-column v-for="item in x" :key="item.value" :prop="item.value" :label="item.label"></el-table-column>
    </el-table>
  </div>
</template>

<script>
import Sortable, { MultiDrag, Swap } from "sortablejs";
Sortable.mount(new MultiDrag(), new Swap());

export default {
  data() {
    return {
      // x轴的数据,定义表头使用和定位使用
      x: [
        { value: "time", label: "" },
        { value: "mon", label: "星期一" },
        { value: "tue", label: "星期二" },
        { value: "wed", label: "星期三" },
        { value: "thu", label: "星期四" },
        { value: "fri", label: "星期五" },
        { value: "sat", label: "星期六" },
        { value: "sun", label: "星期日" }
      ],
      // 要渲染的表格数据
      schedule: [
        {
          time: "9:00~10:00",
          mon: "语文",
          tue: "",
          wed: "",
          thu: "",
          fri: "",
          sat: "",
          sun: ""
        },
        {
          time: "10:00~11:00",
          mon: "",
          tue: "英语",
          wed: "",
          thu: "",
          fri: "",
          sat: "",
          sun: ""
        },
        {
          time: "11:00~12:00",
          mon: "",
          tue: "",
          wed: "数学",
          thu: "",
          fri: "化学",
          sat: "",
          sun: ""
        }
      ]
    };
  },
  mounted() {
    this.initDrag();
  },
  methods: {
    getCellClassName({ row, column }) {
      //   console.log(row, column, rowIndex, columnIndex);
      // 添加filtered属性
      if (!row[column.property]) {
        return "filtered";
      }
      return "";
    },
    initDrag() {
      // 找出每一行(tr)
      const children = document.querySelector(
        `.drag-table .el-table__body-wrapper tbody`
      ).children;

      // 给每一行添加列表拖拽,实现单元格拖拽
      for (let index = 0; index < children.length; index++) {
        const el = children.item(index);
        Sortable.create(el, {
          group: {
            name: "shared", // 定义为可列表间拖拽
          },
          swap: true, // 交换属性
          filter: ".filtered", // 过滤不能拖拽
          onEnd: evt => {
            // console.log("onEnd", evt);
            // 更新数据,这里暂时直接通过交换数据来拖拽,实际情况是会通过接口更新再拉取列表实现
            this.updateData({
              oldRowIndex: evt.from.rowIndex,
              newRowIndex: evt.to.rowIndex,
              oldColumnIndex: evt.oldIndex,
              newColumnIndex: evt.newIndex
            });
          }
        });
      }
    },
    updateData({ oldRowIndex, newRowIndex, oldColumnIndex, newColumnIndex }) {
      // console.log(oldRowIndex, newRowIndex, oldColumnIndex, newColumnIndex);

      // 交换数据
      const schedule = JSON.parse(JSON.stringify(this.schedule));
      const OldProperty = this.x[oldColumnIndex].value;
      const oldValue = schedule[oldRowIndex][OldProperty];
      const newProperty = this.x[newColumnIndex].value;
      const newValue = schedule[newRowIndex][newProperty];
      schedule[newRowIndex][newProperty] = oldValue;
      schedule[oldRowIndex][OldProperty] = newValue;

      // 强制刷新
      this.schedule = [];
      this.$nextTick(() => {
        this.schedule = schedule;
        this.$nextTick(() => {
          this.initDrag();
        });
      });
    }
  }
};
</script>

<style lang="scss" scoped>
.container {
  padding: 0 30px;
  background: #f1eded;
  height: 500px;
  padding-top: 30px;
}
</style>

效果

最后

详细的代码demo,我放在了Simon的Demo中,如果我的分享对你有用,给我点个赞;如果有什么说得不对的地方或者有更好的思路,欢迎评论或私信我~

参考: