最近处理table的时候 遇到了要合并同一列的几行的情况,比如第一列的前面三行都是同一个对象的名字,此时合并显示比较妥当,但是数据是后端接口来的,而且可以筛选条件,搜索出来的数据就是动态的长度,可能这次需要合并前面的五行, 在原有的静态数据的合并的基础上,需要加上一个合并的逻辑代码
下面的内容使用
"antd": "^5.7.0",
其他可能方法名略有不同下面分两步讲解:
1、静态数据表格的行合并,讲解Table组件如何使用
rowSpan
进行合并2、动态数据表格的行合并,通过在静态的处理之上加入解析逻辑来实现
本质是对数据源的处理,提前算好需要合并的数量存储到每一行的数据中去,渲染的时候直接通过合并属性赋值即可,这里只是讲解了行的合并,列的合并也是同样的思路
实现思路:处理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 (
<Table
bordered
rowKey="name"
columns={columns}
dataSource={tableData}
pagination={false}
/>
)
}
根本的原理:处理数据源,合并行列的操作的数量都会在数据源中,提前算好渲染的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) return
let 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 = nextItem
startItem.rowSpan = 1
}
})
return arr
}
return (
<Table
bordered
rowKey="name"
columns={columns}
dataSource={tableData}
pagination={false}
/>
)
}
以上都是基础用法,可以给这个函数改写一下,适应更复杂的场景