sortablejs插件在el-table中的运用

我心飞翔 分类:vue

sortablejs插件在el-table中的运用

概述需求

有一个Table表格,由于数据是根据自增的ID进行排序显示的,有时了调整顺序会在数据库中直接操作数据表,来达到调整数据顺序的目的,因为为了实现在页面实现较为简单的拖拽排序,因此展开讨论。
最后sortablejs插件可以满足需求并可以快捷的实现功能。

参看资料:

官网:http://www.sortablejs.com/

中文文档:https://www.itxst.com/sortablejs/neuinffi.html

1、安装sortablejs插件

首先引入依赖,并重启项目

npm install sortablejs --save

2、实现效果

首先看下实现的效果如下:
1、这是原来的顺序:[1, 2, 3, 4]

2、通过鼠标拖拉即可改变位置: [4, 3, 1, 2]

3、编写vue页面(文末有完整页面)

3.1、在需要编写排序的页面引入sortablejs依赖
import Sortable from 'sortablejs';
3.2、编写el-table并定义ref=“tableRef” ,ref可以理解为id
<template>
  <div class="index">
    <el-row>
      <el-col>
        <el-table :data="tableData" ref="tableRef"
                  size="small" border stripe>
          <el-table-column align="center" label="模板ID" prop="id"></el-table-column>
          <el-table-column align="center" label="模板信息" prop="mc"></el-table-column>
        </el-table>
      </el-col>
    </el-row>

    <el-row style="margin-top: 20px">
      <el-col :offset="17" :span="6">
        <el-button-group>
          <el-button size="small" type="primary" @click="save">保存</el-button>
        </el-button-group>
      </el-col>
    </el-row>
  </div>
</template>
3.3、初始化页面

tableData: 页面初始化数据
newIndexList:复制初始化table的id,后续顺序调整将会直接对其操作。

export default {
  name: "index",
  components: {
    Sortable
  },
  data() {
    return {
      // 表单数据
      tableData: [
        {
          id: 1,
          mc: "模板一"
        },{
          id: 2,
          mc: "模板二"
        },{
          id: 3,
          mc: "模板三"
        },{
          id: 4,
          mc: "模板四"
        }],
      // 排序后的数据列表
      newIndexList: [],
    }
  },
  mounted() {
    // 复制原Table的id按循序存储newIndexList中,
    // 每一次调整位置会对newIndexList进行位置调整。
    this.tableData.forEach( item => {
      this.newIndexList.push(item.id);
    });
    //阻止火狐拖拽新建新页面
    document.body.addEventListener("drop", (event) => {
      event.preventDefault();
      event.stopPropagation();
    }, false);
    this.initSortableList();
  },
  methods: {
    // 更新排序
    initSortableList(){
      let el = this.$refs.tableRef.$el.querySelector('.el-table__body-wrapper tbody');
      //设置配置
      let _this = this
      Sortable .create(el, {
        animation: 150,
        sort: true,
        draggable: '.el-table__row', // 设置可拖拽行的类名(el-table自带的类名)
        forceFallback: true,
        onEnd({newIndex, oldIndex}) {
          let currRow = _this.newIndexList.splice(oldIndex, 1)[0];
          _this.newIndexList.splice(newIndex, 0, currRow);
        }
      })
    },
  }
}
3.4、核心部分编写排序规则

每一次鼠标拖拽Table的某一行进行排序都会执行onEnd()方法。

newIndex:行数据移动到的新位置,起始角标为0。
oldIndex: 行数据原始的位置。

举个例子:将第四行移动到第一行,执行的顺序如下:
1、首先根据取得oldIndex=3,取得第四列的id
2、将第四列的Id插入newIndex=0的位置,而后其余的数据一次后排。
3、id的变化流程为 [1, 2, 3, 4] -> [4, 1, 2, 3]

    // 更新排序
    initSortableList(){
      let el = this.$refs.tableRef.$el.querySelector('.el-table__body-wrapper tbody');
      //设置配置
      let _this = this
      Sortable .create(el, {
        animation: 150,
        sort: true,
        draggable: '.el-table__row', // 设置可拖拽行的类名(el-table自带的类名)
        forceFallback: true,
        onEnd({newIndex, oldIndex}) {
          let currRow = _this.newIndexList.splice(oldIndex, 1)[0];
          _this.newIndexList.splice(newIndex, 0, currRow);
        }
      })
    },
3.5、保存设置参数

执行保存操作时,我们需要将排序没有改变的行数据去除掉。以上截图为例; 显然此次排序每一行的顺序都发生了变化,所以需要修改。

tableData.id          newIndexList
     1                             4
     2                             3
     3                             1
     4                             2

基于上面数据分布格式,我们可以明白,以newIndexList为条件修改成tableData.id的值。

[
  {
    "key": 4,
    "value": 1
  },{
    "key": 3,
    "value": 2
  },{
    "key": 1,
    "value": 3
  },{
    "key": 2,
    "value": 4
  }
]

