el-table树形表格合并相同的值

时间:2025-03-19 08:02:03
<style lang="scss" scoped> .tableBox { /deep/ &.el-table th:first-child, /deep/ &.el-table td:first-child { padding-left: 0; } } </style> <template> <div> <el-table class="tableBox" row-key="uniID" ref="refTable" :data="tableData" style="width: 100%" border :span-method="arraySpanMethod" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > <el-table-column prop="dateTime" label="时间" key="dateTime" min-width="140"> <template slot-scope="{ row }"> {{ row.groupNo ? findValue(row.groupNo, groupNoList) : row.dateTime }} </template> </el-table-column> <el-table-column prop="yieldConsume" label="产量(t)" key="yieldConsume" min-width="110" /> </el-table> </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; interface TableRow { uniID: number; dateTime: string; groupNo: number | null; yieldConsume: number; children?: TableRow[]; parent?: TableRow | null; // 新增预处理字段 spanInfo?: { rowspan: number; hidden: boolean; }; } @Component({}) export default class EnergyAnalysis extends Vue { private tableData: TableRow[] = []; private groupNoList: any = [ { value: '甲', key: 1, }, { value: '乙', key: 2, }, { value: '丙', key: 3, }, { value: '丁', key: 4, }, ]; private testData = { data: { everyDetail: [ { dateTime: '2025-03-01', groupNo: null, yieldConsume: -30176.691, children: [ { dateTime: '2025-03-01', groupNo: 1, yieldConsume: 100, children: null, }, { dateTime: '2025-03-01', groupNo: 1, yieldConsume: -18885.714, children: null, }, { dateTime: '2025-03-01', groupNo: 2, yieldConsume: 100, children: null, }, { dateTime: '2025-03-01', groupNo: 2, yieldConsume: 101, children: null, }, { dateTime: '2025-03-01', groupNo: 2, yieldConsume: 102, children: null, }, ], }, { dateTime: '2025-03-02', groupNo: null, yieldConsume: -30176.691, children: [ { dateTime: '2025-03-02', groupNo: 1, yieldConsume: 111, children: null, }, ], }, ], }, }; created() { this.statisticsQuery(); } private findValue(data: any, list: any) { const a = list.find((el: any) => el.key === data); return a ? a.value : '-'; } private async statisticsQuery() { let index = 1; const recursionList = (list: any[], parent: TableRow | null = null): TableRow[] => { return list?.map((v: any) => { const newRow: TableRow = { ...v, uniID: index++, parent: parent, spanInfo: { rowspan: 1, hidden: false }, // 初始化合并信息 }; // 预处理子节点的合并信息 if (newRow.children) { newRow.children = recursionList(newRow.children, newRow); this.preCalculateSpan(newRow.children); // 关键优化点 } return newRow; }); }; const data = this.testData; this.tableData = recursionList(data.data?.everyDetail); this.$nextTick(() => { (this.$refs as any).refTable.doLayout(); }); } /** * 预处理合并信息 (核心优化逻辑) * @param children - 子节点列表,包含需要进行合并处理的行数据。 */ preCalculateSpan(children: TableRow[]) { let pos = 0; // 当前处理的位置指针 while (pos < children.length) { // 遍历所有子节点 const current = children[pos]; // 当前处理的行 if (current.groupNo == null) { // 如果当前行没有组编号,则跳过 pos++; continue; } // 向后查找相同 groupNo 的数量 let sameCount = 1; // 初始化相同组编号的数量为1(包括当前行) for (let i = pos + 1; i < children.length; i++) { // 从下一个元素开始查找 if (children[i].groupNo === current.groupNo) { // 如果发现相同组编号 sameCount++; // 增加计数 } else { break; // 一旦遇到不同的组编号,停止查找 } } // 更新合并信息 current.spanInfo = { rowspan: sameCount, hidden: false }; // 设置当前行为合并起始行 for (let j = pos + 1; j < pos + sameCount; j++) { // 对于后续的相同组编号的行 children[j].spanInfo = { rowspan: 0, hidden: true }; // 标记这些行为隐藏状态,不需要显示 } pos += sameCount; // 移动位置指针,跳过已处理的行 } } /** * 合并方法直接使用预处理结果 * @param param - 包含 row(当前行数据)、column(当前列配置)、columnIndex(当前列索引)的对象。 * @returns 返回一个对象,指定当前单元格的 rowspan 和 colspan。 */ arraySpanMethod({ row, column, columnIndex }: any) { // 只对第一列应用合并规则 if (columnIndex === 0 && row.spanInfo) { return { rowspan: row.spanInfo.rowspan, // 根据预处理结果设置行跨度 colspan: row.spanInfo.hidden ? 0 : 1, // 如果该行被标记为隐藏,则设置 colspan 为 0 }; } // 默认情况下,每个单元格的 rowspan 和 colspan 都为 1 return { rowspan: 1, colspan: 1 }; } } </script>