ElementUI 嵌套表格 + 单元格合并 + 每次展开一行

话不多说, 直接上效果图

  • 这里是未展开的表格:


    image.png
  • 展开之后:


    image.png

    image.png

完整的Demo代码在最下方

拆分解析:

1. 从最简单的合并表头开始

image.png

这里因为没有从官方文档找到合适的方法, 我利用css样式来实现
Dom部分代码:

  • 第一个单元格需要加上label-class-name属性, 官方文档 的解释是: 当前列标题的自定义类名

  • 第二个需要合并的表头单元格不要加label

  <el-table-column prop="stageGroup" label="阶段" align="center" header-align="right"
    label-class-name="merge_thead">
  </el-table-column>
  <el-table-column prop="stageName" align="center">
  </el-table-column>
  • 接下来只需要把第一个单元格的内容挪到两个单元格中间就行了

css样式:

    .mergeThead {
      overflow: visible !important;
      border-right: none !important;
    }

    .mergeThead .cell {
      position: absolute;
      z-index: 1;
      margin-right: -25px;
    }

2. 嵌套表格

elementUI table的折叠行功能只需要在航煤添加 type="expand" 属性即可, 默认显示为一个箭头, 如果有可以自定义显示内容的方法可以告诉我

<el-table-column type="expand">

我这里展开的内容是另外一个表格, 也就是嵌套表格, 其实很简单

      <el-table-column type="expand">
        <template>
          <el-table :data="data">
          </el-table>
        </template>
      </el-table-column>

3. 每次只允许一行展开

展开一个表格就超过一屏了, 多个占个会影响操作, 所以这里我只让他展开一行

elementUI 默认不限制展开的行数, 如果要设置需要展开的行, 要用到官方提供的
expand-row-keys 属性: 可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
row-key属性:为Function时, 可以获取当前行内数据(这里没有引用官方的解释)
expand-change方法: 当用户对某一行展开或者关闭的时候会触发该事件

<el-table stripe border :data="outDataList" align="center" tooltip-effect="dark" style="width: 100%"
      @expand-change="queryData" :expand-row-keys="expands" :row-key='getRowKeys'>
</el-table>

<script>
    data: {
      expands: [],
      getRowKeys(row) {
        return row.id
      },
    },
    methods: {
       queryData(row, expandedRows) {
         // 这里默认接收两个参数,row时当前行数据,  expandedRows是已经展开的行(数组)
         let that = this
         //只展开一行
          if (expandedRows.length) { 
         //  说明展开了,  如果展开了多行,  这个数组会存放多个行id
         //  我们不需要多余的展开,  因此清空他,  给他push一个当前行的id即可
            that.expands = []
            if (row) {
              that.expands.push(row.id) //只展开当前行id
            }
          } else { //说明收起了
            that.expands = []
          }
       }
    }
</script>

4. 合并单元格

合并单元格需要使用 span-method 方法, 官网只给了一个很简陋的例子, 无法满足需求

span-method 方法: *通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性。该函数可以返回一个包含两个元素的数组,第一个元素代表rowspan,第二个元素代表colspan。 也可以返回一个键名为rowspan和colspan的对象。*

Dom部分代码:

    <el-table :data="innerDataList" align="center" stripe border :span-method="spanMethod"
            tooltip-effect="dark" style="width: 100%" align="center">
    </el-table>

JS部分代码:

  • 关于getSpanData这个方法, 我也是从网上借鉴来的, 我比较菜, 无法详细表达出来, 我自己是通过画图才大致明白的
  • 关于spanMethod这个方法, 可能有人认为这里面的单元格合并是写死的, 我这里的需求确实是给定了哪些单元格需要合并, 反而我认为, 如果不能确定哪些单元格需要合并, 仅仅根据数据去判断是否合并, 和可能会导致表格混乱 (如果确实要根据数据来, 只需要再增加判断即可)
