需求
在最近的项目中有这样一个需求:
- eltable需要根据左侧点击不同的树上的数据来动态渲染;
- 要动态实现表格的数据和头的合并。
最终效果
代码实现
代码基于vue2.x 和elementui2.10实现
动态表头的实现主要是将表头通过循环的方式来渲染。
表格数据的合并主要是用到了
span-method方法
模版部分
<el-table
border
:data="tableData"
:span-method="arraySpanMethod"
:header-cell-style="handerMethod"
style="width: 100%"
id="export-table"
>
// 这种写法是用的header-cell-style来实现合并表头的
<el-table-column prop="level2Data" align="center">
</el-table-column>
<el-table-column prop="level3Data" label="项目" align="center">
</el-table-column>
// 这种写法是用的表头嵌套来实现的合并表头
//tabelMonthData授右上角的select控制
//tabelColumData 受经营数据控制
<el-table-column
v-for="item in tabelMonthData"
:key="item"
align="center"
:label="item"
>
<el-table-column
v-for="c in tabelColumData"
:key="c.label"
:label="c.label"
align="center"
>
</el-table-column>
</el-table-column>
</el-table>
js部分
// 表格合并项处理方法
arraySpanMethod({ rowIndex, columnIndex }) {
let r = rowIndex;
let c = columnIndex;
// 统计有多少重复的数据
let rowObj = this.tableData.reduce((pre, cur) => {
cur.level2Data in pre
? pre[cur.level2Data]++
: (pre[cur.level2Data] = 1);
return pre;
}, {});
// 将重复的数据转换成数组
let rowArray = Object.values(rowObj);
// 动态生成合并的起始点和合并数量
let tempArr = [];
let vArr = [];
rowArray.reduce((pre, acc, i) => {
if (i === 0) {
tempArr.push({ v: 0, col: acc });
vArr.push(0);
} else {
tempArr.push({ v: pre, col: acc });
vArr.push(pre);
}
return pre + acc;
}, 0);
// 执行合并并删除被合并的单元格
if (c === 0) {
const i = tempArr.findIndex((item) => item.v === r);
return i === -1 && !vArr.includes(r) ? [0, 0] : [tempArr[i].col, 1];
}
},
// 点击数据经营的函数
formatData 数据:
[
{ level2Data: '经营数据', level3Data: '资产负载状况', id: 10 },
{ level2Data: '经营数据', level3Data: '损益状况', id: 11 },
{ level2Data: '资产负载状况', level3Data: '资产总额', id: 101 },
{ level2Data: '资产负载状况', level3Data: '负载总额', id: 102 },
{ level2Data: '损益状况', level3Data: '营业收入', id: 110 },
{ level2Data: '损益状况', level3Data: '营业支出', id: 111 },
{ level2Data: '损益状况', level3Data: '净利润', id: 112 }
]
handleTreeCheck(v, data) {
this.tableData = [];
let { checkedKeys } = data;
this.treeOneCheckedKeys = checkedKeys;
for (let i = 0; i < this.formatData.length; i++) {
const item = this.formatData[i];
if (item.pLabel === "经营数据") continue;
if (checkedKeys.includes(item.id)) {
this.tableData.push({
level2Data: item.pLabel,
level3Data: item.label,
});
}
}
},
// 合并表头
handerMethod({ row ,columnIndex }) {
if (row[0].level == 1) {
row[0].colSpan = 0;
row[1].colSpan = 2;
if (columnIndex === 0) {
return { display: "none" };
}
}
},
// 勾选附加数据
handleRowTreeCheck(v, data) {
// 数据重置
this.tabelColumData = this.$options.data().tabelColumData;
let { checkedNodes, checkedKeys } = data;
this.tree2CheckedKeys = checkedKeys;
let temp = checkedNodes.filter((i) => !i.children);
if (temp)
temp.forEach((item) => {
if (item.label.endsWith("变化")) {
this.tabelColumData.push(
{
label: `${item.label}值`,
},
{
label: `${item.label}比例`,
}
);
} else {
this.tabelColumData.push({
label: "指标完成比例",
});
}
});
},
// 右上角的时间选中
handleTimeChange(v) {
this.tabelMonthData = this.timeOptions
.filter((i) => i.value === v)
.map((item) => {
return item.month;
})
.flat();
},