antd Table 组件动态合并单元格
最近处理table的时候 遇到了要合并同一列的几行的情况,比如第一列的前面三行都是同一个对象的名字,此时合并显示比较妥当,但是数据是后端接口来的,而且可以筛选条件,搜索出来的数据就是动态的长度,可能这次需要合并前面的五行, 在原有的静态数据的合并的基础上,需要加上一个合并的逻辑代码
下面的内容使用
"antd": "^5.7.0",
其他可能方法名略有不同下面分两步讲解:
1、静态数据表格的行合并,讲解Table组件如何使用
rowSpan
进行合并2、动态数据表格的行合并,通过在静态的处理之上加入解析逻辑来实现
本质是对数据源的处理,提前算好需要合并的数量存储到每一行的数据中去,渲染的时候直接通过合并属性赋值即可,这里只是讲解了行的合并,列的合并也是同样的思路
1、静态表格合并行
实现思路:处理columns
中的rowSpan
,遇到需要合并的行,设置rowSpan的数量为需要合并的行总数,然后把后面的rowSpan设置为0(不渲染该位置),否则这个表格就会乱套
下面就是我们合并了 ‘分类’ 列下面的 【1,2】行 ,【4,5,6】 行
import React, { useEffect, useState } from 'react'
import { Table} from 'antd'export default function Index() {const [ tableData, setTableData ] = useState<any>([])const columns = [{title: '分类',dataIndex: 'category',onCell: (row, index) => {// 合并 【1,2】行if (index === 0) {return { rowSpan: 2 }}if (index === 1) {return { rowSpan: 0 }}// 合并【4,5,6】 行if (index === 3) {return { rowSpan: 3 }}if (index === 4) {return { rowSpan: 0 }}if (index === 5) {return { rowSpan: 0 }}}},{title: '名称',dataIndex: 'name',},{title: '评价',dataIndex: 'desc',},];useEffect(() => {// 模拟生命周期接口获取数据let data = [{"category":"水果","name": "桃", "desc": "桃子好吃"},{"category":"水果","name": "梨", "desc": "梨子真好吃"},{"category":"蔬菜","name": "茄子", "desc": "茄子茄子"},{"category":"家禽","name": "牛肉", "desc": "吃不起"},{"category":"家禽","name": "羊肉", "desc": "羊肉好羊肉"},{"category":"家禽","name": "猪肉", "desc": "good"}]setTableData(data)}, [])return (<TableborderedrowKey="name"columns={columns}dataSource={tableData}pagination={false}/>)
}
1、静态表格合并行
根本的原理:处理数据源,合并行列的操作的数量都会在数据源中,提前算好渲染的rowSpan到每一行中就可以
主要看 handleData
函数,作一次循环找到重复类型的第一个row, 并向后一个比较,直到遍历完毕数组:
-
设置
startItem
记录开始计数的对象,并默认rowSpan = 1(自己就是1个) -
取下一项进行比较,当
dataIndex
(这里是类名) 相同则记录startItem.rowSpan + 1
, 否则说明已经没有更多dataIndex
重复的项了,设置新的startItem
为下一项重复以上步骤,直到遍历结束
import React, { useEffect, useState } from 'react'
import { Table} from 'antd'export default function Index() {const [ tableData, setTableData ] = useState<any>([])const columns = [{title: '分类',dataIndex: 'category',onCell: (row:any) => ({ rowSpan: row.rowSpan || 0 })},{title: '名称',dataIndex: 'name',},{title: '评价',dataIndex: 'desc',},];useEffect(() => {// 模拟生命周期接口获取数据、处理数据let data = [{"category":"水果","name": "桃", "desc": "桃子好吃"},{"category":"水果","name": "梨", "desc": "梨子真好吃"},{"category":"蔬菜","name": "茄子", "desc": "茄子茄子"},{"category":"家禽","name": "牛肉", "desc": "吃不起"},{"category":"家禽","name": "羊肉", "desc": "羊肉好羊肉"},{"category":"家禽","name": "猪肉", "desc": "good"}]let res = handleData(data, 'category')setTableData(res)}, [])// 处理数据rowSpan函数const handleData = (array, dataIndex) => {if(array.length === 0) returnlet arr = [...array]// 1、startItem(默认rowSpan = 1)记录开始计数的对象let startItem:any = arr[0]startItem.rowSpan = 1// 2、遍历数组,取下一项进行比较,当name相同则startItem的rowSpan+1, 否则设置新的startItem为下一项arr.forEach((item:any, index) => {let nextItem:any = arr[index+1] || {}if(item[key] === nextItem[key]){startItem.rowSpan++}else{startItem = nextItemstartItem.rowSpan = 1}})return arr}return (<TableborderedrowKey="name"columns={columns}dataSource={tableData}pagination={false}/>)
}
以上都是基础用法,可以给这个函数改写一下,适应更复杂的场景