<script>
    data: {
      spanData: [],
      pos: null,
    },
    methods: {
      // 计算需要合并的单元格
      getSpanData(data) {
        // 存放计算好的合并单元格的规则
        this.spanData = []
        for (var i = 0, len = data.length; i < len; i++) {
          if (i === 0) {
            this.spanData.push(1)
            this.pos = 0
          } else { // 判断当前元素与上一个元素是否相同
            if (data[i].stageGroup === data[i - 1].stageGroup) {
              this.spanData[this.pos] += 1
              this.spanData.push(0)
            } else {
              this.spanData.push(1)
              this.pos = i
            }
          }
        }
      },
      spanMethod({ row, column, rowIndex, columnIndex }) {
        let _row = null
        let _col = null
        if (row.stageGroup == '第一阶段') {
          if (columnIndex === 0 || columnIndex === 3 || columnIndex === 5 || columnIndex === 6) {
            _row = this.spanData[rowIndex]
            _col = _row > 0 ? 1 : 0
            return {
              rowspan: _row,
              colspan: _col
            }
          } else if (columnIndex === 10 && row.stageName !== '未接通') {
            _row = this.spanData[rowIndex] - 1
            _col = _row > 0 ? 1 : 0
            return {
              rowspan: 3,
              colspan: 1
            }
          }
        } else if (columnIndex === 0) {
          _row = this.spanData[rowIndex]
          _col = _row > 0 ? 1 : 0
          return {
            rowspan: _row,
            colspan: _col
          }
        }
      },
      queryData(row, expandedRows) {
        let that = this
        //只展开一行
        if (expandedRows.length) { //说明展开了
           that.expands = []
          if (row) {
            that.expands.push(row.id) //只展开当前行id
           }
         } else { //说明收起了
           that.expands = []
         }
          // 下面res是异步获取的数据
          let res = []
          this.innerDataList = res
          //  调用判断合并单元格规则的方法
          this.getSpanData(this.innerDataList)
       }
    }
</script>

下面是Demo完整代码:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <!-- 引入样式 -->
  <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  <style>
    #app {}

    .merge_thead {
      overflow: visible !important;
      border-right: none !important;
    }

    .merge_thead .cell {
      position: absolute;
      z-index: 1;
      margin-right: -25px;
    }
  </style>
</head>

<body>
  <div class="app">
    <el-table stripe border :data="outDataList" align="center" tooltip-effect="dark" style="width: 100%"
      @expand-change="queryData" :expand-row-keys="expands" :row-key='getRowKeys'>
      <el-table-column prop="batchId" label="数据批次" min-width="90px" align="center">
        <template slot-scope="props">
          <span>{{props.row.batchId}}</span>
        </template>
      </el-table-column>
      <el-table-column prop="distributeNumber" label="下发数量" align="center">
        <template slot-scope="props">
          <span>{{props.row.distributeNumber || 0}}</span>
        </template>
      </el-table-column>
      <el-table-column prop="transformNumber" label="转化数量" align="center">
        <template slot-scope="props">
          <span>{{props.row.transformNumber || 0}}</span>
        </template>
      </el-table-column>
      <el-table-column prop="transformRate" label="转化率" align="center">
        <template slot-scope="props">
          <span>{{props.row.transformRate || '---'}}</span>
        </template>
      </el-table-column>
      <el-table-column prop="createTime" label="转化统计截止时间" align="center">
        <template slot-scope="props">
          <span>{{props.row.createTime || '---'}}</span>
        </template>
      </el-table-column>
      <el-table-column prop="查看详情" type="expand" align="center">
        <template>
          <el-table :data="innerDataList" align="center" stripe border :span-method="spanMethod"
            tooltip-effect="dark" style="width: 100%" align="center">
            <el-table-column prop="stageGroup" label="阶段" align="center" header-align="right"
              label-class-name="merge_thead">
            </el-table-column>
            <el-table-column prop="stageName" align="center">
            </el-table-column>
            <el-table-column prop="distributeNumber" label="数据下发量" align="center">
            </el-table-column>
            <el-table-column prop="rate" label="总量占比" align="center">
            </el-table-column>
            <el-table-column prop="coverRate" label="覆盖率" align="center">
            </el-table-column>
            <el-table-column prop="connectedRate" label="接通率" align="center">
            </el-table-column>
            <el-table-column prop="connectedTransRate" label="接通转化率" align="center">
            </el-table-column>
            <el-table-column prop="transRate" label="整体转化率" align="center">
            </el-table-column>
            <el-table-column prop="transNumberRate" label="转化量占比" align="center">
            </el-table-column>
            <el-table-column prop="avgTransTime" label="平均转化时长" align="center">
            </el-table-column>
            <el-table-column prop="callRate" label="整体拨打频次" align="center">
            </el-table-column>
          </el-table>
        </template>
      </el-table-column>
    </el-table>


  </div>
