Vue复杂表格(动态组合表头,跨行)

先上效果图:javascript

效果图 若是只是须要这样的表头,那么iview就能够作到(项目使用的iview),可是遇到这种既须要复杂表头,又须要跨行的状况,查了一下,好多都说让改iview源码,。。。就想着本身写一个。html

渣渣的我来讲,哈,颇有成就感。但愿你们提出建议。vue

下面是具体思路:java

1.首先是对表头的数据进行处理。npm

使用了和iview Table columns属性一样的格式存值。数组

columns : [
          {                             
            title: '种类',
            key: 'type',       //这一列值所对应的key
            hasrowspan:true    // 在这里增长了一个属性,表示这一列是可能须要合并列的,后面会说
          },
          {
            title: '编制人数',
            key: 'amount',
          },
          {
            title: '参数',
            children: [             //children表示参数下有子标题,能够嵌套多个children
              {
                title: '大小',
                key: 'size',
                align: 'center',    
              },
              ........
            ],
          },
          .........
        ],

在处理columns时须要获得的数据有:app

  • 一个最大行数maxHeight:表头一共占用几行
  •  一个二维数组 newArr :从效果图中能够看到本例的thead一共须要两个tr。newArr[0],newArr[2]分别表明第一个和第二个tr所须要的值。
  • needRowSpan:哪些列须要进行跨行处理,保存须要跨行处理的key值
  • colKeyList:保存全部的key,(tbody中的数据是按照表头的顺序显示,它们使用key链接)

2.处理表中的数据,数据的格式很简单,其中key值就是上面column中所对应的key,它将决定将值展现在哪一列。iview

