表格通过合并单元格的方式,展示树形结构

2 阅读1分钟

Snipaste_2026-03-23_16-22-56.png 效果图

      {
        name: '河南省',
        children: [
          {
            name: '郑州市'
          },
          {
            name: '商丘市'
          },
          {
            name: '信阳市',
            children: [
              {
                name: '淮滨县',
                children: [
                  {
                    name: '王家岗乡'
                  }
                ]
              },
              {
                name: '固始县'
              }
            ]
          }
        ]
      },
      {
        name: '安徽省',
        children: [
          {
            name: '阜阳市',
          }
        ]
      },
      {
        name: '上海市'
      }
    ]

首先先获取数据的最长节点,用来判断合并单元格时应该合并几列

function getMaxNodeCount(tree, currentCount = 0) {
  if (!tree || tree.length === 0) return currentCount;

  let maxCount = 0;

  for (const node of tree) {
    // 当前节点算一个节点
    const currentPathCount = currentCount + 1;

    // 如果有子节点,继续递归
    if (node.children && node.children.length > 0) {
      const childMax = getMaxNodeCount(node.children, currentPathCount);
      maxCount = Math.max(maxCount, childMax);
    } else {
      // 叶子节点,记录当前路径的节点数
      maxCount = Math.max(maxCount, currentPathCount);
    }
  }

  return maxCount;
}

const maxLength = getMaxNodeCount(treeData);
console.log("最大深度(节点数):", maxLength);

判断每个单元格应该合并几列

const configColSpan = (arr, index = 0) => {
  arr.forEach(item => {
    if (!item.children || !item.children.length) {
      item.colSpan = maxLength - index
    } else {
      item.colSpan = 1
      configColSpan(item.children, index + 1)
    }
  })
}
configColSpan(treeData)

接着判断每个单元格应该合并几行

const configRowSpan = (nodes) => {
  var rowSpan = 0;
  for (let n = 0; n < nodes.length; n++) {
    if (nodes[n].children && nodes[n].children.length > 0) {
      nodes[n].rowSpan = configRowSpan(nodes[n].children);
    } else {
      nodes[n].rowSpan = 1;
    }
    rowSpan += nodes[n].rowSpan;
    nodes[n].index = n
  }
  return rowSpan;
}
configRowSpan(treeData)

最后将数据转换为二维数组

//处理树形结构为二维数组
let list = [[]]
const treeToArr = (tree, arr) => {
  tree.forEach(item => {
    arr[arr.length - 1].push(item)
    if (!item.children || item.children.length == 0) {
      arr.push([])
    } else {
      treeToArr(item.children, arr);
    }
  })
}

treeToArr(treeData, list);
const tableData = ref(list)

模板代码

      <tr v-for="(item, index) in tableData">
        <template v-for="(arr, i) in item" :key="i">
          <td :colspan="arr.colSpan" :rowspan="arr.rowSpan">{{ arr.name }}</td>
        </template>
      </tr>
    </table>