element-ui Table 合并行或列的技巧

1,213 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

element-ui 的 Table 组件有一个合并行或列的功能,官方分别给出了合并行和列的示例,但不够清晰明确,初看之下并不能很好的理解和运用,而且官方示例只做了一个简单展示,要是遇到更多层次的合并,得有一番费时的磋磨才能折腾出来。

那么,如何将后端接口返回的数据进行合并行或列呢?

下面是一个完成的效果,我们会对一级菜单做对应的行合并,二级菜单做行合并,这样在表格内做出一个整洁的层级结构。

image.png

按照上面的效果图,后端接口会返回一个三层的树形结构,我们会对这个数据进行处理,来达到要求。

需要注意的是,树形结构的子级字段不能是 children,不然 Table 组件会自动渲染成树形表格。

数据结构

后端返回以下数据结构,一个典型的三级树形结构

[
    {
        "id":"160502903066808320",
        "parentId":"",
        "name":"一级菜单",
        "parentName":null,
        "type": [
            {
                "id":"160502903075196928",
                "parentId":"160502903066808320",
                "name":"二级菜单",
                "parentName":"一级菜单",
                "sortList":[]
            },
            {
                "id":"375627622619828224",
                "parentId":"160502903066808320",
                "name":"二级菜单",
                "parentName":"一级菜单",
                "sortList":[
                {
                    "id":"400237691110522880",
                    "parentId":"375627622619828224",
                    "name":"三级菜单",
                    "parentName":"二级菜单"
                },
            ]
        ]
    }
    // ...
]

span-method

要想解决问题,不要急于立马就想得到一个能用的代码,静下心来,一步一步来。

先来理解一下 span-method 函数的使用。

该函数的参数是一个对象,里面包含四个属性:

  • row:当前行的数据
  • column:当前列
  • rowIndex: 表格的行索引,不包括表头,从0开始
  • columnIndex:表格的列索引,从0开始
spanMethod ({ row, column, rowIndex, columnIndex }) {
  // ...
}

最后我们会通过该函数来决定哪些行是需要进行合并的。

思路

对于上面的结果示意图中,我们其实要合并的是表格第一列和第二列的行。

在拿到后端接口数据之后,我们会调用 formatTable 函数对数据进行遍历处理,一个基本的思路是:

  1. 创建变量用于保存需要合并的行数和新的表格数据。
  2. 遍历接口数据,优先统计二级级菜单下的三级菜单的数量,该数量就是二级菜单需要合并的行数。
  3. 遍历到所有二级菜单的最后一个三级菜单时,保存一个行数,该行数就是一级菜单需要合并的行数。
  4. 给每一条新的表格数据添加 nameIndex 和 typeIndex 属性来控制是否需要合并行。
  5. 在 span-method 函数中处理行的合并。

下面直接来看代码。

// 格式表格数据,获取 nameIndex 和 typeIndex
formatTable() {
  let newDate = [] // 存储处理后的表格数据
  let nameIndex = [] // 保存第一列需要合并的行数
  let typeIndex = [] // 保存第二列需要合并的行数
  let total // 用于统计第一列需要合并的行数
  
  // 遍历接口数据
  tableData.forEach((v, index, list) => {
        if (v.type && v.type.length) {
          // 先初始化统计行数
          total = 0
          v.type.forEach((subV, i, typeData) => {
            // 先统计三级菜单的数量,因为三级菜单的数量有多少,对应的二级菜单就需要合并多少行
            if (subV.sortList && subV.sortList.length) {
              subV.sortList.forEach((ss, k, data) => {
                // 判断是否是最后一个三级菜单 
                if (k === data.length - 1) {
                  typeIndex.push(data.length) // 把每一个二级菜单下的三级菜单数量保存起来
                  total += data.length // 一级菜单需要合并的行数等于对应的二级菜单下的三级的所有总和
                }
                // 新的数据
                newDate.push({
                  id: ss.id || subV.id || v.id,
                  parentName: v.name,
                  name: subV.name,
                  sortName: ss.name
                })
              })
            } else {
               // 如果没有二级菜单,我们需要填充一个空数据,以保持表格列的对齐,此时 total 和 tyoeIndex 要加 1
              // 再加上二级菜单的数量,获取到一级菜单该合并的行数, 二级菜单的行合并也是同理
              total += 1
              typeIndex.push(1)
              getDate.push({
                id: subV.id,
                parentName: subV.parentName,
                name: subV.name,
                sortName: '无'
              })
            }
             // 循环完成后把 total 保存起来
            if (i === typeData.length - 1) {
              nameIndex.push(a)
            }
          })
        }
      })
      
      // 然后给 newData 的数据添加 nameIndex 和 typeIndex 属性来控制是否合并
      let k = 0
      let t = 0
      // 对于第一列的合并,并不是每一条数据都需要设置 nameIndex,比如第一条数据的nameIndex等于10,也就是需要合并10行,,那么下一个需要设置nameIndex的数据的索引是10,然后重新设置索引,因为数量是经过上面计算的,所以不用担心会将nameIndex设置给错误的数据,下面的typeIndex同理。
      nameIndex.forEach((v, i, nameArr) => {
        if (nameArr[i]) {
          getDate[k].nameIndex = nameArr[i]
          k += nameArr[i]
        }
      })

      typeIndex.forEach((v, i, typeArr) => {
        if (typeArr[i]) {
          getDate[t].typeIndex = typeArr[i]
          t += typeArr[i]
        }
      })
      // 最后重新赋值给tableData
      this.tableData = getDate
}

接着编写 spanMethod 函数来控制哪些行是需要合并的。

spanMethod ({ row, column, rowIndex, columnIndex }) {
  // 对第一列做行合并
  if (columnIndex === 0) {
    if (row.nameIndex) { // 如果有值,说明需要合并
      return [row.nameIndex, 1]
    } else return [0, 0]
  }
  // 对第一列做行合并
  if (columnIndex === 1) {
    if (row.typeIndex) {
      return [row.typeIndex, 1]
    } else return [0, 0]
  }
}

至此,我们就完成了行的合并,其实对于列的合并也是同理,大家可以自己上手操作试试。