• element UI树形表格拖动创建子级


    呜呜呜,手上项目最新的需求要求写一个树形图表格,这就算了,还要拖动,单行的表格拖动也不复杂,网上一搜就是很多,用sortablejs轻松搞定。然而树形的表格,拖动的时候就不那么容易了,并且平常拖动基本上是交换顺序的作用,我们的需求不一样,是将一条数据拖到另一条数据下面变成子级,折腾了我半天,于是又来这里哭诉了,哈哈哈。

    言归正传,开始编写基于elementUI的拖拽表格吧。好在element 已经做好了树形表格,我只需要加上拖动功能,拖动还是使用的sortablejs,主要就是计算拖动后的数据比较麻烦。

    直接看代码吧:

    html 部分:

    <el-table
          :data="tableData"
          style=" 100%;margin-bottom: 20px;"
          :row-key="rowKey"
          border
          ref="treeTable"
          :expand-row-keys="expandRow"
          @cell-mouse-enter.once='rowDrop'
          :tree-props="treeProps">
          <slot></slot>
        </el-table>
    

     js部分:

    js部分我写了几个函数,其中:

      getExpandRow函数是遍历层级,根据设置的默认展开到第几层,拿到符合条件的id数组,表格根据这个数组默认展开几级。

      getDealData函数也是遍历树形结构,处理数据,将树形结构平铺,一条一条的存入数组,这是因为element 的树形表格拖动时是一行一行的拖动的,用sortablejs拖动拿到的也是每一行的下标,这样存入数组了之后,就能通过下标拿到拖动的这一行的数据和即将放入的那一行的数据,并且存的时候我将children也存入了的,这样根据下标拿到的数据就是当前节点和下面的所有子节点。就可以将其整个的移动到目标节点下面。

      rowDrop是初始化拖动表格的函数,调用时没有在mouted里调用,是为了避免在表格还没有渲染好就去调用,这样会报错,放在表格的cell-mouse-enter.once事件里,并用once修饰符,执行一次,就可以了。rowDrop里对树形数据进行了处理,处理前都去执行了getDealData,是因为每次拖动了,表格都会重新排序。处理树形数据的时候需要依据平铺的数组。

      重点是changeData函数,它是rowDrop的具体实施,主要考虑到几个方面:

        1.父级不能移动到子级下面;

        2.子级已经在当前的父级下面了,则不需要再插入,否则会重复;

        3.因为只想遍历一次数据,所以在遍历的时候同时做了删除拖动的节点,并将这个节点移动到目标节点下面;

        4.最后重新渲染表格时,我最开始用了doLayout,发现没有生效,界面上树形的折叠按钮显示缩进不对,用$nextTick()也还是不对,最后决定就还是先清空数组,然后再赋值,这样表格就重新渲染了。

    import Sortable from 'sortablejs'
    export default {
      props: {
        data: {
          type: Array,
          default: () => []
        },
        treeProps: {
          type: Object,
          default: () => {
            return { children: 'children', hasChildren: 'hasChildren' }
          }
        },
        rowKey: {
          type: String,
          default: 'id'
        },
        expandLevel: {
          type: Number,
          default: 3
        }
      },
      watch: {
        data: {
          handler: function(val) {
            this.tableData = JSON.parse(JSON.stringify(val))
            this.getExpandRow(val)
          },
          deep: true,
          immediate: true
        }
      },
      data() {
        return {
          selectObj: {},
          tableData: [],
          flattArray: [],
          expandRow: []
        }
      },
      mounted() {
        this.getDealData()
      },
      methods: {
        getExpandRow(data) {
          const result = []
          const children = this.treeProps.children
          const rowKey = this.rowKey
          let level = 0
          // 默认展开三级
          const func = (arr, parent) => {
            arr.forEach(item => {
              if (item[children] && item[children].length !== 0) {
                if (level < this.expandLevel) {
                  result.push(item[rowKey] + '')
                }
                level++
                func(item[children], item)
              }
            })
          }
          func(data)
          this.expandRow = result
        },
        getDealData() {
          const result = []
          const children = this.treeProps.children
          const rowKey = this.rowKey
          const func = function(arr, parent) {
            arr.forEach(item => {
              const obj = Object.assign(item)
              if (parent) {
                if (obj.parentIds) {
                  obj.parentIds.push(parent[rowKey])
                } else {
                  obj.parentIds = [parent[rowKey]]
                }
              }
              // 将每一级的数据都一一装入result,不需要删除下面的children,方便拖动的时候根据下标直接拿到整个数据,包括当前节点的子节点
              result.push(obj)
              if (item[children] && item[children].length !== 0) {
                func(item[children], item)
              }
            })
          }
          func(this.tableData)
          this.flattArray = result
        },
        rowDrop() {
          const tbody = document.querySelector('.el-table__body-wrapper tbody')
          const self = this
          Sortable.create(tbody, {
            onEnd({ newIndex, oldIndex }) {
              self.getDealData()
              const sourceObj = self.flattArray[oldIndex]
              const targetObj = self.flattArray[newIndex]
              // 改变要显示的树形数据
              self.changeData(sourceObj, targetObj)
            }
          })
        },
        changeData(sourceObj, targetObj) {
          let flag = 0
          const data = Object.assign(this.tableData)
          const children = this.treeProps.children
          const rowKey = this.rowKey
          const func = function(arr, parent) {
            for (let i = arr.length - 1; i >= 0; i--) {
              const item = arr[i]
              // 判断是否是原来拖动的节点,如果是,则删除
              if (item[rowKey] === sourceObj[rowKey] && (!parent || parent && parent[rowKey] !== targetObj[rowKey])) {
                arr.splice(i, 1)
                flag++
              }
              // 判断是否是需要插入的节点,如果是,则装入数据
              if (item[rowKey] === targetObj[rowKey]) {
                if (item[children]) {
                  // 判断源数据是否已经是在目标节点下面的子节点,如果是则不移动了
                  let repeat = false
                  item[children].forEach(e => {
                    if (e[rowKey] === sourceObj[rowKey]) {
                      repeat = true
                    }
                  })
                  if (!repeat) {
                    sourceObj.parentIds = []
                    item[children].unshift(sourceObj)
                  }
                } else {
                  sourceObj.parentIds = []
                  item[children] = [sourceObj]
                }
                flag++
              }
              // 判断是否需要循环下一级,如果需要则进入下一级
              if (flag !== 2 && item[children] && item[children].length !== 0) {
                func(item[children], item)
              } else if (flag === 2) {
                break
              }
            }
          }
          // 检测是否是将父级拖到子级下面,如果是则数据不变,界面重新回到原数据
          if (targetObj.parentIds) {
            if (targetObj.parentIds.findIndex(_ => _ === sourceObj[this.rowKey]) === -1) {
              func(data)
            } else {
              this.$message.error('不能将父级拖到子级下面')
            }
          } else {
            func(data)
          }
          this.$set(this, 'tableData', [])
          // 重新渲染表格,用doLayout不生效,所以重新装了一遍
          this.$nextTick(() => {
            this.$set(this, 'tableData', data)
          })
        }
      }
    }

  • 相关阅读:
    提取多层嵌套Json数据
    Jquery 使用Ajax获取后台返回的Json数据后,页面处理
    Jquery购物车jsorder改进版,支持后台处理程序直接转换成DataTable处理
    长连接和短连接
    JAVA8新特性
    线程、进程与程序+并行与并发
    订单号生成规则
    散列表解决冲突的方式
    125.Valid Palindrome
    128.Longest Consecutive Sequence
  • 原文地址:https://www.cnblogs.com/liuqin-always/p/14103450.html
Copyright © 2020-2023  润新知