1. 程式人生 > 實用技巧 >vue中樹狀結構轉行資料,並渲染成table的方法

vue中樹狀結構轉行資料,並渲染成table的方法

場景: 我們現在有一個樹狀結構的資料,如下圖:

大概的資料結構如下:

const tree = {
	value: '根節點',
    children: [
      {
        value: '學校',
        children: [
          {
            value: '學生',
            children: [
              {
                value: '年齡',
                children: [
                  {
                    value: '身高'
                  }
                ]
              }
            ]
          }
          ......
        ]
      }
    ]
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

現在我們要將這樣的資料轉為行資料, 並用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' }],
	......
];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
第二步,新增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 }],
	......
];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

但是可以發現,我們需要的合併資訊解析到了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 }],
	......
];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

大功告成!最後的效果如下: