在日常的中后台工作中,表格合并是个很常见的需求。本文实现了一种表格合并的方案,让你不再为表格合并的开发而发愁,提升你的工作效率
rowspan & colspan
表格合并主要依赖 rowspan
和 colspan
两个属性
rowspan: 设置单元格可横跨的行数,为0表示横跨到最后一行
colspan: 设置单元格可横跨的列数,为0表示横跨到最后一列
写了一个简单demo,供大家体验
技术栈%20%20Vue3%20+%20ElementPlus
ElementPlus
%20组件%20table
%20进行表格合并需要我们实现%20span-method%20方法。官网也给出了对应的例子。但是问题的难点就在于如何实现%20span-method%20方法,每次不同合并需求,开发同学都得写一遍,有没有简单点的方法呢?
合并方案
接下来看看我们实际面对的场景,并给出一种简单通用的解决方案。
-
需求场景一 业务中有一些表格卡片,这些卡片均是服务端下发,前端进行渲染。每次有新的需求,前端不需要开发,只要服务端下发对应的数据即可。那么该如何实现该需求呢?
- 思考
因为是服务端下发,所以首先需要让服务端知道他自己要下发啥? 我们需要一种数据结构来描述表格的合并信息,并且该数据结构应该非常易于理解(别人一看就懂的那种)
有了服务端下发的合并信息之后,我们需要根据该信息实现 span-method 方法 - 解决方案
- 设计描述表格合并信息的数据结构
以上面的截图为例,我们用我们的数据结构描述一下合并信息// 单元格合并信息 export interface CellMergeInfo { // 行合并信息 row?: { start: number; // 起始行,索引从0开始 end: number; // 终止行,索引从0开始 col: number[]; // 合并列,哪几列需要合并,索引从0开始 }; // 列合并信息 col?: { start: number; // 起始列,索引从0开始 end: number; // 终止列,索引从0开始 row: number[]; // 合并行,哪几行需要合并,索引从0开始 }; } // 表格合并信息 export type TableMergeInfo = CellMergeInfo[]
// 第1,2,3列的第一行到第四行进行合并 const mergeInfo: TableMergeInfo = [ { row: { start: 0, end: 3, col: [0,1,2], } } ]
- 实现 span-method 方法
function mergwTable({ rowIndex, columnIndex }) { const array = mergeInfo; for (let i = 0; i < array.length; i++) { const ele = array[i]; if (ele.row) { const { start, end, col } = ele.row; if (col.includes(columnIndex)) { if (rowIndex === start) { return { rowspan: end - start + 1, colspan: 1, }; } if (rowIndex > start && rowIndex <= end) { return { rowspan: 0, colspan: 0, }; } } } if (ele.col) { const { start, end, row } = ele.col; if (row.includes(rowIndex)) { if (columnIndex === start) { return { rowspan: 1, colspan: end - start + 1, }; } if (columnIndex > start && columnIndex <= end) { return { rowspan: 0, colspan: 0, }; } } } } }
- 思考
-
需求场景二 我们需要统计人的年龄,性别,学历等信息,为了更好的展示效果,我们需要对表格进行一些合并操作。
- 思考
我们还是沿用需求场景一的设计,唯一不同的是当前表格的合并信息是动态的,需要根据数据动态计算合并信息。 - 解决方案
假设服务端给到我们如下数据:
const tableData = [ { name: "张三", // 姓名 age: "22", // 年龄 sex: "男", // 性别 academic: "小学", // 学历 school: "xxx小学", // 学校 idCard: 1, // 身份证 }, { name: "张三", age: "22", sex: "男", academic: "初中", school: "xxx中学", idCard: 1, }, { name: "张三", age: "22", sex: "男", academic: "高中", school: "xxx高中", idCard: 1, }, { name: "李四", age: "32", sex: "男", academic: "小学", school: "xxx小学", idCard: 2, }, { name: "李四", age: "32", sex: "男", academic: "初中", school: "xxx中学", idCard: 2, }, ];
观察数据和我们最终想要的合并效果可以发现,只要同一列相邻两行信息一样&&隶属于同一个主体,我们就可以进行合并),这个主体在我们目前的场景中是人,在其他场景中可能是其他,但是主体都有一个特质,独一无二,可以进行唯一的区分。
// 动态计算行合并信息 // param 需要进行合并的列取的字段 // paramColIndex param所在列的索引 // uniqueParam 用来区分主体的参数,如userId,id 等等 // data 表格渲染的数据 // 比如 createRowMergeInfo("name", 0, "idCard", tableData); function createRowMergeInfo( param: string, paramColIndex: number, uniqueParam: string, data: any[] ) { const result: any[] = []; let startRow = 0; for (let i = 1; i < data.length; i++) { const prev = data[i - 1][param]; const now = data[i][param]; const prevUnique = data[i - 1][uniqueParam]; const nowUnique = data[i][uniqueParam]; // 主体不一致的时候或者上下两行不同时,停止合并 if (now !== prev || prevUnique !== nowUnique) { result.push({ row: { start: startRow, end: i - 1, col: [paramColIndex], }, }); startRow = i; } if (i === data.length - 1) { result.push({ row: { start: startRow, end: i + 1, col: [paramColIndex], }, }); } } return result; } const row1 = createRowMergeInfo("name", 0, "idCard", tableData); const row2 = createRowMergeInfo("age", 1, "idCard", tableData); const row3 = createRowMergeInfo("sex", 2, "idCard", tableData); const mergeInfo = ref<TableMergeInfo>([...row1, ...row2, ...row3]);
- 思考
总结
回顾一下我们的方案:
- 设计了一种数据结构
TableMergeInfo
来语义化的描述表格合并信息 - 根据表格合并信息实现了
span-method
的逻辑 - 对于静态的合并信息,我们可以直接按照需求写死合并信息
- 对于动态的合并信息,我们需要根据数据来动态的计算合并信息,并且给出了方法
createRowMergeInfo
来实现行合并信息的计算;目前没有遇到动态列合并的场景,因此createColMergeInfo
没有给出实现
- 方案优点
- 通用性强,适用多个场景
- 开发同学的工作由实现
span-method
变为如何生成合并信息;对于静态合并,可以直接写死;对于动态合并,使用 createRowMergeInfo 方法来进行生成;大大减轻了开发同学的工作量
写在最后
至此,我们使用该方案完成了所有表格合并的需求,让团队同学可以快速完成该类需求。
虽然使用的是 Vue + ElementPlus
技术栈,但是思路是一样的,只不过写法稍有改动而已。
如还有其他需求场景,欢迎补充。