话不多说, 直接上效果图
-
这里是未展开的表格:
-
展开之后:
完整的Demo代码在最下方
拆分解析:
1. 从最简单的合并表头开始
这里因为没有从官方文档找到合适的方法, 我利用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>