项目场景:
表格中数值相同且相临的单元格内合并显示
演示代码在页面最后
解决方案:
使用 Element-ui Table 插件完成
原理:
Element-ui Table 提供了一个 span-method 属性,用来合并单元格.
通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性。该函数可以返回一个包含两个元素的数组,第一个元素代表rowspan,第二个元素代表colspan。 也可以返回一个键名为rowspan和colspan的对象。
文档地址
如下面的代码,我们就能完成"目标效果"
<template>
<div class="w">
<h4>目标效果</h4>
<el-table ref="dTable" :data="list" :span-method="arraySpanMethod" border stripe>
<el-table-column prop="c1" label="C1" width="180" sortable />
<el-table-column prop="c2" label="C2" />
<el-table-column prop="c3" label="C3" />
<el-table-column prop="c4" label="C4" />
<el-table-column prop="c5" label="C5" width="220" />
<el-table-column prop="c6" label="c6" width="220" />
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
list: [{
c1: '第一类', c2: 'Siri', c3: 'B1', c4: '2022年5月27日', c5: '1', c6: '1' }, {
c1: '第一类', c2: 'Siri', c3: 'B2', c4: '2022年5月27日', c5: '1', c6: '2' }, {
c1: '第一类', c2: 'Siri', c3: 'B3', c4: '2022年5月27日', c5: null, c6: '3' }, {
c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '4' }, {
c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '5' }, {
c1: '小爱同学', c2: '小爱同学', c3: '小爱同学', c4: '2022年5月27日', c5: '201', c6: '6' }, {
c1: '小明', c2: '小明', c3: '小明', c4: '2022年5 月27日', c5: '201', c6: '7' }, {
c1: '小明', c2: '小明', c3: '小明', c4: '2022年5月27日', c5: '201', c6: '8' }]
}
},
mounted() {
},
methods: {
// el-table span-method 合并行或列的计算方法
// 原理 : 合并方法会遍历所有的单元格,返回该单元格所占的行,列( [rowspan,colspan] )就是显示的结果
// ps : [1,1] 不变, [0,0] [1,0] [0,1] 隐藏,只要有一个值为0就会隐藏
arraySpanMethod({
row, column, rowIndex, columnIndex }) {
var map = [
[[3, 1], [3, 1], [2, 1]],
[[0, 1], [0, 1], [0, 1]],
[[0, 1], [0, 1], [1, 1]],
[[2, 1], [2, 2], [0, 0]],
[[0, 1], [0, 0], [0, 0]],
[[1, 3], [1, 0], [0, 0]],
[[2, 3], [1, 0], [0, 0]],
[[0, 0], [1, 0], [0, 0]]
]
try {
var r = map[rowIndex][columnIndex]
return r
} catch (error) {
return [1, 1]
}
}
}
}
</script>
设计思路:
按上面的原理代码,我们将问题转化为 : 如何生成一个反映表格合并的二维数组
坑1
table使用的数据是
对象数组 [{c1:123}]
,对象中的key不能对应列的显示顺序
坑2
Table插件内部数据发生变化时(如:排序,筛选),父组件传递的值是不变的
所以必须监听子组件内部的值
最终代码:
/**
* \src\views\table\merge-cell.js
* @param {*} table vue对象, Element-ui Table 插件
* @param {*} colMap 数组, 需要合并的列名,如: ['c1','c2','c2']
* @param {*} isSpanRow 布尔,是否合并行
* @param {*} isSpanCol 布尔,是否合并列
* @returns 二维数组,Table的显示结果
*
* mergeCell(this.$refs.dTable, ['c1', 'c2', 'c3'], true, true)
*/
var mergeCell = function(table, colMap, isSpanRow = true, isSpanCol = true) {
var list = table.tableData
// table.tableData 中 每行的数据是一个对象,不能直接的反应显示的顺序
// 即你不能知道第一列右则的列是那个.
// 需要通过table.columns建立显示的顺序
var columns = []
for (const item of table.columns) {
columns.push(item.property)
}
// 结果应是一个二维数组
// 先充填数据,使用其成为全显示的数组
var result = []
for (let row = 0, rowLen = list.length; row < rowLen; row++) {
const ss = []
for (let col = 0, colLen = columns.length; col < colLen; col++) {
ss.push([1, 1])
}
result.push(ss)
}
// 合并行
if (isSpanRow) {
result = mergeRow(list, columns, colMap, result)
}
// 合并列
if (isSpanCol) {
result = mergeCol(list, columns, colMap, result)
}
return result
}
var mergeRow = function(list, columns, colMap, result) {
for (const item of colMap) {
const p = {
colKey: item,
col: columns.indexOf(item),
row: 0,
val: ''
}
for (let row = 0, rowLen = list.length; row < rowLen; row++) {
if (p.val === list[row][p.colKey]) {
result[p.row][p.col][0] += 1
result[row][p.col][0] = 0
} else {
p.row = row
p.val = list[row][p.colKey]
}
}
}
return result
}
var mergeCol = function(list, columns, colMap, result) {
for (let row = 0, rowLen = list.length; row < rowLen; row++) {
const p = {
col: 0,
row: row,
val: ''
}
for (let col = 0, colLen = columns.length; col < colLen; col++) {
if (!colMap.includes(columns[col])) {
p.col = col
p.val = list[row][columns[col]]
continue
}
if (p.val === list[row][columns[col]]) {
result[p.row][p.col][1] += 1
result[p.row][col][1] = 0
} else {
p.col = col
p.val = list[row][columns[col]]
}
}
}
return result
}
export default mergeCell
/**
* src\views\table\merge.vue
* 合并单元格 DEMO
*/
<template>
<div class="w">
<h4>原始表</h4>
<el-table
border
:data="list"
style="width: 100%"
header-row-class-name="t-header"
stripe
class="dTable"
>
<el-table-column prop="c1" label="C1" width="180" />
<el-table-column prop="c2" label="C2" />
<el-table-column prop="c3" label="C3" />
<el-table-column prop="c4" label="C4" />
<el-table-column prop="c5" label="C5" width="220" />
<el-table-column prop="c6" label="c6" width="220" />
</el-table>
<h4>目标效果</h4>
<el-table
ref="dTable"
border
:data="list"
style="width: 100%"
header-row-class-name="t-header"
stripe
class="dTable"
:span-method="arraySpanMethod"
>
<el-table-column prop="c1" label="C1" width="180" sortable />
<el-table-column
prop="c2"
label="C2"
:filters="[
{ text: 'Siri', value: 'Siri' },
{ text: 'YOYO', value: 'YOYO' },
{ text: '小爱同学', value: '小爱同学' }
]"
:filter-method="filterHandler"
/>
<el-table-column prop="c3" label="C3" />
<el-table-column prop="c4" label="C4" />
<el-table-column prop="c5" label="C5" width="220" />
<el-table-column prop="c6" label="c6" width="220" />
</el-table>
</div>
</template>
<script>
import mergeCell from './merge-cell.js'
export default {
filters: {
},
data() {
return {
list: [
{
c1: '第一类', c2: 'Siri', c3: 'B1', c4: '2022年5月27日', c5: '1', c6: '1'
},
{
c1: '第一类', c2: 'Siri', c3: 'B2', c4: '2022年5月27日', c5: '1', c6: '2'
},
{
c1: '第一类', c2: 'Siri', c3: 'B3', c4: '2022年5月27日', c5: null, c6: '3'
},
{
c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '4'
},
{
c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '5'
},
{
c1: '小爱同学', c2: '小爱同学', c3: '小爱同学', c4: '2022年5月27日', c5: '201', c6: '6'
},
{
c1: '小明', c2: '小明', c3: '小明', c4: '2022年5月27日', c5: '201', c6: '7'
},
{
c1: '小明', c2: '小明', c3: '小明', c4: '2022年5月27日', c5: '201', c6: '8'
}
],
mergeMap: []
}
},
mounted() {
// 当el-table 内部数据发生变化时(如:排序,筛选),父组件传递的值是不变的.
// 所以必须监听子组件内部的值
// https://cn.vuejs.org/v2/api/#vm-watch
this.$watch(
function() {
return this.$refs.dTable.tableData
},
function(newVal, oldVal) {
var ss = mergeCell(this.$refs.dTable, ['c1', 'c2', 'c3'], true, true)
this.mergeMap = ss
},
{
immediate: true
}
)
},
methods: {
// el-table span-method 合并行或列的计算方法
// 原理 : 合并方法会遍历所有的单元格,返回该单元格所占的行,列( [rowspan,colspan] )就是显示的结果
// ps : [1,1] 不变, [0,0] [1,0] [0,1] 隐藏,只要有一个值为0就会隐藏
arraySpanMethod({
row, column, rowIndex, columnIndex }) {
var map = this.mergeMap
try {
var r = map[rowIndex][columnIndex]
return r
} catch (error) {
return r
}
},
filterHandler(value, row, column) {
const property = column['property']
return row[property] === value
}
}
}
</script>