dataList: [
          {
            type: "图书",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "图书",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          .......
        ],

  在对这个数据进行处理时,要获得的数据有:ui

  • span:{} 格式以下,这是一个对象,表示哪一列的哪一行(对应td位置)rowspan的值(span中不存在时为1)
span:{
  date: {0: 5, 5: 5},                     //key为‘date’的列,第一个td须要跨5行,第六td须要跨5行
  type: {0: 2, 2: 2, 4: 3, 7: 3}
}
  • 在dataList每一个对象中加入一个属性tdList,用来存放这一行所须要的key。缘由:

  若是某一行有一列(td)跨了两行,那么它的下一行就要少一个tdthis

     那么在这里就用少一个key来实现少一个td。

     直接上完整代码:

<!DOCTYPE html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>多表头表格</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <table class="table">
        <thead>
        <tr v-for="items in newArr">
            <th v-for="item in items" :rowspan="item.rowspan" :colspan="item.colspan">{{item.title}}</th>
        </tr>
        </thead>
        <tbody v-if="needRowSpan.length>0">
            <tr v-for="(items,index) in dataList">
                <td v-for="item in items.tdList" :rowspan="resetRowSpan(index,item)">{{items[item]}}</td>
            </tr>
        </tbody>
        <tbody v-else>
        <tr v-for="(items,index) in dataList">
            <td v-for="item in colKeyList">{{items[item]}}</td>
        </tr>
        </tbody>
    </table>
</div>

</body>
<script>
  var app = new Vue({
    el: '#app',
    data(){
      return{
        columns : [
          {
            title: '种类',
            key: 'type',
            hasrowspan:true
          },
          {
            title: '编制人数',
            key: 'amount',
          },
          {
            title: '日期',
            key: 'date',
            hasrowspan:true
          },
          {
            title: '参数',
            children: [
              {
                title: '大小',
                key: 'size',
                align: 'center',
              },
              {
                title: '参数1',
                key: 'param1',
                align: 'center',
              },
              {
                title: '参数2',
                key: 'param2',
                align: 'center',
              },
              {
                title: '参数3',
                key: 'param3',
                align: 'center',
              },
            ],
          },
          {
            title: '数据',
            align: 'center',
            children: [
              {
                title: '数据1',
                key: 'infor1',
                align: 'center',
              },
              {
                title: '数据2',
                key: 'infor2',
                align: 'center',
              },
              {
                title: '数据3',
                key: 'infor3',
                align: 'center',
              },
              {
                title: '数据4',
                key: 'infor4',
                align: 'center',
              },
            ],
          },
        ],    //表头原始参数
        newArr:[[]],        //表头
        maxHeight:1,         //表头总共占的行数
        colKeyList:[],        //全部的key
        dataList: [
          {
            type: "图书",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "图书",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "电子书",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "电子书",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "化妆品",
            date: "2018-11-26",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "化妆品",
            date: "2018-11-27",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "化妆品",
            date: "2018-11-27",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "水果",
            date: "2018-11-27",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "水果",
            date: "2018-11-27",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
          {
            type: "水果",
            date: "2018-11-27",
            amount: 0,
            size: 0,
            param1: 0,
            param2: 0,
            param3: 0,
            infor1: 0,
            infor2: 0,
            infor3: 0,
            infor4: 0,
          },
        ],      //tbody具体数据
        needRowSpan:[],        //tbody须要跨行的key
        span:{}                 //所跨的行数
      }
    },
    mounted(){
      this.maxHeight=this.getMaxFloor(this.columns);       //1. 计算出表头一共须要多少行
      this.columnsHandle(this.columns);                       //2. 对表头进行处理
      this.dataHandle(this.dataList,this.needRowSpan);         // 3. 对数据进行处理(传入参数: 具体数据,须要跨行列的(key))
    },
    methods: {
      resetRowSpan(row, key) {
        if (this.span[key] && this.span[key][row]) {
          return this.span[key][row];
        }
        return 1;
      },
      gerMaxCol(items) {
        let max = 0;
        function each(data) {
          if (max < data.length) {
            max = data.length;
          }
          data.forEach((item) => {
            if (item.children) {
              each(item.children);
            }
          });
        }
        each(items);
        return max;
      },
      getMaxFloor(treeData) {
        const that = this;
        let max = 0;
        function each(data, floor) {
          data.forEach((e) => {
            if (floor > max) {
              max = floor;
            }
            if (e.children && e.children.length > 0) {
              each(e.children, floor + 1);
          }
          });
        }
        each(treeData, 1);
        return max;
      },
      columnsHandle(treeData) {
        const that = this;
        const maxFloor = this.maxHeight;
        const keyList = [];
        function each(data, index) {
          if (that.newArr[index] === undefined) {
            that.newArr[index] = [];
          }
          data.forEach((e) => {
            const obj = {
              title: e.title,
              key: e.key,
              rowspan: maxFloor,
              colspan: 1,
            };
            if (e.children) {
              obj.colspan = that.gerMaxCol(e.children);
              obj.rowspan = maxFloor - that.getMaxFloor(e.children);
            } else {
              that.colKeyList.push(e.key);
              if (e.hasrowspan) {             //  若是存在hasrowspan属性而且值为true,则代表该key列存在跨行
                that.needRowSpan.push(e.key);
              }
            }
            that.newArr[index].push(obj);
            if (e.children && e.children.length > 0) {
              each(e.children, index + 1);
            }
          });
        }
        each(treeData, 0);
      },
      dataHandle(dataList, needRowSpan) {
        needRowSpan.forEach((key) => {
          const sum = {};
          let i = 0; let k = 0;
          const that = this;
          for (let j = 0; j < dataList.length; j += 1) {
            i += 1;
            let tdList = [];
            if (dataList[j].tdList) {
              tdList = [...dataList[j].tdList];
            } else {
              tdList = [...that.colKeyList];
            }
            if (dataList[j - 1] && (dataList[j][key] === dataList[j - 1][key] || !dataList[j][key])) {
              const index = tdList.indexOf(key);
              if (index > -1) {
                tdList.splice(index, 1);
              }
            }
            dataList[j].tdList = tdList;
            if (dataList[j + 1] && dataList[j + 1][key]) {
              if (dataList[j][key] !== dataList[j + 1][key]) {
                sum[k] = i;
                i = 0; k = j + 1;
              }
            } else if (!dataList[j + 1]) {
              sum[k] = i;
            }
          }
          this.span[key] = sum;
        });
        console.log(this.span);
        this.showTable = true;
      },
    }
  });

</script>
    <style>
        #app .table {
            width:100%;
            border-collapse:collapse;
            font-size:12px;
            color: #515a6e;
            border:1px solid  #515a6e;
        }
        .table thead tr th{
            height: 40px;
            white-space: nowrap;
            overflow: hidden;
            background-color: #f8f8f9;
        }
         td, th
        {
             text-align:center;
            border:1px solid  #e8eaec;
        }
         /*tr:hover{*/
           /*background:#EBF7FF;*/
         /*}*/
        .table thead tr th ,.table tbody tr td{
            padding:0 10px;
        }
        .table tbody tr td{
            height:48px;
        }
    </style>
</html>