如果字段id都是匹配,则说明没有进行位置调整,则不需要提交。

    //保存编辑完顺序后的数组
    save(){
      //1、封装需要更新的id  <key, value> key: 原来id, value:新的id
      // 等位对比,查看templateList和newIndexList每一项ID是否对应。
      let sortList = [];
      this.tableData.forEach( (item, index) => {
        if(item.id !== this.newIndexList[index]){
          sortList.push({
            key: this.newIndexList[index],
            value: item.id + 10000
          });
        }
      });
      //2、如果循序没有改变,则执行退出
      if(!sortList.length){
        return;
      }
      //3、tableData数据顺序发生变化,则提交到数据库。
    },

也许你会发现item.id + 10000这个有意思的地方,因为我们在更新排序时,修改的是主键,所以会存在主键冲突,所以先增加10000,修改完成后根据已经修改的id在执行自减10000操作,这样就可以实现主键ID的交换了。

vue代码如下:

 <template>
  <div class="index">
    <el-row>
      <el-col>
        <el-table :data="tableData" ref="tableRef"
                  size="small" border stripe>
          <el-table-column align="center" label="模板ID" prop="id"></el-table-column>
          <el-table-column align="center" label="模板信息" prop="mc"></el-table-column>
        </el-table>
      </el-col>
    </el-row>

    <el-row style="margin-top: 20px">
      <el-col :offset="17" :span="6">
        <el-button-group>
          <el-button size="small" type="primary" @click="save">保存</el-button>
        </el-button-group>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import Sortable from 'sortablejs';

export default {
  name: "index",
  components: {
    Sortable
  },
  data() {
    return {
      // 表单数据
      tableData: [
        {
          id: 1,
          mc: "模板一"
        },{
          id: 2,
          mc: "模板二"
        },{
          id: 3,
          mc: "模板三"
        },{
          id: 4,
          mc: "模板四"
        }],
      // 排序后的数据列表
      newIndexList: [],
    }
  },
  mounted() {
    // 复制原Table的id按循序存储newIndexList中,
    // 每一次调整位置会对newIndexList进行位置调整。
    this.tableData.forEach( item => {
      this.newIndexList.push(item.id);
    });
    //阻止火狐拖拽新建新页面
    document.body.addEventListener("drop", (event) => {
      event.preventDefault();
      event.stopPropagation();
    }, false);
    this.initSortableList();
  },
  methods: {
    // 更新排序
    initSortableList(){
      let el = this.$refs.tableRef.$el.querySelector('.el-table__body-wrapper tbody');
      //设置配置
      let _this = this
      Sortable .create(el, {
        animation: 150,
        sort: true,
        draggable: '.el-table__row', // 设置可拖拽行的类名(el-table自带的类名)
        forceFallback: true,
        onEnd({newIndex, oldIndex}) {
          let currRow = _this.newIndexList.splice(oldIndex, 1)[0];
          _this.newIndexList.splice(newIndex, 0, currRow);
        }
      })
    },

    //保存编辑完顺序后的数组
    save(){
      //1、封装需要更新的id  <key, value> key: 原来id, value:新的id
      // 等位对比,查看templateList和newIndexList每一项ID是否对应。
      let sortList = [];
      this.tableData.forEach( (item, index) => {
        if(item.id !== this.newIndexList[index]){
          sortList.push({
            key: this.newIndexList[index],
            value: item.id + 10000
          });
        }
      });
      //2、如果循序没有改变,则执行退出
      if(!sortList.length){
        return;
      }
      //3、tableData数据顺序发生变化,则提交到数据库。
    },
  },
}
</script>
<style scoped>
</style>

4、如何保存数据库呢

4.1、假设数据表结构如下:
CREATE TABLE demo (
	id BIGINT PRIMARY KEY auto_increment COMMENT '模板id序',
    mc VARCHAR ( 100 ) COMMENT '模板名称' 
);
4.2、使用MyBatis实现数据表ID的更新

使用的MyBatis框架,通过动态SQL实现功能。
这里需要分两步走:
第一步:将排序后的id 加上 10000并修改原来的 id。注意:这个10000的一定是你的表数据自增无法达到的数据才可以,否则会出现主键冲突(vue前端已经实现自增了,这里无需任何操作)。

UPDATE DEMO
        SET ID =
        <foreach collection="list" item="item" open="(CASE" close="END)">
            WHEN ID = #{item.key} THEN #{item.value}
        </foreach>
        WHERE ID IN
        <foreach collection="list" item="item" open="(" close=")" separator=",">
            #{item.key}
        </foreach>

第二步:将修改的ID主键数值减10000

UPDATE DEMO
        SET ID =
        <foreach collection="list" item="item" open="(CASE" close="END)">
            WHEN ID = #{item.value} THEN #{item.value} - 10000
        </foreach>
        WHERE ID IN
        <foreach collection="list" item="item" open="(" close=")" separator=",">
            #{item.value}
        </foreach>

这样便捷的排序就实现了。

希望文章能对您所有帮助

回复

我来回复
  • 暂无回复内容