展示更多(分页)
基于分页的节点加载 - 通过"加载更多"功能动态分批加载子节点
基于分页的节点加载
功能概述
本示例演示了在树形关系图中动态加载子节点的高级分页机制。当展开类别节点时,它会加载固定数量的子节点(可通过 pageSize 配置),并提供"加载更多"按钮从服务器获取额外数据。这种方法优化了初始渲染性能,同时允许用户按需渐进式加载大数据集。
核心特性实现
动态数据分页加载
核心分页逻辑在 onNodeExpand 事件处理器和 loadNextPageData 函数中实现:
const onNodeExpand = async (node: RGNode, e: RGUserEvent) => {
if (node.data.childrenLoaded) {
console.log('该节点的子节点已经加载过');
return;
}
node.data.childrenLoaded = true;
const myType = node.data.myType;
const fromIndex = 0;
await loadNextPageData(myType, fromIndex);
};
const loadNextPageData = async (myType: string, fromIndex: number) => {
const typeNode = graphInstance.getNodes().find(n => n.data.myType === myType);
const { currentPageItems, total } = await fetchMockDataFromRemoteServer(myType, pageSize, fromIndex);
const currentPageGraphJsonData: RGJsonData = { nodes: [], lines: [] };
for (const entItem of currentPageItems) {
currentPageGraphJsonData.nodes.push({
id: entItem.companyId,
text: entItem.companyName,
data: { ownerType: myType }
});
currentPageGraphJsonData.lines.push({
from: typeNodeId,
to: entItem.companyId
});
}
updateLoadMoreButtonNode(typeNodeId, myType, fromIndex + currentPageItems.length, total, currentPageGraphJsonData);
// 将新节点定位在父节点位置以实现平滑动画
currentPageGraphJsonData.nodes.forEach((n: JsonNode) => {
n.x = typeNode.x;
n.y = typeNode.y;
});
graphInstance.addNodes(currentPageGraphJsonData.nodes);
graphInstance.addLines(currentPageGraphJsonData.lines);
await graphInstance.sleep(400); // 等待渲染完成
await graphInstance.doLayout();
};
关键要点:
- 延迟加载:子节点仅在父节点展开时加载
- 服务器分页:使用
pageSize和fromIndex参数分块获取数据 - 平滑定位:新节点初始定位在父节点坐标,实现自然的展开动画
- 渲染等待:使用
sleep(400)确保节点在布局计算前完成渲染
动态"加载更多"按钮管理
updateLoadMoreButtonNode 函数根据剩余项动态添加或移除特殊按钮节点:
const updateLoadMoreButtonNode = (
typeNodeId: string,
myType: string,
fromIndex: number,
total: number,
currentPageGraphJsonData: RGJsonData
) => {
const loadNextPageButtonNodeId = `${myType}-next-button`;
graphInstance.removeNodeById(loadNextPageButtonNodeId);
const remainingItemCount = total - fromIndex + 1;
if (remainingItemCount > 0) {
currentPageGraphJsonData.nodes.push({
id: loadNextPageButtonNodeId,
text: `Load More(${remainingItemCount})`,
data: { myType: 'more-btn', loadType: myType, fromIndex: (fromIndex + 1) }
});
currentPageGraphJsonData.lines.push({
from: typeNodeId,
to: loadNextPageButtonNodeId
});
}
};
自定义节点插槽实现
NodeSlot 组件使用自定义样式和点击处理器处理不同节点类型:
const NodeSlot: React.FC<RGNodeSlotProps & {loadNextPage: (n: RGNode) => void|Promise<void>}> = ({ node, loadNextPage }) => {
const myType = node.data.myType;
const buttonTypes = ['investment-button', 'shareholder-button', 'historical-investment-button', 'historical-shareholder-button'];
return (
<>
{buttonTypes.includes(myType) && (
<div className="my-node my-button-node">{node.text}</div>
)}
{myType === 'root' && (
<div className="my-node my-root">{node.text}</div>
)}
{myType === 'more-btn' && (
<div className="my-node more-btn px-2" onClick={() => {loadNextPage(node)}}>
{node.text}
</div>
)}
{!myType && (
<div className="my-node">{node.text}</div>
)}
</>
);
};
可配置的页面大小
用户可以通过数字输入调整每页加载的项数:
const [pageSize, setPageSize] = React.useState<number>(10);
<SimpleUINumber
currentValue={pageSize}
min={1}
max={200}
onChange={(newValue: number) => { setPageSize(newValue); }}
/>
树形布局配置
使用带有正交线条的树形布局实现清晰的层级结构:
const graphOptions: RGOptions = {
defaultExpandHolderPosition: 'right',
defaultNodeShape: RGNodeShape.rect,
defaultNodeBorderWidth: 0,
defaultLineShape: RGLineShape.StandardOrthogonal,
defaultPolyLineRadius: 5,
defaultJunctionPoint: RGJunctionPoint.lr,
reLayoutWhenExpandedOrCollapsed: true,
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 100,
treeNodeGapV: 10,
alignItemsX: 'start',
alignParentItemsX: 'end',
}
};
创意使用场景
企业投资网络分析: 为拥有数千家子公司和投资的大型公司渐进式加载投资关系。首先加载近期投资,然后使用分页按需加载历史数据。
社交网络连接: 分页显示用户的连接,每批加载 50-100 个好友/关注者。"加载更多"按钮显示剩余连接数,允许用户探索大型网络而不会有初始性能开销。
家谱树可视化: 渲染每个祖先可以有数百个后代的家族树。使用分页首先加载直系子女,然后随着用户探索不同分支,渐进式加载孙辈和更远的后代。
电商产品关联: 显示目录商品的基于分类的关联产品关系。每批加载各分类(如"配件"、“相似产品”)的产品,在加载按钮中显示剩余数量。
文档引用图谱: 可视化学术论文或法律文档中的引用网络。首先加载直接引用,然后逐页加载间接引用,允许研究人员探索复杂的文档关系网。