合并单元格,列存在包含与被包含关系
如图,支付场景为第一序列,支付产品存在与支付场景下,而APPID有存在与支付产品下,场景>产品>APPID
思路
1.初始化数据阶段,对数据进行添加需要合并的参数和标识
- =合并第一列=
- 首要知道需要合并第一列相同属性sceneId,开始遍历数组
- 找到相同的sceneId,是否已经有一项有sceneIdStart属性
- 没有的情况下,在当前项,添加sceneIdStart属性为当前索引,并且计算有多少个相同的sceneId,并复制给当前项的sceneIdNum
- =合并第二列=
- 首要知道需要合并第二列相同属性productValue,他是包含在,第一列,相同的sceneId中,开始遍历数组
- 遍历过程中,判断当前项是否有sceneIdNum,
- 有的情况下,使用slice(item.sceneIdStart, item.sceneIdStart + item.sceneIdNum),浅拷贝该数组,赋值给sceneIdStartArr,这个新数组就是包含相同的sceneId数据(利用slice浅拷贝,属性为对象会影响到源数据的特性)(后续需要修改的内容也是这里的,后续流程其实是在走合并第一列的流程)
- 开始遍历sceneIdStartArr数组,找到相同的productValue,并且是否已经有一项有productValueStart属性
- 没有的情况下,在当前项,添加sceneIdStart属性为当前索引,并且计算有多少个相同的productValue,并赋值给当前项的productValueNum
- =合并第三列=(和合并第二列逻辑一样)
- 首要知道需要合并第二列相同属性APPID,他是包含在,第二列,相同的productValue中,开始遍历数组
- 遍历过程中,判断当前项是否有productValueNum,
- 有的情况下,使用slice(item.productValueStart, item.productValueStart + item.productValueNum),浅拷贝该数组,赋值给productValueStartArr,这个新数组就是包含相同的productValue数据
- 开始遍历productValueStartArr数组,找到相同的APPID,并且是否已经有一项有APPIDStart属性
- 没有的情况下,在当前项,添加APPIDStart属性为当前索引,并且计算有多少个相同的APPID,并赋值给当前项的APPIDNum
2.el-tabel,调用span-method=“objectSpanMethod”,在该方法中,只要判断第几列,需要用到特定的参数或标识
- objectSpanMethod存在四个参数({ row, column, rowIndex, columnIndex })
- 判断是否为第一列,columnIndex === 0,判断当前数据是否包含sceneIdNum属性,有的话添加
if (row.sceneIdNum) {
return {
rowspan: row.sceneIdNum,
colspan: 1,
}
} else {
return {
rowspan: 0,
colspan: 0,
}
}
-
后续第二例和,第三列同样操作
-
初始化计算的好处:减少了在el-tabel实时计算
代码展示
- 步骤一
/* **
* @title 合并单元格列: 需要合并的属性
* @param { String|Number } sceneId 支付场景
* @param { String } productValue 支付产品
* @param { String|Number } APPID APPID
*/
infodata() {
// 1.合并sceneId 支付场景
this.tableData.forEach((item, index, arr) => {
item.index = index
// 1.判断 数组中是否有相同的sceneId 和 sceneIdStart
const isSceneIdAndStart = arr.some((v) => {
return v.sceneId === item.sceneId && v.hasOwnProperty('sceneIdStart')
})
// 2. 如果没有进行对当前id计算,则进行计算和赋值
if (!isSceneIdAndStart) {
// 用来计算sceneId 的个数
const sceneNum = arr.reduce((accumulator, currentValue) => {
const isVal = currentValue.sceneId === item.sceneId ? 1 : 0
return accumulator + isVal
}, 0)
item.sceneIdStart = index
item.sceneIdNum = sceneNum
}
})
// 2.合并 APPID APPID
this.tableData.forEach((item, index, arr) => {
// 1.判断 数组中是否有相同的sceneId 的sceneIdNum
if (item.hasOwnProperty('sceneIdNum')) {
// 2.合并一组相同的数据,并且判断当前合并数组是否有相同的productValueStart
const sceneIdStartArr = arr.slice(item.sceneIdStart, item.sceneIdStart + item.sceneIdNum)
sceneIdStartArr.forEach((d, k, n) => {
const isProductValueAndStart = n.some((v) => {
return d.productValue === v.productValue && v.hasOwnProperty('productValueStart')
})
// 3. 如果没有进行对当前isProductValueAndStart,则进行计算和赋值
if (!isProductValueAndStart) {
const productValueNum = n.reduce((accumulator, currentValue) => {
const isVal = currentValue.productValue === d.productValue ? 1 : 0
return accumulator + isVal
}, 0)
d.productValueStart = d.index
d.productValueNum = productValueNum
// console.log('d', d)
}
})
}
})
// 3.合并 APPID
this.tableData.forEach((item, index, arr) => {
// 1.判断 数组中是否有的productValueNum
if (item.hasOwnProperty('productValueNum')) {
// 2.合并一组相同的数据,并且判断当前合并数组是否有相同的productValueStart,利用slice浅拷贝缺陷
const productValueStartArr = arr.slice(
item.productValueStart,
item.productValueStart + item.productValueNum,
)
productValueStartArr.forEach((d, k, n) => {
const isAPPIDAndStart = n.some((v) => {
return d.APPID === v.APPID && v.hasOwnProperty('APPIDStart')
})
// 3. 如果没有进行对当前isProductValueAndStart,则进行计算和赋值
if (!isAPPIDAndStart) {
const APPIDNum = n.reduce((accumulator, currentValue) => {
const isVal = currentValue.APPID === d.APPID ? 1 : 0
return accumulator + isVal
}, 0)
d.APPIDStart = d.index
d.APPIDNum = APPIDNum
// console.log('d', d)
}
})
}
})
},
- 步骤二
<el-table :data="tableData" :span-method="objectSpanMethod" border style="width: 100%"></el-table>
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
// 第一列合并
if (columnIndex === 0) {
if (row.sceneIdNum) {
return {
rowspan: row.sceneIdNum,
colspan: 1,
}
} else {
return {
rowspan: 0,
colspan: 0,
}
}
}
// 第二列合并
if (columnIndex === 1) {
if (row.productValueNum) {
return {
rowspan: row.productValueNum,
colspan: 1,
}
} else {
return {
rowspan: 0,
colspan: 0,
}
}
}
// 第三列合并
if (columnIndex === 2) {
if (row.APPIDNum) {
return {
rowspan: row.APPIDNum,
colspan: 1,
}
} else {
return {
rowspan: 0,
colspan: 0,
}
}
}
},
- 数据源
{
"tableData": [
{
"scene": "demo1",
"sceneId": "1499411749324066816",
"product": "微信付款码",
"productValue": "wx_bar",
"APPID": "12987122",
"channel": "微信",
"weight": "1",
"mchnum": 10
},
{
"scene": "demo1",
"sceneId": "1499411749324066816",
"product": "微信付款码",
"productValue": "wx_bar",
"APPID": "12987122",
"channel": "第三方服务商",
"weight": "1",
"mchnum": 10
},
{
"scene": "demo1",
"sceneId": "1499411749324066816",
"product": "支付宝付款码",
"productValue": "ali_bar",
"APPID": "12987122",
"channel": "第三方服务商",
"weight": "1",
"mchnum": 10
},
{
"scene": "demo2",
"sceneId": "1499411585939148800",
"product": "微信小程序",
"productValue": "wx_lite",
"APPID": "12987124",
"channel": "微信",
"weight": "1",
"mchnum": 10
},
{
"scene": "demo2",
"sceneId": "1499411585939148800",
"product": "微信小程序",
"productValue": "wx_lite",
"APPID": "12987122",
"channel": "第三方服务商",
"weight": "1",
"mchnum": 10
}
]
}
样式优化-自定义指令
export const eltablebacknode = Vue => {
// el-table 合并单元格背景色
// v-backnode='"{ length: 6, merge: 3 }"' 表格每行应6列,合并列为3列
Vue.directive('backnode', {
bind: function (el, binding) {
// console.log('el, binding', el, binding)
const length = binding.value.length
const merge = binding.value.merge
const residue = length - merge // 抛去需要合并的列单元格 其余剩余内容
Vue.nextTick(() => {
let tbody = el.querySelector('.el-table__body tbody')
let trAll = Array.from(tbody.querySelectorAll('tr'))
// console.log('trAll', trAll[0])
trAll.forEach(row => {
const childNodes = row.childNodes
const rowResidue = childNodes.length - residue // 当前行有多少合并单元格
// console.log(childNodes.length, residue);
if (childNodes.length > residue) {
childNodes.forEach((v, i) => {
// console.log('i < rowResidue', i < rowResidue);
if (i < rowResidue) {
v.classList.add('tdbackBack')
}
})
}
console.log('row', childNodes)
})
})
},
})
}
.el-table__body tbody .el-table__row td.tdbackBack{
background-color: #fff!important;
}