最近,项目上遇到了复杂表格的需求,在消耗了大量脑细胞后,终于实现了这一需求,现在就把我的实现思路和代码总结一下,为需要的小伙伴做个参考。
产品需求
- 合并相同数据的单元格(仅合并列);
- 内容不固定,需要动态合并;
- 指定合并列;
- 数据相同但所属父级不一致的单元格不合并;
- 空值不合并;
由于保密要求,所以不展示相关截图和数据,请大家见谅,这里只展示主要代码(代码注释已写,大家直接看就可以)。
模拟实际表格数据格式:
tableColumn: [ { prop: 'School', label: '学校' }, { prop: 'Grade', label: '年级' }, { prop: 'Class', label: '班级' }, { prop: 'Name', label: '姓名' }, { prop: 'Chinese', label: '中文' }, { prop: 'Math', label: '数学' }, { prop: 'English', label: '英文' } ], tableData: [ { School: '第一小学', Grade: '1 年级', Class: '1 班', Name: '张三', Chinese: '90', Math: '100', English: '80' }, ... ]
关键代码
抽成共用方法放到 utils 下,文件名根据需要自己起:
/** * 分析每一列,找出所有【列】可合并(数据相同)的单元格 * @param {Array} tableData 表数据 * @param {Array} tableColumn 表字段/表头 * @param {Array} mergeCols 指定合并哪些列(字段) * @returns */ export const getMergeCells = (tableData = [], tableColumn = [], mergeCols = []) => { const fields = tableColumn?.map(v => v.prop) const array = [] if (!tableData?.length || !tableColumn?.length || !mergeCols?.length) return // 倒叙遍历行(方便统计合并列单元格数至最上方,避免表格塌陷) for (let row = tableData.length - 1; row >= 0; row--) { array[row] = [] for (let col = 0; col < fields.length; col++) { // 1.最后一行单元格不合并(初始无可对比数据) // 2.不在指定列(mergeCols)的单元格不合并 // 3.空值不合并 if (row === tableData.length - 1 || !mergeCols.includes(fields[col]) || !tableData[row][fields[col]]) { array[row][col] = [1, 1] continue } // 4.数据相同但所属父级不一致的单元格不合并 const parentFields = mergeCols.slice(0, col) // 在指定合并列中找出所有父级 if (mergeCols.includes(fields[col]) && parentFields?.includes(fields[col - 1])) { const currentParents = parentFields.map(field => tableData[row][field]) // 当前单元格所有父级 const nextRowParents = parentFields.map(field => tableData[row + 1][field]) // 下一行单元格所有父级 if (currentParents?.toString() !== nextRowParents?.toString()) { array[row][col] = [1, 1] continue } } // 5.合并相同数据的单元格 if (tableData[row][fields[col]] === tableData[row + 1][fields[col]]) { const beforeCell = array[row + 1][col] array[row][col] = [1 + beforeCell[0], 1] beforeCell[0] = 0 beforeCell[1] = 0 } else { array[row][col] = [1, 1] // 否则不合并 } } } // console.log(array, 'array') return array }
优点:
- 可动态指定需要合并的列
- 空值(null、undefined、空字符串、0)不会被合并
- 数据相同但所属父级不相同的不会被合并,并且不用手动指定父级
- 支持分页
缺点:
- 不支持相邻的行合并(大家有需要可自行修改)
如何使用
在 computed 下:
computed: { // 获取所有单元格合并数据 spanArr() { if (!this.tableColumn.length) return [] const mergeCols = ['School', 'Grade'] // 需要合并的列(字段) return getMergeCells(this.tableData, this.tableColumn, mergeCols) } },
methods 方法:
methods: { // 表数据合并 objectSpanMethod({ row, column, rowIndex, columnIndex }) { return this.spanArr[rowIndex][columnIndex] } }
el-table 组件主要靠 span-method 方法实现合并,所以将 objectSpanMethod 方法写在 span-method 上,即可实现。
注意:在实际异步获取的数据中,如果后端没有将数据进行排序返回,前端获取数据后需要对其进行排序处理,这样相邻的数据才能更好合并。
比如,我就是上面情况,后端返回数据后,我是先在 then 里面对需要合并的数据进行排序,然后在合并列:
await getUserUtilizationRate({date: this.dateValue, staff, appeal_type, task_id}).then((res) => { let sortedData = res.data.sort((a, b) => { // 先按 staff 排序 if (a.staff < b.staff) return -1; if (a.staff > b.staff) return 1; // staff 相同,按 appeal_type 排序 if (a.appeal_type < b.appeal_type) return -1; if (a.appeal_type > b.appeal_type) return 1; // appeal_type 相同,按 detail_appeal_type 排序 if (a.detail_appeal_type < b.detail_appeal_type) return -1; if (a.detail_appeal_type > b.detail_appeal_type) return 1; return 0; }) this.tableData = sortedData })
结语
以上就是 vue 结合 elementui 的 el-table 实现动态合并可指定列数据相同的单元格实现方法的详细内容,希望对大家有用。