场景: 我们现在有一个树状结构的数据,如下图:
在这里插入图片描述
大概的数据结构如下:

const tree = {
	value: '根节点',
    children: [
      {
        value: '学校',
        children: [
          {
            value: '学生',
            children: [
              {
                value: '年龄',
                children: [
                  {
                    value: '身高'
                  }
                ]
              }
            ]
          }
          ......
        ]
      }
    ]
  }

现在我们要将这样的数据转为行数据, 并用vue将其渲染为table, 效果如下:
在这里插入图片描述

OK,下面我们一一进行解析。

第一步,递归树状结构,转化为行数据
parseTreeToRow(node, data = [], row = []) {
   if (!node.children) {
    data.push(row);
   } else {
     for (let i = 0; i < node.children.length; i++) {
       const child = node.children[i];
       const cell = { value: child.value };
       this.parseTreeToRow(child, data, [...row, cell]);
     }
   }
   return data;
};

//通过上面的递归函数,解析出来的行数据如下:
const data = [
	[{ value: '学校'}, { value: '学生' }, { value: '年龄' }, { value: '身高' }],
	[{ value: '成都一中'}, { value: '张三' }, { value: '17' }, { value: '170' }],
	[{ value: '成都二中'}, { value: '李四' }, { value: '17' }, { value: '174' }],
	[{ value: '成都二中'}, { value: '王五' }, { value: '18' }, { value: '168' }],
	[{ value: '成都二中'}, { value: '王五' }, { value: '19' }, { value: '177' }],
	......
];
第二步,添加rowspan信息

此时,很明显渲染出来的table并没有进行合并,如果对解析后的行数据二次解析亦可找到其rowspan,这里不做二次解析,我们回到上面的递归函数中,做如下处理:

parseTreeToRow(node, data = [], row = []) {
   if (!node.children) {
     data.push(row);
   } else {
     for (let i = 0; i < node.children.length; i++) {
       const child = node.children[i];
       const cell = {
       	value: child.value,
       	rowspan: this.computeLeafCount(node)
       };
       this.parseTreeToRow(child, data, [...row, cell]);
     }
   }
   return data;
};

/**
 1. 计算某个节点下叶子节点的数量
 2. @param { Object }    node        节点
 3. @returns { Number }  leafCount   叶子节点的数量
   */
  computeLeafCount(node) {
    if(!node.children){
      node.rowspan = 1;
      return 1;
    } else {
      let leafCount = 0;
      for(let i = 0 ; i < node.children.length ; i++) {
        leafCount = leafCount + this.computeLeafCount(node.children[i]);
      }
      node.rowspan = leafCount;
      return leafCount;
    }
  }
//解析出的数据如下:
const data = [
	[{ value: '学校', rowspan: 7 }, { value: '学生', rowspan: 1 }, { value: '年龄', rowspan: 1 }, { value: '身高', rowspan: 1 }],
	[{ value: '成都一中', rowspan: 7 }, { value: '张三', rowspan: 1 }, { value: '17', rowspan: 1 }, { value: '170', rowspan: 1 }],
	[{ value: '成都二中', rowspan: 7 }, { value: '李四', rowspan: 3 }, { value: '17', rowspan: 1 }, { value: '174', rowspan: 1 }],
	[{ value: '成都二中', rowspan: 7 }, { value: '王五', rowspan: 3 }, { value: '18', rowspan: 1 }, { value: '168', rowspan: 1 }],
	[{ value: '成都二中', rowspan: 7 }, { value: '王五', rowspan: 3 }, { value: '19', rowspan: 1 }, { value: '177', rowspan: 1 }],
	......
];

但是可以发现,我们需要的合并信息解析到了child中,且相同的单元格只有第一行的rowspan保留,其余要置为0,。

第三步,处理异常合并信息

回到递归函数中,

parseTreeToRow(node, data = [], row = []) {
   if (!node.children) {
     data.push(row);
   } else {
     for (let i = 0; i < node.children.length; i++) {
       const child = node.children[i];
       const cell = { value: child.value };
		/******************添加的代码******************/
		//深度克隆父亲,因为后代共用了该引用数据
       const extendRow = [ ...JSON.parse(JSON.stringify(row)), cell];
       if (extendRow.length === 1) {	//第一列
         extendRow[0].rowspan = 1;
       } else if (extendRow.length > 1) {
         //将该行的最后一列的rowspan赋给上一列
         //再将自身置为1(避免最后一列无值)
         extendRow[extendRow.length - 2].rowspan = i === 0 ? this.computeLeafCount(node) : 0;
         extendRow[extendRow.length - 1].rowspan = 1;
       }
       /******************添加的代码******************/
       this.parseTreeToRow(child, data, extendRow);
     }
   }
   return data;
};

//解析后的数据结构如下:
const data = [
	[{ value: '学校', rowspan: 1 }, { value: '学生', rowspan: 1 }, { value: '年龄', rowspan: 1 }, { value: '身高', rowspan: 1 }],
	[{ value: '成都一中', rowspan: 1 }, { value: '张三', rowspan: 1 }, { value: '17', rowspan: 1 }, { value: '170', rowspan: 1 }],
	[{ value: '成都二中', rowspan: 1 }, { value: '李四', rowspan: 3 }, { value: '17', rowspan: 1 }, { value: '174', rowspan: 1 }],
	[{ value: '成都二中', rowspan: 1 }, { value: '王五', rowspan: 0 }, { value: '18', rowspan: 1 }, { value: '168', rowspan: 1 }],
	[{ value: '成都二中', rowspan: 1 }, { value: '王五', rowspan: 0 }, { value: '19', rowspan: 1 }, { value: '177', rowspan: 1 }],
	......
];
<table>
  <tr v-for="(row, i) in data" :key="i">
     <td
       v-for="(cell, j)  in row"
       v-if="cell.rowspan"
       :key="j"
       :rowspan="cell.rowspan"
       :colspan="cell.colspan">
       <div class="cell">{{ cell.value }}</div>
     </td>
   </tr>
 </table>

大功告成!最后的效果如下:
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