方法一
效果图:

优点
- 利用三维数组,单元格实现合并行或列
缺点
- 但是myMappings是写死的,不是动态生成的
<template>
<div>
<el-table
:show-header="false"
:highlight-current-row="false"
:data="tableData"
:span-method="tableSpanMethod"
border
style="width: 100%"
>
<el-table-column prop="0" />
<el-table-column prop="1" />
<el-table-column prop="2" />
<el-table-column prop="3" />
<el-table-column prop="4" />
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [
['BOARD_1', '', 'BOARD_2', '', 'FAN'],
['BOARD_3', '', 'BOARD_4', '', ''],
['PWR', '', '', '', '']
]
}
},
methods: {
tableSpanMethod({ row, column, rowIndex, columnIndex }) {
var myMappings = [
[[1, 2], [0, 0], [1, 2], [0, 0], [2, 1]],
[[1, 2], [0, 0], [1, 2], [0, 0], [0, 0]],
[[1, 5], [1, 1], [1, 1], [1, 1], [1, 1]]
]
return myMappings[rowIndex][columnIndex]
}
}
}
</script>
方法二
效果图

优点
- 只需要传递需合并字段的prop值,就能实现表格的动态合并
缺点
- 【产品小类】和【频段】是独立合并的
- 应该先按【产品小类】合并,再按【频段】合并
- 现在把【小类1】和【小类2】的【频段2】都合并到一起了
<template>
<el-table size="small" :data="data" :span-method="objectSpanMethod" :summary-method="getSummaries" show-summary border>
<el-table-column
v-for="column in columns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:width="column.width"
show-overflow-tooltip
/>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [
{
subName: '小类1',
freq: '频段1',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '2',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类1',
freq: '频段1',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '5',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类1',
freq: '频段2',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '6',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类1',
freq: '频段2',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '9',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类2',
freq: '频段2',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '9',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
}
],
columns: [
{ prop: 'subName', label: '产品小类', width: '100' },
{ prop: 'freq', label: '频段', width: '80' },
{ prop: 'itemName', label: '设备号' },
{ prop: 'pillarNo', label: '位置', width: '200' },
{ prop: 'totalValue', label: '测试总数', width: '80' },
{ prop: 'dataValue', label: '故障数', width: '80' },
{ prop: 'dataPerc', label: '故障率', width: '80' },
{ prop: 'startDatetime', label: '统计开始时间', width: '135' },
{ prop: 'endDatetime', label: '统计结束时间', width: '135' }
]
}
},
computed: {
data() {
return this.mergeTableRow(this.tableData, ['subName', 'freq'])
}
},
methods: {
mergeTableRow(data, merge) {
if (!merge || merge.length === 0) {
return data
}
merge.forEach((m) => {
const mList = {}
data = data.map((v, index) => {
const rowVal = v[m]
if (mList[rowVal] && mList[rowVal].newIndex === index) {
mList[rowVal]['num']++
mList[rowVal]['newIndex']++
data[mList[rowVal]['index']][m + '-span'].rowspan++
v[m + '-span'] = {
rowspan: 0,
colspan: 0
}
} else {
mList[rowVal] = { num: 1, index: index, newIndex: index + 1 }
v[m + '-span'] = {
rowspan: 1,
colspan: 1
}
}
return v
})
})
return data
},
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
const span = column['property'] + '-span'
if (row[span]) {
return row[span]
}
},
getSummaries(param) {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '总计'
return
}
const values = data.map(item => Number(item[column.property]))
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
} else {
sums[index] = '--'
}
})
sums[6] = (sums[5] * 100 / sums[4]).toFixed(2) + '%'
return sums
}
}
}
</script>
方法三
效果图

优点
- 可以先按【产品小类】合并,再按【频段】合并,完全实现定制化合并
缺点
- myMappings还未实现函数传参,知道效果图后,才能计算出myMappings
<template>
<el-table size="small" :data="tableData" :span-method="objectSpanMethod" :summary-method="getSummaries" show-summary border>
<el-table-column
v-for="column in columns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:width="column.width"
show-overflow-tooltip
/>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [
['小类1', '频段1', '设备号1', '位置1', '100', '2', '1%', '2020-04-13 00:00:00', '2020-04-14 00:00:00'],
['小类1', '频段1', '设备号1', '位置1', '100', '2', '1%', '2020-04-13 00:00:00', '2020-04-14 00:00:00'],
['小类1', '频段2', '设备号1', '位置1', '100', '2', '1%', '2020-04-13 00:00:00', '2020-04-14 00:00:00'],
['小类1', '频段2', '设备号1', '位置1', '100', '2', '1%', '2020-04-13 00:00:00', '2020-04-14 00:00:00'],
['小类2', '频段2', '设备号1', '位置1', '100', '2', '1%', '2020-04-13 00:00:00', '2020-04-14 00:00:00']
],
columns: [
{ prop: '0', label: '产品小类', width: '100' },
{ prop: '1', label: '频段', width: '80' },
{ prop: '2', label: '设备号' },
{ prop: '3', label: '位置', width: '200' },
{ prop: '4', label: '测试总数', width: '80' },
{ prop: '5', label: '故障数', width: '80' },
{ prop: '6', label: '故障率', width: '80' },
{ prop: '7', label: '统计开始时间', width: '135' },
{ prop: '8', label: '统计结束时间', width: '135' }
]
}
},
methods: {
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
/**
* myMappings是一个5*9的二维数组,每个元素的值均是[x, y],
* x代表rowspan,y代表colspan,[x, y]为合并后该单元格左上角的坐标值
* [4, 1]表示,行向下合并4个单元格,列保持不变,仍为1个单元格
* [0, 0]表示,该单元格被别的单元格吞并了,不显示
* [1, 1]表示,显示原有单元格,单元格的行和列均不进行合并
*/
var myMappings = [
[[4, 1], [2, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
[[0, 0], [0, 0], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
[[0, 0], [2, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
[[0, 0], [0, 0], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
[[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]]
]
return myMappings[rowIndex][columnIndex]
},
getSummaries(param) {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '总计'
return
}
const values = data.map(item => Number(item[column.property]))
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
} else {
sums[index] = '--'
}
})
sums[6] = (sums[5] * 100 / sums[4]).toFixed(2) + '%'
return sums
}
}
}
</script>
改进方法三
效果图

