React中Mantine Table多级行分组实现问题求助
基于@mantine/core Table的多级通用分组实现
核心思路
通过通用分组函数将原始数据按指定列分层聚合,再递归渲染带跨行属性的分组头和数据行,实现无需硬编码列名的多级分组效果。
实现步骤
1. 通用数据分组函数
支持传入任意数量的分组列,递归生成嵌套分组结构:
// 兼容提示:若环境不支持Object.groupBy,可替换为lodash.groupBy const groupDataByColumns = (data, groupKeys) => { if (!groupKeys.length) return data.map(item => ({ type: 'row', data: item })); const currentKey = groupKeys[0]; const grouped = Object.groupBy(data, item => item[currentKey]); return Object.entries(grouped).map(([groupValue, items]) => ({ type: 'group', key: currentKey, value: groupValue, children: groupDataByColumns(items, groupKeys.slice(1)) })); };
2. 跨行计算工具函数
计算每个分组下的总行数,用于设置分组单元格的rowspan属性:
const calculateRowSpan = (groupItem) => { if (groupItem.type === 'row') return 1; return groupItem.children.reduce((total, child) => total + calculateRowSpan(child), 0); };
3. 递归渲染分组与数据行
根据分组结构递归生成Table行,自动处理分组单元格的跨行和缩进:
const renderRows = (groupedData, allColumns, groupKeys, depth = 0) => { return groupedData.flatMap(item => { if (item.type === 'row') { return ( <Table.Tr key={item.data.id}> {allColumns.map(col => groupKeys.includes(col.key) ? <Table.Td key={col.key}></Table.Td> : <Table.Td key={col.key}>{item.data[col.key]}</Table.Td> )} </Table.Tr> ); } const rowSpan = calculateRowSpan(item); const groupRow = ( <Table.Tr key={`group-${item.value}-${depth}`}> {allColumns.map((col, idx) => { const groupColIndex = groupKeys.indexOf(item.key); if (idx === groupColIndex) { return ( <Table.Td key={col.key} rowSpan={rowSpan} style={{ fontWeight: 'bold', paddingLeft: `${depth * 20}px`, verticalAlign: 'top' }} > {item.value} </Table.Td> ); } return <Table.Td key={col.key}></Table.Td>; })} </Table.Tr> ); const childRows = renderRows(item.children, allColumns, groupKeys, depth + 1); return [groupRow, ...childRows]; }); };
4. 完整组件示例
import { Table } from '@mantine/core'; // 示例数据 const sampleData = [ { id: 1, state_name: 'Karnataka', district: 'Banglore', gender: 'Male' }, { id: 2, state_name: 'Karnataka', district: 'Banglore', gender: 'Female' }, { id: 3, state_name: 'Karnataka', district: 'Mysore', gender: 'Male' }, { id: 4, state_name: 'Karnataka', district: 'Mysore', gender: 'Female' }, { id: 5, state_name: 'Karnataka', district: 'Manglore', gender: 'Male' }, { id: 6, state_name: 'Karnataka', district: 'Manglore', gender: 'Female' }, { id: 7, state_name: 'Maharashtra', district: 'Mumbai', gender: 'Male' }, { id: 8, state_name: 'Maharashtra', district: 'Mumbai', gender: 'Female' }, ]; // 通用分组函数 const groupDataByColumns = (data, groupKeys) => { if (!groupKeys.length) return data.map(item => ({ type: 'row', data: item })); const currentKey = groupKeys[0]; const grouped = Object.groupBy(data, item => item[currentKey]); return Object.entries(grouped).map(([groupValue, items]) => ({ type: 'group', key: currentKey, value: groupValue, children: groupDataByColumns(items, groupKeys.slice(1)) })); }; // 计算跨行数 const calculateRowSpan = (groupItem) => { if (groupItem.type === 'row') return 1; return groupItem.children.reduce((total, child) => total + calculateRowSpan(child), 0); }; // 递归渲染行 const renderRows = (groupedData, allColumns, groupKeys, depth = 0) => { return groupedData.flatMap(item => { if (item.type === 'row') { return ( <Table.Tr key={item.data.id}> {allColumns.map(col => groupKeys.includes(col.key) ? <Table.Td key={col.key}></Table.Td> : <Table.Td key={col.key}>{item.data[col.key]}</Table.Td> )} </Table.Tr> ); } const rowSpan = calculateRowSpan(item); const groupRow = ( <Table.Tr key={`group-${item.value}-${depth}`}> {allColumns.map((col, idx) => { const groupColIndex = groupKeys.indexOf(item.key); if (idx === groupColIndex) { return ( <Table.Td key={col.key} rowSpan={rowSpan} style={{ fontWeight: 'bold', paddingLeft: `${depth * 20}px`, verticalAlign: 'top' }} > {item.value} </Table.Td> ); } return <Table.Td key={col.key}></Table.Td>; })} </Table.Tr> ); const childRows = renderRows(item.children, allColumns, groupKeys, depth + 1); return [groupRow, ...childRows]; }); }; export default function GroupedTable() { // 定义所有列(分组列+数据列) const columns = [ { key: 'state_name', label: 'State' }, { key: 'district', label: 'District' }, { key: 'gender', label: 'Gender' }, ]; // 指定分组顺序,支持任意数量的分组列 const groupColumns = ['state_name', 'district']; // 生成分组数据 const groupedData = groupDataByColumns(sampleData, groupColumns); return ( <Table> <Table.Thead> <Table.Tr> {columns.map(col => ( <Table.Th key={col.key}>{col.label}</Table.Th> ))} </Table.Tr> </Table.Thead> <Table.Tbody>{renderRows(groupedData, columns, groupColumns)}</Table.Tbody> </Table> ); }
关键特性
- 通用分组:仅需修改
groupColumns数组即可实现任意层级的分组,无需硬编码列名 - 自动跨行:通过计算分组总行数,自动设置分组单元格的
rowspan属性 - 样式可定制:分组头的缩进、字体粗细等样式可根据需求调整
内容的提问来源于stack exchange,提问作者Aishwarya J S
相关产品推荐
相关产品推荐

