背景
前两天写了一篇关于el-table多行合并的开发备注,感觉还是有用。
如果还不知道el-table合并的原理和思路可以看这个链接 el-table多行合并-CSDN博客。
当时为了上线来不及优化代码。导致代码累赘,逻辑稍微复杂,自己看不下去了,所以进行了优化,那篇有点长,所以写了这篇。
优化点:
(1)优化多层遍历(之前4层,),使用递归的方式
(2)减少变量使用
(3)代码直接复用
注意:当前只实现了行合并。下表合并项只有2列,现在可以实现所有列
(0,0)2,1 | (1,0)1,1 | |
(1,1)1,1 | ||
(0,2)5,1 | (1,2)3,1 | |
(1,5)2,1 | ||
代码
html
<el-table
v-loading="loading"
id="market"
ref="market"
class="table"
height="100%"
tooltip-effect="light"
:data="tableData"
:span-method="objectSpanMethods"
border
>
<template v-for="cols in colConfigs">
<!-- 无需合并的列 -->
<el-table-column
v-if="cols.type === 'label' && !cols.children"
:key="cols.prop"
:prop="cols.prop"
:label="cols.label"
:width="cols.width"
>
</el-table-column>
<!-- 需要合并的列 -->
<template
v-else-if="cols.type === 'label' && cols.children"
>
<el-table-column
v-for="children in cols.children"
:key="children.prop"
:prop="children.prop"
:label="children.label"
:width="cols.width"
/>
</template>
</template>
</el-table>
data变量
说下 sortData: 中的字段的意思
["dt", "desc", true],
0: 属性名
1: 排序方式
2: 生成合并数组表时,是不是检索项
data() {
return {
sortData: [
["dt", "desc", true],
["productType", "asc", true],
["stageType", "asc", true],
["scenarioType", "asc", false],
["scenarioNameNew", "asc", true],
],
loading: false,
tableData: [],
// 表格的信息 需要合并的需要放在 children 中
colConfigs: [
{
type: "label",
children: [
{ prop: "dt", label: "日期", width: 100 },
{ prop: "productName", label: "产品", width: 100 },
{ prop: "stageName", label: "阶段", width: 100 },
{
prop: "scenarioNameNew",
label: "场景名称",
width: 500,
},
{ prop: "mapping", label: "映射关系", width: 100 },
{ prop: "rosterAll", label: "总名单", width: 100 },
{ prop: "rosterIvr", label: "ivr外呼名单", width: 100 },
{ prop: "rosterLlm", label: "llm外呼名单", width: 100 },
{
prop: "rosterArtificial",
label: "人工外呼名单",
width: 100,
},
{ prop: "ratioIvr", label: "ivr占比", width: 100 },
{ prop: "ratioLlm", label: "llm占比", width: 100 },
{
prop: "ratioArtificial",
label: "人工占比",
width: 100,
},
],
},
],
// 用来记录每一个单元格的下标
tableMergeIndex: [],
};
},
相关方法FUN
FUN 根据属性和属性排序方式重排数组
sortByProperties 根据thi.sortData中的属性排序 包含升序和降序
sortByProperties(arr, ...props) {
return arr.sort((a, b) => {
for (let i = 0; i < props.length; i++) {
const prop = props[i];
const [key, order] = Array.isArray(prop)
? prop
: [prop, "asc"];
// 获取属性值
const valueA = a[key];
const valueB = b[key];
// 比较值,考虑升序和降序
if (valueA < valueB) {
return order === "asc" ? -1 : 1;
}
if (valueA > valueB) {
return order === "asc" ? 1 : -1;
}
// 如果相等,比较下一个属性
}
// 所有属性都相等
return 0;
});
},
调用获取list后排序
this.sortByProperties(list, ...this.sortData);
this.tableData = list;
//获取用来记录每一个单元格的下标this.$nextTick(() => {
this.newTableMergeData();
});
FUN生成表格需要的下标
newTableMergeData
//tabel生成 objectSpanMethods方法所需要的 所有坐标
newTableMergeData() {
let propertyValues = this.getPropertyValuesObj(
this.tableData,
this.sortData.map((item) => {
return item[0];
})
);
const tableList = [];
const sortKeyList = [];
const propertyValuesList = [];
for (let index = 0; index < this.sortData.length; index++) {
const element = this.sortData[index];
if (element[2]) {
sortKeyList.push(element[0]);
const list = Object.keys(propertyValues[element[0]]);
let valList = list.sort((a, b) => {
if (a < b) {
return element[1] === "asc" ? -1 : 1;
}
if (a > b) {
return element[1] === "asc" ? 1 : -1;
}
});
// 这里做了个特殊处理 scenarioNameNew
// 这个字段中有字符串"其他" 需要合并 并且放在最后
if (element[0] == "scenarioNameNew") {
valList = this.moveOthersToEnd(valList);
}
propertyValuesList.push(valList);
}
}
this.mergeRows(
tableList,
propertyValuesList,
sortKeyList,
0,
0,
[]
);
const newList = [];
const colsNum = this.sortData.filter((item) => {
return item[2];
}).length;
// 生成所有的表格坐标,有合并行的列用[0,1] 其他用[1,1]
for (let i = 0; i < this.tableData.length; i++) {
for (let j = 0; j < this.colConfigs[0].children.length; j++) {
const key = j + "_" + i;
if (j < colsNum) {
// 如果是特殊合并行数组中的 就用特殊中的值
if (tableList[key]) {
newList[key] = tableList[key];
} else {
newList[key] = [0, 1];
}
} else {
newList[key] = [1, 1];
}
}
}
this.tableMergeIndex = newList;
},
上面代码中涉及方法
FUN 获取每一个属性的值和次数
getPropertyValuesObj
/**
* 获取每一个属性的值和次数
* @param {*} array 数据
* @param {*} propertys 属性排序列表
* 返回 示例:
* {
* dt:{20240401:10,20240402:1},
* productType: {1: 4, 2: 36},
* ....
* }
*
*/
getPropertyValuesObj(array, propertys) {
const obj = {};
for (let i = 0; i < propertys.length; i++) {
const prop = propertys[i];
const propertyValues = {};
array.reduce((acc, item) => {
const value = item[prop];
if (value in acc) {
acc[value]++;
} else {
acc[value] = 1;
}
return acc;
}, propertyValues);
obj[prop] = propertyValues;
}
return obj;
},
FUN 字符串其他移动到最后
moveOthersToEnd
/**
* 将数字中的指定字符串“其他”移到最后 我项目逻辑
* @param {*} arr
* arr :['k1','其他','10FA74A0']
*/
moveOthersToEnd(arr) {
const otherIndex = arr.indexOf("其他");
if (otherIndex !== -1) {
arr.splice(otherIndex, 1);
arr.push("其他");
}
return arr;
},
FUN 生成特殊有合并行坐标的数据数组
mergeRows
类似如下
[
0_0:[5,1],
0_5:[3,1],
0_8:[1,1]
1_0:[2,1]
....
]
/**
*
* @param {递归的存储合数组} list
* @param {所有属性排序后的二位数组} allKeyList
* [
* ['20240415','20240413'],第一项属性 dt的值范围-已处理升序和降序问题
* ['1','2'],第二项属性 productType的值范围-已处理升序和降序问题
* ....
* ]
* @param {排序属性} propertyList
* 属性排序
* ['dt','productType',...]
* @param {每个新属性基础行数 默认0} baseColNum
* @param {排序属性Index} propertyIndex
* 用于递归判断,且获取当前层级属性的所有值
* @param {属性值数组} propertyValList
* 存储当前层级及之上的属性值数组,用于获取相等数据条数,
* 如 属性['dt','propertyList']
* propertyValList :['20240415','1']
* 根据上述条件获取的数据量 num 1
*/
mergeRows(
list,
allKeyList,
propertyList,
baseColNum,
propertyIndex,
propertyValList
) {
if (propertyIndex == 0) {
console.log(
list,
allKeyList,
propertyList,
baseColNum,
propertyIndex,
propertyValList
);
}
let yNum0 = baseColNum;
const arr = allKeyList[propertyIndex];
for (let index = 0; index < arr.length; index++) {
const propertyValListprivate = [
...propertyValList,
allKeyList[propertyIndex][index],
];
if (propertyIndex < propertyList.length - 1) {
this.mergeRows(
list,
allKeyList,
propertyList,
yNum0,
propertyIndex + 1,
propertyValListprivate
);
}
const num = this.getObjectsWithSameValues(
this.tableData,
propertyList.slice(0, propertyIndex + 1),
propertyValListprivate
).length;
let key0 = propertyIndex + "_" + yNum0;
if (num > 0) {
yNum0 += num;
list[key0] = [num, 1];
}
}
},
FUN 根据 属性列表properties及属性值vals 查看对应数据条数
getObjectsWithSameValues
/**
* 根据 属性列表properties及属性值vals 查看对应数据条数
* @param {*} arr 数据列表
* @param {*} properties
* @param {*} vals
*/
getObjectsWithSameValues(arr, properties, vals) {
const list = arr.filter((item) => {
let isRight = true;
for (let index = 0; index < properties.length; index++) {
const element = properties[index];
if (item[element] != vals[index]) {
isRight = false;
}
}
return isRight;
});
return list;
},
el-table合并项配合 :span-method
objectSpanMethods
// el-table合并项配合 :span-method
objectSpanMethods({ row, column, rowIndex, columnIndex }) {
let key = columnIndex + "_" + rowIndex;
if (this.tableMergeIndex[key]) {
return this.tableMergeIndex[key];
}
},
总结
如果能解决问题,麻烦给我点个赞,非常感谢。
再次备注:逻辑请看el-table多行合并-CSDN博客