AntD Table如何使用rowSpan实现嵌套JSON数据跨行合并展示
AntD Table 跨行合并实现方案
问题根因
当前实现直接将嵌套数组的所有内容通过map渲染到单个单元格内,未将嵌套的层级数据拆解为表格独立行,无法触发rowSpan的跨行合并逻辑。AntD Table的单元格合并是通过给单元格DOM设置rowSpan/colSpan属性实现的,必须先将嵌套结构拍平为一维行数据,再给需要合并的单元格配置对应跨行数值,无需显示的重复单元格直接设置rowSpan: 0即可自动隐藏。
具体实现步骤
- 第一步:预处理数据源,拍平嵌套结构
原始数据中单个套餐对应多个运营商,单个运营商对应多档API佣金规则,需要把每一档规则拆成独立的表格行,同时提前计算好每一列需要合并的行数:import { useMemo } from 'react'; import { Table } from 'antd'; import { EditOutlined, DeleteOutlined } from '@ant-design/icons'; // 假设rawDataSource是你传入的原始JSON数据 const flatDataSource = useMemo(() => { const flatList = []; rawDataSource.forEach(packageItem => { // 计算当前套餐总共占多少行 = 旗下所有运营商的API档位数量总和 const packageTotalRows = packageItem.providerOperatorCommissionInfos.reduce( (total, operator) => total + operator.apiSlabs.length, 0 ); let packageRowIndex = 0; // 标记套餐列当前渲染到第几行,第一行设置全量rowSpan,其余行设0 packageItem.providerOperatorCommissionInfos.forEach(operatorItem => { const operatorTotalRows = operatorItem.apiSlabs.length; let operatorRowIndex = 0; // 标记运营商列当前渲染到第几行,第一行设置对应rowSpan,其余行设0 operatorItem.apiSlabs.forEach(slabItem => { flatList.push({ key: `${packageItem.packageName}-${operatorItem.operatorName}-${slabItem.min}-${slabItem.max}`, packageName: packageItem.packageName, operatorName: operatorItem.operatorName, operatorCategory: operatorItem.operatorCategory, slabMin: slabItem.min, slabMax: slabItem.max, commissionAmount: slabItem.commission, amountType: slabItem.amountType, commissionType: slabItem.commissionType, gst: slabItem.gst, tds: slabItem.tds, // 存储各列的合并配置 packageColSpan: packageRowIndex === 0 ? packageTotalRows : 0, operatorColSpan: operatorRowIndex === 0 ? operatorTotalRows : 0, actionColSpan: packageRowIndex === 0 ? packageTotalRows : 0 }); packageRowIndex++; operatorRowIndex++; }) }) }) return flatList; }, [rawDataSource]) - 第二步:改写columns配置,通过
onCell属性设置单元格rowSpan
移除原来render函数里的嵌套map逻辑,每个单元格直接读取当前行的对应字段渲染即可:const columns = [ { title: 'Package Name', dataIndex: 'packageName', align: 'center', onCell: (record) => ({ rowSpan: record.packageColSpan }) }, { title: 'Operator', align: 'center', render: (_, record) => ( <div className="cell-wrap"> <p>{record.operatorName}</p> <p>({record.operatorCategory})</p> </div> ), onCell: (record) => ({ rowSpan: record.operatorColSpan }) }, { title: 'API Slab', align: 'center', render: (_, record) => `[${record.slabMin}-${record.slabMax}]` }, { title: 'Commission', children: [ { title: 'Amount', align: 'center', render: (_, record) => `${record.commissionAmount} ${record.amountType === 'FIXED' ? '₹' : '%'}` }, { title: 'C/S', align: 'center', render: (_, record) => record.commissionType[0] } ] }, { title: 'GST', dataIndex: 'gst', align: 'center' }, { title: 'TDS', dataIndex: 'tds', align: 'center' }, { title: 'Actions', align: 'center', render: () => ( <div className="action-wrap"> <EditOutlined /> <DeleteOutlined style={{ marginLeft: 15 }} /> </div> ), onCell: (record) => ({ rowSpan: record.actionColSpan }) } ]; - 第三步:将拍平后的
flatDataSource传入Table组件的dataSource属性即可<Table columns={columns} dataSource={flatDataSource} bordered />
效果参考
- 预期实现效果:

- 原有错误渲染效果:

注意事项
rowSpan设为0时AntD会自动隐藏当前单元格,不会出现内容重复渲染的问题- 拍平数据时必须给每一行设置全局唯一的
key,否则会出现渲染错位、状态异常的问题 - 不要在单个单元格的render函数里循环渲染多行内容,跨行效果完全由
rowSpan属性控制,每个单元格只需要渲染当前行对应的单份内容
内容的提问来源于stack exchange,提问作者chaoviper
相关产品推荐
相关产品推荐

