SortTable实现el-table表格拖拽排序,以及实现拖拽排序算法

原创  郑建华   2021-01-22   158人阅读  2 条评论

    最近又开始写前端了,所以碰到问题是难免的,不过有问题就解决问题,这才是程序员的生存之道。闲话少说,这次是el-table的拖拽排序问题。需要实现如下需求,现有一个表格,每行数据都能进行拖拽,进行上下移动,比如,将图中第二行的排序字段拖拽到第四行,同时原本的第三行第四行数据向上平移。

image.png

    这样的UI效果是可以有组件支持的,那就是SortTable.js。别人造好的轮子,直接拿来用就行了。那么问题在哪里呢?在vue中使用sortTable组件拖拽表格行数据后,影响的只是页面展示效果,实际的数据并没有发生变化,还是原来的顺序。vue是基于数据驱动的,做一些交互操作都是直接修改数据,页面直接渲染的,比如当前页面的字段编码下拉框的选择,这里如果值改变,同时要更改本行的全部数据。所以,如果拖拽后不影响实际的数据,那其他相关的数据交互操作就会出现问题。

 解决思路:

    1、先缓存一个与实际数据一致的数组。

    2、在拖拽时,获取到原下标和拖拽后的下标,根据等同页面展示效果的拖拽算法,修改缓存的数组的顺序。

    3、根据缓存的数组,重新渲染表格数据,保证页面效果与实际数据一致

代码实现:

1、实现等同页面效果的拖拽排序方法,利用新数组实现修改数组元素顺序的功能

/**
 *  根据下标重新获取拖拽排序数组
 *  @param oldIndex 从下标拖拽
 *  @param newIndex 拖拽到的下标
 *  @param oldList 拖拽的数组
 *  @return [] 拖拽后的数组
 *  例如:
 *   1->3
 *  下标值 0 1 2 3 4 5
 *  原数组 a b c d e f
 *  新数组 a c d b e f
 *  newList[5] = oldList[5]
 *  newList[4] = oldList[4]
 *  newList[3] = oldList[1]
 *  newList[2] = oldList[3]
 *  newList[1] = oldList[2]
 *  newList[0] = oldList[0]
 *
 *  3->1
 *  下标值 0 1 2 3 4 5
 *  原数组 a b c d e f
 *  新数组 a d b c e f
 *  newList[5] = oldList[5]
 *  newList[4] = oldList[4]
 *  newList[3] = oldList[2]
 *  newList[2] = oldList[1]
 *  newList[1] = oldList[3]
 *  newList[0] = oldList[0]
 */
getOrderList(oldIndex, newIndex, oldList) {
  // 新数据集合
  const newList = []
  // 第一种情况 oldIndex < newIndex 1->3
  if (oldIndex < newIndex) {
    for (let i = 0; i < oldList.length; i++) {
      // 超过移动下标范围的 数据(小于第一个下标或者大于最后一个下标) 直接赋值
      if (i < oldIndex || i > newIndex) {
        newList[i] = oldList[i]
      } else {
        // 当前下标 小于 最后一个下标时
        if (i < newIndex) {
          // 取下标向后偏移1的数据
          newList[i] = oldList[i + 1]
          // 当前下标 等于 最后一个下标时
        } else {
          // 取第一个下标的数据
          newList[i] = oldList[oldIndex]
        }
      }
    }
    // 第二种情况 oldIndex > newIndex  3->1
  } else if (oldIndex > newIndex) {
    for (let i = 0; i < oldList.length; i++) {
      // 超过移动下标范围的 数据(大于第一个下标或者小于最后一个下标) 直接赋值
      if (i > oldIndex || i < newIndex) {
        newList[i] = oldList[i]
      } else {
        // 当前下标 等于 最后一个下标时
        if (i === newIndex) {
          // 取第一个下标的数据
          newList[i] = oldList[oldIndex]
          // 当前下标 大于 最后一个下标时
        } else if (i > newIndex) {
          // 取下标向前偏移1的数据
          newList[i] = oldList[i - 1]
        }
      }
    }
  // 第三种情况    oldIndex = newIndex  1->1
  } else {
    for (let i = 0; i < oldList.length; i++) {
      // 直接赋值
      newList[i] = oldList[i]
    }
  }
  return newList
}

2、初始化拖拽方法,在其中重新生成排序后的数组,并且重新渲染表格

// 拖拽初始化方法
rowDrop() {
  const tbody = document.querySelector('.fields-table tbody')
  const _this = this
  this.$nextTick(() => {
    Sortable.create(tbody, {
      handle: '.fields-move',
      animation: 150,
      onEnd({ oldIndex, newIndex }) {
        // 转换缓存数组顺序
        _this.backupsData = _this.getOrderList(oldIndex, newIndex, _this.backupsData)
        // 缓存数据数组 赋值给  实际数据数组
        _this.dataSetFieldList = JSON.parse(JSON.stringify(_this.backupsData))
        // 重新渲染表格(必须重新渲染,否则页面表格拖拽后,显示数据不正确)
        _this.randomKey = Math.random()
        _this.$forceUpdate()
      }
    })
  })
}

 3、由于重新渲染表格,导致拖拽的事件被移除,监听表格数据变化,重新渲染拖拽事件

watch: {
  /**
   * 监听表格数据变化,重新渲染拖拽事件
   */
  dataSetFieldList: {
    handler(val) {
      this.$nextTick(() => {
        // 绑定拖拽事件
        this.rowDrop()
      })
    },
    immediate: true
  }
},

遇到的问题:

1、缓存数据重新赋值,采用JSON转换方式,避免引用传值,防止出现数据混乱问题。

2、重新给实际数据赋值后,必须要重新渲染,否则页面显示的数据会不正确,即与实际数据中显示的顺序不一致。

3、重新渲染表格采用给table绑定key,赋值为随机数的方式,仅使用$forceUpdate()方法不能生效。

4、重新渲染表格会导致已绑定的拖拽事件被移除,表现为拖拽一次后就不能拖拽了,而直接在重新渲染表格后再次调用拖拽初始化方法,会存在数据还未渲染就进入了拖拽初始化方法,所以采用了监听数据变化的方法,并且在其中加入数据渲染完成后调用,保证数据渲染完成再进行拖拽初始化,重新绑定事件。



本文地址:https://www.zjh336.cn/?id=2022
版权声明:本文为原创文章,版权归 郑建华 所有,欢迎分享本文,转载请保留出处!

发表评论


表情

 评论列表

  1. etbs
    etbs 【实习生】  @回复

    空包代发、一单一用单号网www.kuaid100.cn

  2. 今日头条新闻
    今日头条新闻 【实习生】  @回复

    文章不错支持一下吧