有时候需要合并 Antd Table 里面的行,并且是在不能预知哪几行需要合并的情况下。这个时候需要根据待渲染的数据,动态的合并,相邻的若干行。现在有这样一个表格:
这是代码:
import React from "react";
import "./styles.css";
import { Table } from "antd";
import "antd/dist/antd.css";
const dataSource = [
{
name: "小明",
exam: "月考",
chinese: 100,
math: 90,
english: 87
},
{
name: "小明",
exam: "期中",
chinese: 98,
math: 100,
english: 88
},
{
name: "小明",
exam: "期末",
chinese: 100,
math: 100,
english: 100
},
{
name: "小红",
exam: "月考",
chinese: 100,
math: 100,
english: 100
},
{
name: "小红",
exam: "期中",
chinese: 100,
math: 100,
english: 100
},
{
name: "小丽",
exam: "月考",
chinese: 100,
math: 100,
english: 100
}
];
const columns = [
{
key: "name",
title: "姓名",
dataIndex: "name",
render: (text) => <span>{text}</span>
},
{
key: "exam",
title: "考试",
dataIndex: "exam",
render: (text) => <span>{text}</span>
},
{
key: "chinese",
title: "语文",
dataIndex: "chinese",
render: (text) => <span>{text}</span>
},
{
key: "math",
title: "数学",
dataIndex: "math",
render: (text) => <span>{text}</span>
},
{
key: "english",
title: "英语",
dataIndex: "english",
render: (text) => <span>{text}</span>
}
];
export default function App() {
return (
<div className="App">
<h2>学期考试成绩表</h2>
<Table
bordered={true}
dataSource={dataSource}
columns={columns}
footer={null}
/>
</div>
);
}
我们现在需要合并表格左边姓名相同的行,如果数据是从服务器拉取的,我们并不知道有哪几个行需要合并,因此只能动态设置 Table 的 rowSpan、colSpan 来实现。
假设数据已经排好序了,也就是在这个表格里面姓名相同的行已经排在相邻的地方,下面我们实现一个 mergeRows
方法来实现合并,这个方法在需要合并的行的数据中加入 rowSpan
、colSpan
字段并设置相应的值,然后渲染的时候我们在 columns 中使用行的 rowSpan
、colSpan
:
我们在 App 添加一个方法:
const mergeRows = (rows, key) => {
rows[0].rowSpan = 1;
let idx = 0;
return rows.slice(1).reduce(
(mergedRows, item, index) => {
if (item[key] === mergedRows[idx][key]) {
mergedRows[idx].rowSpan++;
item.colSpan = 0;
} else {
item.rowSpan = 1;
idx = index + 1;
}
return [...mergedRows, item];
},
[rows[0]]
);
};
然后 Table
组件里的 dataSource
传入 mergeRows(dataSource, "name")
:
<Table
bordered={true}
- dataSource={dataSource}
+ dataSource={mergeRows(dataSource, "name")}
columns={columns}
footer={null}
/>
还需要重写 columns ,改变 name 列的 render 方法:
const columns = [
{
key: "name",
title: "姓名",
dataIndex: "name",
- render: (text) => <span>{text}</span>
+ render: (text, record) => ({
+ children: <div style={{ fontWeight: "bold" }}>{text}</div>,
+ props: {
+ rowSpan: record.rowSpan,
+ colSpan: record.colSpan
+ }
+ })
},
{
key: "exam",
title: "考试",
dataIndex: "exam",
render: (text) => <span>{text}</span>
},
{
key: "chinese",
title: "语文",
dataIndex: "chinese",
render: (text) => <span>{text}</span>
},
{
key: "math",
title: "数学",
dataIndex: "math",
render: (text) => <span>{text}</span>
},
{
key: "english",
title: "英语",
dataIndex: "english",
render: (text) => <span>{text}</span>
}
];
现在渲染出来的表格就合并行了:
如果数据没有实现做好排序,还可以用下面这个 classifyRows
方法排序归类好需要合并的行,之后再调用 mergeRows
合并行:
const classifyRows = (data, classifyRule) => {
const keys = classifyRule
let classified = data.slice(1).reduce((ordered, row) => {
// 从已经排好序的行中寻找一行 row1,如果这行 row1 的 keys 值分别等于当前遍历到的行的 keys 值,就把当前遍历到的这一行插入到 row1 之后
// 否则把当前遍历到的行放在所有排好序的行的最后一行
const index = ordered.findIndex(orderedRow =>
keys.reduce(
(boolean, curKey) => boolean && orderedRow[curKey] === row[curKey],
true
)
)
if (index !== -1) {
return [...ordered.slice(0, index + 1), row, ...ordered.slice(index + 1)]
} else { return [...ordered, row] }
}, [data[0]])
return classified
}
现在我们把 dataSource
顺序打乱,比如把 小明
的一行数据放到 小红
的两条数据之间:
const dataSource = [
{
name: "小明",
exam: "月考",
chinese: 100,
math: 90,
english: 87
},
{
name: "小明",
exam: "期末",
chinese: 100,
math: 100,
english: 100
},
{
name: "小红",
exam: "月考",
chinese: 100,
math: 100,
english: 100
},
{
name: "小明",
exam: "期中",
chinese: 98,
math: 100,
english: 88
},
{
name: "小红",
exam: "期中",
chinese: 100,
math: 100,
english: 100
},
{
name: "小丽",
exam: "月考",
chinese: 100,
math: 100,
english: 100
}
];
如果不用 classifyRows
归类排序,那么渲染出来的表格就是:
小明
的第三条数据没有被合并到小明
的前两条数据中, 而且 小红
的两条本该被合并的数据也没有合并,下面我们在 mergeRows
之前使用 classifyRows
:
<Table
bordered={true}
- dataSource={mergeRows(dataSource, "name")}
+ dataSource={
+ mergeRows(
+ classifyRows(dataSource, ['name']), 'name')
+ }
columns={columns}
footer={null}
/>
可以发现这条数据会被合并到小明
的前两条数据中:
演示项目地址: codesandbox.io/s/pensive-s…