</body>
<!-- 先引入 Vue -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
  new Vue({
    el: '.app',
    data: {
      innerDataList: [],
      outDataList: [
        {
          "id": 1,
          "batchId": "234",
          "intentId": 10057,
          "distributeNumber": 100,
          "transformNumber": 100,
          "transformRate": "100%",
          "createTime": "2019-11-21 11:26:27",
          "modifyTime": "2019-11-21 11:26:29"
        },
        {
          "id": 2,
          "batchId": "123",
          "intentId": 10057,
          "distributeNumber": "2345",
          "transformNumber": "468",
          "transformRate": "57.6%",
          "createTime": "2019-11-21 11:29:30",
          "modifyTime": "更新时间"
        }
      ],
      spanData: [],
      pos: null,
      expands: [],
      getRowKeys(row) {
        return row.id
      },
    },
    methods: {
      // 计算需要合并的单元格
      getSpanData(data) {
        // 存放计算好的合并单元格的规则
        this.spanData = []
        for (var i = 0, len = data.length; i < len; i++) {
          if (i === 0) {
            this.spanData.push(1)
            this.pos = 0
          } else { // 判断当前元素与上一个元素是否相同
            if (data[i].stageGroup === data[i - 1].stageGroup) {
              this.spanData[this.pos] += 1
              this.spanData.push(0)
            } else {
              this.spanData.push(1)
              this.pos = i
            }
          }
        }
      },
      spanMethod({
        row,
        column,
        rowIndex,
        columnIndex
      }) {
        let _row = null
        let _col = null
        if (row.stageGroup == '第一阶段') {
          if (columnIndex === 0 || columnIndex === 3 || columnIndex === 5 || columnIndex === 6) {
            _row = this.spanData[rowIndex]
            _col = _row > 0 ? 1 : 0
            return {
              rowspan: _row,
              colspan: _col
            }
          } else if (columnIndex === 10 && row.stageName !== '未接通') {
            _row = this.spanData[rowIndex] - 1
            _col = _row > 0 ? 1 : 0
            return {
              rowspan: 3,
              colspan: 1
            }
          }
        } else if (columnIndex === 0) {
          _row = this.spanData[rowIndex]
          _col = _row > 0 ? 1 : 0
          return {
            rowspan: _row,
            colspan: _col
          }
        }
      },
      queryData(row, expandedRows) {
        let that = this
        //只展开一行
        if (expandedRows.length) { //说明展开了
          that.expands = []
          if (row) {
            that.expands.push(row.id) //只展开当前行id
          }
        } else { //说明收起了
          that.expands = []
        }
        // 下面res是异步获取的数据
        let res = [{
          "stage": "第一阶段",
          "batchReportDetails": [{
            "id": 1,
            "batchReportId": 1,
            "stageGroup": "第一阶段",
            "stageName": "第一阶段客服A",
            "distributeNumber": 35005,
            "rate": "13.82",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 0,
            "transRate": 0,
            "transNumberRate": 0,
            "avgTransTime": 0,
            "callRate": 0,
            "createTime": 0,
            "modifyTime": 0,
            "intentId": 10057
          },
          {
            "id": 2,
            "batchReportId": 1,
            "stageGroup": "第一阶段",
            "stageName": "第一阶段客服B",
            "distributeNumber": 35005,
            "rate": "7.67",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 0,
            "transRate": 0,
            "transNumberRate": 0,
            "avgTransTime": 0,
            "callRate": 0,
            "createTime": 0,
            "modifyTime": 0,
            "intentId": 10057
          },
          {
            "id": 3,
            "batchReportId": 1,
            "stageGroup": "第一阶段",
            "stageName": "第一阶段客服C",
            "distributeNumber": 35005,
            "rate": "57.45",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 0,
            "transRate": 0,
            "transNumberRate": 0,
            "avgTransTime": 0,
            "callRate": 0,
            "createTime": 0,
            "modifyTime": 0,
            "intentId": 10057
          },
          {
            "id": 4,
            "batchReportId": 1,
            "stageGroup": "第一阶段",
            "stageName": "未接通",
            "distributeNumber": 35005,
            "rate": "21.06",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 13,
            "transRate": 13,
            "transNumberRate": 13,
            "avgTransTime": 134,
            "callRate": 134,
            "createTime": 134,
            "modifyTime": 134,
            "intentId": 10057
          }
          ]
        }, {
          "stage": "第二阶段",
          "batchReportDetails": [{
            "id": 1,
            "batchReportId": 1,
            "stageGroup": "第二阶段",
            "stageName": "第二阶段客服A",
            "distributeNumber": 35005,
            "rate": "13.82",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 134,
            "transRate": 134,
            "transNumberRate": 14,
            "avgTransTime": 14,
            "callRate": 14,
            "createTime": 14,
            "modifyTime": 14,
            "intentId": 10057
          },
          {
            "id": 2,
            "batchReportId": 1,
            "stageGroup": "第二阶段",
            "stageName": "第二阶段客服B",
            "distributeNumber": 35005,
            "rate": "7.67",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 14,
            "transRate": 14,
            "transNumberRate": 14,
            "avgTransTime": 14,
            "callRate": 5,
            "createTime": 5,
            "modifyTime": 5,
            "intentId": 10057
          },
          {
            "id": 3,
            "batchReportId": 1,
            "stageGroup": "第二阶段",
            "stageName": "第二阶段未接通",
            "distributeNumber": 35005,
            "rate": "57.45",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 5,
            "transRate": 5,
            "transNumberRate": 5,
            "avgTransTime": 5,
            "callRate": 5,
            "createTime": 5,
            "modifyTime": 5,
            "intentId": 10057
          },
          {
            "id": 5,
            "batchReportId": 1,
            "stageGroup": "第二阶段",
            "stageName": "第二阶段客服C",
            "distributeNumber": 35005,
            "rate": "21.06",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 5,
            "transRate": 57,
            "transNumberRate": 57,
            "avgTransTime": 57,
            "callRate": 57,
            "createTime": 57,
            "modifyTime": 57,
            "intentId": 10057
          },
          {
            "id": 6,
            "batchReportId": 1,
            "stageGroup": "第二阶段",
            "stageName": "第二阶段客服C",
            "distributeNumber": 35005,
            "rate": "21.06",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 57,
            "transRate": 57,
            "transNumberRate": 57,
            "avgTransTime": 57,
            "callRate": 579,
            "createTime": 579,
            "modifyTime": 579,
            "intentId": 10057
          }
          ]
        }, {
          "stage": "第三阶段",
          "batchReportDetails": [{
            "id": 1,
            "batchReportId": 1,
            "stageGroup": "第三阶段",
            "stageName": "客服A",
            "distributeNumber": 35005,
            "rate": "13.82",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 579,
            "transRate": 579,
            "transNumberRate": 579,
            "avgTransTime": 579,
            "callRate": 579,
            "createTime": 579,
            "modifyTime": 579,
            "intentId": 10057
          },
          {
            "id": 2,
            "batchReportId": 1,
            "stageGroup": "第三阶段",
            "stageName": "客服B",
            "distributeNumber": 35005,
            "rate": "7.67",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 579,
            "transRate": 16,
            "transNumberRate": 16,
            "avgTransTime": 16,
            "callRate": 16,
            "createTime": 16,
            "modifyTime": 16,
            "intentId": 10057
          }
          ]
        }, {
          "stage": "回收",
          "batchReportDetails": [{
            "id": 1,
            "batchReportId": 1,
            "stageGroup": "回收",
            "stageName": "客服A",
            "distributeNumber": 35005,
            "rate": "13.82",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 16,
            "transRate": 166,
            "transNumberRate": 166,
            "avgTransTime": 166,
            "callRate": 166,
            "createTime": 166,
            "modifyTime": 166,
            "intentId": 10057
          },
          {
            "id": 2,
            "batchReportId": 1,
            "stageGroup": "回收",
            "stageName": "客服B",
            "distributeNumber": 35005,
            "rate": "7.67",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 166,
            "transRate": 165,
            "transNumberRate": 165,
            "avgTransTime": 165,
            "callRate": 165,
            "createTime": 165,
            "modifyTime": 165,
            "intentId": 10057
          },
          {
            "id": 3,
            "batchReportId": 1,
            "stageGroup": "回收",
            "stageName": "未接通",
            "distributeNumber": 35005,
            "rate": "57.45",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 165,
            "transRate": 165,
            "transNumberRate": 165,
            "avgTransTime": 165,
            "callRate": 165,
            "createTime": 1657,
            "modifyTime": 1657,
            "intentId": 10057
          },
          {
            "id": 5,
            "batchReportId": 1,
            "stageGroup": "回收",
            "stageName": "客服C",
            "distributeNumber": 35005,
            "rate": "21.06",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 1657,
            "transRate": 1657,
            "transNumberRate": 1657,
            "avgTransTime": 1657,
            "callRate": 1657,
            "createTime": 1657,
            "modifyTime": 1657,
            "intentId": 10057
          },
          {
            "id": 6,
            "batchReportId": 1,
            "stageGroup": "回收",
            "stageName": "客服C",
            "distributeNumber": 35005,
            "rate": "21.06",
            "coverRate": "100.00",
            "connectedRate": "42.55",
            "connectedTransRate": 1657,
            "transRate": 1657,
            "transNumberRate": 44,
            "avgTransTime": 44,
            "callRate": 44,
            "createTime": 44,
            "modifyTime": 44,
            "intentId": 10057
          }
          ]
        }]
        this.innerDataList = []
        //  这里是因为后端给我的数据结构原因,  所一个拼接,  请注意自己的数据结构
        for (let n of res) {
            this.innerDataList.push(...n.batchReportDetails)
        }
        this.getSpanData(this.innerDataList)
        console.log(this.innerDataList);
      },

    }
  })
</script>

</html>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容