优点
- 先按【产品小类】合并,再按【频段】合并,且myMappings已实现动态生成
缺点
- myMappings生成方法不能接受传参,只能定制化合并单元格
<template>
<el-table
size="small"
:data="tableData"
:span-method="objectSpanMethod"
:summary-method="getSummaries"
show-summary
border>
<el-table-column
v-for="column in columns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:width="column.width"
show-overflow-tooltip
/>
</el-table>
</template>
<script>
export default {
data() {
return {
originTableData: [
{
subName: '小类1',
freq: '频段1',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '2',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类1',
freq: '频段1',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '5',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类1',
freq: '频段2',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '6',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类1',
freq: '频段2',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '9',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
},
{
subName: '小类2',
freq: '频段2',
itemName: '设备号1',
pillarNo: '位置1',
totalValue: '100',
dataValue: '9',
dataPerc: '1%',
startDatetime: '2020-04-13 00:00:00',
endDatetime: '2020-04-14 00:00:00'
}
],
columns: [
{ prop: '0', label: '产品小类', width: '100' },
{ prop: '1', label: '频段', width: '80' },
{ prop: '2', label: '设备号' },
{ prop: '3', label: '位置', width: '200' },
{ prop: '4', label: '测试总数', width: '80' },
{ prop: '5', label: '故障数', width: '80' },
{ prop: '6', label: '故障率', width: '80' },
{ prop: '7', label: '统计开始时间', width: '135' },
{ prop: '8', label: '统计结束时间', width: '135' }
],
map:[]
}
},
computed: {
tableData() {
// 格式化接口返回的表格数据,每一项,对象转数组
let formatTableData = []
this.originTableData.map(item => {
formatTableData.push(Object.values(item))
})
return formatTableData
}
},
methods: {
originMap() {
// 初始化一个5X9的二维数组,每一项的值均为[1,1]
const rows = this.originTableData.length
var arr = new Array()
for(let i=0; i<rows; i++){
arr[i] = new Array()
for(let j=0; j<9; j++){
arr[i][j] = [1,1]
}
}
return arr
},
mergedMap() {
/**
* 动态生成myMappings
* --------------------------------步骤1------------------------------------
* 合并第0列的单元格时,从表格最后一行遍历到第一行,修改originMap的初始值
* 如果tab[n][0] === tab[n-1][0],则第n-1行第0列的X坐标 += 第n行第0列的X坐标
* 再将第n行第0列的X坐标和Y坐标,均赋值为[0, 0]
* [0, 0]表示该单元格不显示,因为被合并了
* --------------------------------步骤2------------------------------------
* 合并第1列的单元格时,需考虑第0列上下两行的X坐标的值是否相等
* 只有tab[n][0] === tab[n-1][0]时, 继续以步骤1的方法遍历,修改originMap的初始值
*/
const tab = this.tableData
const map = this.originMap()
const rows = this.originTableData.length - 1
// 合并第0列
for(var r=rows; r>0; r--) {
if(tab[r][0] === tab[r-1][0]) {
map[r-1][0][0] += map[r][0][0]
map[r][0] = [0, 0]
}
}
// 合并第1列,与第0列的值有关
for(var r=rows; r>0; r--) {
if(tab[r][1] === tab[r-1][1] && tab[r][0] === tab[r-1][0]) {
map[r-1][1][0] += map[r][1][0]
map[r][1] = [0, 0]
}
}
return map
},
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
var myMappings = this.mergedMap()
return myMappings[rowIndex][columnIndex]
},
getSummaries(param) {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '总计'
return
}
const values = data.map(item => Number(item[column.property]))
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
} else {
sums[index] = '--'
}
})
sums[6] = (sums[5] * 100 / sums[4]).toFixed(2) + '%'
return sums
}
}
}
</script>