vue2实现列表横向拖拽排序(列数据、列表头对应更新)

所需的库

  • sortablejs
  • element ui

具体实现效果

用户可以通过拖动首行列表头控制整列的排列顺序,并且存入本地。

遇到的坑

通过v-for生成列表,key值选取问题(解决拖拽时列数据和表头对应不上的问题)

这个问题在csdn上有很多解决方案,但大多都失败了(比如key取index,path等),其余的即使可以实现,也会影响到我项目的其他模块(本人用的若依框架开发),这里直接说我现在key的获取:通过全局js文件给表头分配一个key,每当表头更新的时候通过全局方法手动修改key值,这样就能触发vue的diff算法重新渲染列表,全局js文件代码如下:

  1. tableData.js
//Mes表格数据
const tableData = {
      prePartCol: [
        {
          label: '操作',
          key: 0,
        },
        {
          label: '预套料零件ID',
          prop: 'id',
          key: 1,
        },
        {
          label: '零件编号',
          prop: 'mat_code',
          key: 2,
        },
        {
          label: '预套料作业编码',
          prop: 'preTaskCode',
          key: 9,
        },
        {
          label: '零件类型',
          prop: 'iscircle',
          key: 3,
        },
        {
          label: '零件材质',
          prop: 'mat_quality',
          key: 4,
        },
        {
          label: '零件长度/外径',
          prop: 'length',
          key: 5,
        },
        {
          label: '零件宽度/内径',
          prop: 'width',
          key: 6,
        },
        {
          label: '零件厚度',
          prop: 'thick',
          key: 7,
        },
        {
          label: '数量',
          prop: 'qty',
          key: 8,
        },
      ],
      prePartList: [
        {
          label: '操作',
          key: 0,
        },
        {
          label: '预套料零件ID',
          prop: 'id',
          key: 1,
        },
        {
          label: '零件编号',
          prop: 'mat_code',
          key: 2,
        },
        {
          label: '零件类型',
          prop: 'iscircle',
          key: 3,
        },
        {
          label: '零件材质',
          prop: 'mat_quality',
          key: 4,
        },
        {
          label: '零件长度/外径',
          prop: 'length',
          key: 5,
        },
        {
          label: '零件宽度/内径',
          prop: 'width',
          key: 6,
        },
        {
          label: '零件厚度',
          prop: 'thick',
          key: 7,
        },
        {
          label: '数量',
          prop: 'qty',
          key: 8,
        },
        {
          label: '预套料作业编码',
          prop: 'preTaskCode',
          key: 9,
        },
      ],
   };
export default tableData;

这里仅列出一个表格所需的数据,其中prePartCol列表为表头的list,用于拖拽时改变的列,prePartList为原始list,用于储存列拖拽后的新list

  1. colDrag.js
import Sortable from "sortablejs";
// 列拖拽相关方法
export function columnDrop(dropCol) {
    const wrapperTr = document.querySelector('.el-table__header-wrapper tr')
    Sortable.create(wrapperTr, {
        animation: 180,
        delay: 0,
        ghostClass: 'sortable-ghost',
        onEnd: (evt) => {
            if(evt.newIndex == evt.oldIndex){
                //这一句,是判断相同时不移动,也是解决,列宽拖动的问题
                return dropCol;
            }
          const oldItem = dropCol[evt.oldIndex - 1]
          dropCol.splice(evt.oldIndex - 1, 1)
          dropCol.splice(evt.newIndex - 1, 0, oldItem)
        }
    })
    return dropCol;
}
  /** 判断表头是否更变顺序,若改变则修改对应的key值并更新原始数组 */
  export function changeList(originalList, currentList){
    for(let i = 0; i < originalList.length; i++){
      if(originalList[i].key !== currentList[i].key){
        currentList[i].key = currentList[i].key + 0.000000000000001;
        originalList[i] = currentList[i];
      }else{
        // currentList[i].key = currentList[i].key - 0.000000000000001;
        // originalList[i] = currentList[i];
      }
    }
  }
export default columnDrop;

该全局方法通过引入sortablejs实现了列拖拽方法和表头key值手动更新方法,逻辑不难,参考sortablejs官方文档即可

页面实现

el-table

<el-table v-loading="loading" :data="partList" @selection-change="handleSelectionChange" @sort-change="sortChange">
      <el-table-column type="selection" align="center" width="55" fixed="left" />
      <el-table-column v-for="(item, index) in dropCol" align="center" show-overflow-tooltip :key="item.key"
        :prop="dropCol[index].prop" :label="item.label" :min-width="item.minWidth" sortable="custom">
        <template v-slot="scope">
          <span v-if="dropCol[index].label === '操作'">
            <router-link :to="{ name: 'Task', params: { showButton: false, preTaskCode: scope.row.preTaskCode } }"
              class="link-type">
              <span>{{ '查看预套料作业' }}</span>
            </router-link>
          </span>
          <span v-if="dropCol[index].prop === 'iscircle'">
            <dict-tag :options="dict.type.is_circle" :value="scope.row.iscircle" />
          </span>
          <span v-else>
            {{ scope.row[dropCol[index].prop] }}
          </span>
        </template>
      </el-table-column>
    </el-table>

通过v-for渲染列表,partList数据绑定后端数据库,多选框列单独拎出来,用fixed="left"固定在左边,不然会影响列拖拽,dropCol为tableData.js中的prePartCol列表,列表中元素的顺序即为渲染出的默认顺序,若要对列表中某一属性做特殊处理可使用v-if选择

data

data() {
    return {
      // 预套料零件表格数据
      partList: [],
      dropCol: tableData.prePartCol,
      originalList: tableData.prePartList,
      },

mounted

  mounted() {
    this.dropCol = this.columnDrop(JSON.parse(localStorage.getItem('prePart'), 'prePart') || tableData.prePartCol);
    this.getList();
    console.log(this.dropCol);
  },

通过localStorage或tableData获取dropCol,优先localStorage的方式

watch

  watch: {
    dropCol: function () {
      //this.$forceUpdate()
      console.log("我被触发了")
      // console.log(this.dropCol.map(item => item.key))
      // console.log(this.dropCol.map(item => item.label))
      changeList(this.originalList, this.dropCol);
      console.log(this.originalList.map(item => item.key))
      localStorage.setItem('prePart', JSON.stringify(this.originalList));
    },

监听dropCol的变化,发生变化就调用全局方法changeList(),实现key值的手动更新和originalList列表更新,然后在将新的列表存入localStorage

总结一下

通过上述方法基本可以实现大部分列表的列拖拽,嵌套列表没试过不确定,是否有更好地实现方法暂时还没有发现,如果有欢迎各位大佬评论留言,上述代码也存在很多不足,主要是为了满足客户需求hhh

posted @ 2024-06-20 10:36  MayoiKaze  阅读(333)  评论(0)    收藏  举报