Element-UI ,Table组件实现拖拽效果

8,087 阅读2分钟

最近业务需求要添加table的拖拽,但是element并没有table的拖拽功能,只能自己添加了。

找了两个组价库sortablejsvuedraggable,后者是基于前者实现的更加符合vue标准的库,依赖于前者,但是项目中用的element的table, vuedraggable在这个基础上就不能使用,看官方的示例 , 所以只能使用sortablejs了,我直接安装了vuedraggable,它依赖sortablejs可以直接使用里面的特性,万一后期再有其他拖拽的功能,这个库上手还比较方便。

Sortable使用示例

<template>
  <div>
    <el-table
      :data="tableOptions.data"
      border
      size="small"
      row-key='id'
      ref="table"
      @cell-mouse-enter.once='rowDrop'
    >
      <el-table-column
        prop="sort"
        label="拖拽区域"
      >
        <template slot-scope="scope">
          <el-button type="text" size="small" class="handle">按住拖拽</el-button>
        </template>
      </el-table-column>

      <el-table-column
        v-for="item of column"
        :key="item.id"
        :prop="item.prop"
        :label="item.label"
        :width="item.width"
        :fixed="item.fixed"
        :class-name="item['class-name']" //  这个是列拖拽使用
      />

    </el-table>
    <el-pagination
      background
      :hide-on-single-page="paginationOptions.showPage"
      :layout="paginationOptions.layout"
      :page-sizes="paginationOptions.pageSizes"
      :total="paginationOptions.total"
      :page-size="paginationOptions.pageSize"
      :current-page="paginationOptions.currentPage"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>
<script>
  import Sortable from 'sortablejs';
  import table from '@/mixins/table';

  export default {
    mixins: [table],
    props: {},
    data() {
      return {
        column: [
          {
            prop: 'id',
            label: '序号',
            width: 100,
          },
          {
            prop: 'roleName',
            label: '角色名称',
            'class-name': 'canDrag',
          },
          {
            prop: 'createTime',
            label: '创建时间',
            'class-name': 'canDrag',
          },
          {
            prop: 'roleId',
            label: '角色Id',
            width: 100,
          },
          {
            prop: 'edit',
            label: '编辑',
            width: 180,
            fixed: 'right',
          },
        ],
        paginationOptions: {
          pageSizes: [10, 20, 30, 40],
        },
      };
    },
    watch: {
      'tableOptions.data': {
        deep: true,
        handler: function(newData) {
          // console.log(newData);  // 可以发现每次拖拽后数据发生了改变
        },
      },
    },
    mounted() {
      this.tableOptions.data = [
        { id: '1', roleName: 'test1', createTime: '202011115', roleId: 10 },
        { id: '2', roleName: 'test2', createTime: '202011116', roleId: 21 },
        { id: '3', roleName: 'test3', createTime: '202011117', roleId: 22 },
        { id: '4', roleName: 'test4', createTime: '202011118', roleId: 25 },
      ];
    },
    methods: {
      rowDrop() {
        // 行拖拽
        const _this = this;
        const tbody2 = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody');
        Sortable.create(tbody2, {
          handle: '.handle',
          animation: 150,
          onChoose() {
            //选择元素
            _this.column[_this.column.length - 1].fixed = false;
          },
          onUnchoose: function(evt) {
            // 取消选择元素
            _this.column[_this.column.length - 1].fixed = 'right';

          },
          onEnd({ newIndex, oldIndex }) {
            // 拖拽完成
            const currRow = _this.tableOptions.data.splice(oldIndex, 1)[0];
            _this.tableOptions.data.splice(newIndex, 0, currRow);
          },
        });

        // // 列拖拽
        // const tbody = this.$refs.table.$el.querySelector('.el-table__header-wrapper tr');
        // Sortable.create(tbody, {
        //   draggable: '.canDrag',
        //   animation: 150,
        //   onEnd({ newIndex, oldIndex }) {
        //     // 拖拽完成
        //     const currRow = _this.column.splice(oldIndex - 1, 1)[0];
        //     _this.column.splice(newIndex - 1, 0, currRow);
        //   },
        // });
      },
    },
  };
</script>

<style type="text/scss" lang="scss">
  .handle, .canDrag {
    cursor: move
  }

  ::v-deep .hover-row > td {
    background-color: #fff !important;
  }

  ::v-deep .sortable-chosen > td {
    // 拖动的样式
    background-color: #eff2f6 !important;
  }

  ::v-deep .el-table--enable-row-hover .el-table__body tr:hover > td {
    // 修复拖拽的时候hover的不消失的问题
    background-color: #fff;
  }
</style>

上面的代码有一些注意的地方我一一列举下来

  1. element table务必指定row-key,row-key必须是唯一的,不然会出现排序不对的情况。
  2. 我在table里面使用了fixed,最后一列是固定在右侧,用过element的知道这是两个table的拼接成的,所以拖拽是没有反应的
    1. 因为上面我们有指定row-key,所以拖拽后不会出现错位的情况,但是在拖拽的时候,是有很明显的错位出现。
    2. 我解决的思路是在拖拽的时候先把这个固定取消onChoose,然后拖拽完成后在把固定加上onUnchoose,具体看两个方法的代码
  3. 样式table会有鼠标滑过的效果,但是使用拖拽后,鼠标滑过后的效果不消失,甚至会出现很多个鼠标滑过的效果,解决的方式比较粗暴,我是把所有的滑过的效果全部取消了,谁有更好的方案欢迎评论区留言。
    1. 不使用fixed,可以防止2、3 问题
  4. mounted生命周期调取的rowDrop方法,会出现的问题是,如果在created调取的接口量大在mounted周期可能不能更好的渲染完成,此时将获取不到tbody,所以这里我的解决办法就是使用,table组件自带的@cell-mouse-enter.once='rowDrop',hover滑过的时候完成初始化,加上once是他只需要执行一次,或者更粗暴的办法直接使用定时器。

参考文档

draggable

sortable