产业链层级图谱
这个示例将半导体产业链层级渲染为自上而下的 relation-graph 树,并使用自定义卡片节点。它利用计算得到的节点层级控制展开深度、卡片样式和语义标题,并提供带小地图开关、画布设置和图片导出的悬浮辅助面板。
带预设层级深度控制的产业链层级查看器
这个示例构建了什么
这个示例构建了一个用于半导体分类体系的只读产业链查看器。画布以一棵自上而下的树形结构打开,根节点是 Semiconductor,然后向下分支到原材料、设计、制造、测试、封装和设备等领域。
用户可以通过一个悬浮控制窗口显示不同的层级深度,切换缩略图覆盖层,打开共享画布设置,并将图谱导出为图片。最重要的视觉设计点在于,这个图谱并不只是按深度展开:它还会根据完成布局后计算出的层级,对每个节点卡片重新着色并应用不同样式。
数据如何组织
数据是在 initializeGraph() 内部声明的单个内联 RGJsonData 对象。它采用经典的树形结构:一个 rootId、一个扁平的 nodes 数组,以及一个将每个父节点连接到其子节点的扁平 lines 数组。
在调用 setJsonData() 之前几乎没有任何预处理。唯一的转换是对 lines 执行一次 map(),当某条连线还没有 id 时为其分配一个生成的 id。真正重要的结构性处理发生在图谱完成布局之后:示例会读取 node.lot.level,然后就地更新每个节点的 expanded、color 和 className 字段。
在真实应用中,同样的数据形态可以表示供应链、产品分类体系、制造能力地图、政策拆解结构,或任何其他源数据是父子关系而不是自由网络结构的多层级业务层次。
relation-graph 的使用方式
页面在 index.tsx 中通过 RGProvider 包裹,而实际的图谱逻辑则通过 RGHooks.useGraphInstance() 获取共享实例。这个 hook 在悬浮工具组件中也被复用,因此示例可以在同一个图谱上下文中完成数据加载、选项变更和当前画布导出。
图谱本身被配置为一棵自上而下的树。graphOptions 设置了 layout.layoutName = 'tree'、from = 'top'、120 的垂直间距、居中对齐、矩形节点、底部展开控制点,并通过 RGLineShape.Curve2 和 RGJunctionPoint.tb 使用上下弯曲连线。reLayoutWhenExpandedOrCollapsed 被启用,这与预设深度的行为一致。
这个示例按顺序使用了多个图谱实例 API。在挂载时,它会调用 loading()、setJsonData()、openByLevel(1)、moveToCenter()、zoomToFit() 和 clearLoading()。之后,深度切换依赖 getNodes()、updateNode() 和 doLayout(),以保持可见层级与样式状态同步。
两个插槽定义了展示层。RGSlotOnNode 用自定义卡片替换默认节点主体,RGSlotOnView 则在启用缩略图开关时按条件挂载 RGMiniView。节点插槽再次使用 node.lot.level 来选择诸如 Industry Chain Name、Industry Link 和 Subsection Link 这样的头部标签。
样式被拆分为运行时节点更新和 SCSS 覆盖。运行时代码写入每个节点的 color 和 className,而 my-relation-graph.scss 读取 var(--rg-node-color) 来绘制卡片边框、头部、正文文字和展开按钮。按层级区分的 class 随后会针对不同层级调整宽度和排版。
悬浮面板来自共享的 DraggableWindow 组件。这个辅助组件增加了拖拽、最小化、设置覆盖层、通过 setOptions() 切换滚轮与拖拽模式,以及在 DOM 截图步骤前后分别调用 prepareForImageGeneration() 和 restoreAfterImageGeneration() 来导出图片。
关键交互
- 图谱会在挂载时自动加载,初始先展开到一个计算出的深度层级,然后将视图居中并缩放到适配画布。
- 预设选择器暴露了三个深度命令,标签分别是
2、3和4。在代码里,这些按钮传入openByDeep()的参数是1、2和3,因此这些标签只是展示文本,而不是内部原始层级编号。 - 每次深度变化都会复用同一套布局后元数据,用于展开或折叠节点、分配层级颜色、分配层级 class、重新执行布局,并再次适配视口。
- 缩略图复选框会在同一个画布上切换
RGMiniView覆盖层,而不是打开一个独立的导航器。 - 悬浮辅助窗口可以被拖动、最小化、切换到画布设置覆盖层,并用于将当前图谱下载为图片。
关键代码片段
这个片段展示了示例如何使用一个内联层级数据集,并在加载前补齐缺失的连线 ID。
const myJsonData: RGJsonData = {
rootId: '0',
nodes: [
{ id: '0', text: 'Semiconductor' },
{ id: '1', text: 'Final Test' },
// ... many semiconductor categories omitted ...
],
lines: [
{ text: '', from: '0', to: '1' },
{ text: '', from: '1', to: '2' },
// ... hierarchy links omitted ...
].map((line, index) => ({ ...line, id: line.id || `line-${index}` }))
};
这个片段展示了加载顺序:先挂载数据,然后执行基于层级的展开,最后才将视图重新居中并适配。
graphInstance.loading();
await graphInstance.setJsonData(myJsonData);
await openByLevel(1);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
graphInstance.clearLoading();
这个片段展示了该示例的核心技巧:计算得到的布局深度会成为展开状态、颜色和 CSS class 的统一事实来源。
const nodes = graphInstance.getNodes();
const levelColors = ['#5b05f1', '#FD8B37', '#9b9903', '#247c02'];
nodes.forEach(node => {
const nodeLevel = Math.abs(node.lot.level || 0);
graphInstance.updateNode(node, {
color: levelColors[nodeLevel] || '#247c02',
expanded: nodeLevel < level,
className: 'my-industy-node-level-' + nodeLevel
});
});
await graphInstance.doLayout();
这个片段展示了自定义节点插槽如何把同一份图谱数据渲染成带层级标签的业务卡片,而不是默认的文本节点。
<RGSlotOnNode>
{({ node }: RGNodeSlotProps) => {
const level = node.lot?.level || 0;
let headerText = "Product Type";
if (level === 0) headerText = "Industry Chain Name";
else if (level === 1) headerText = "Industry Link";
else if (level === 2) headerText = "Subsection Link";
return (
<div className={`my-industy-node my-industy-node-level-${level}`}>
这个片段展示了缩略图如何被视为视图级覆盖层,并且仅在控制面板启用时才挂载。
<SimpleUIBoolean
currentValue={showMiniView}
onChange={setShowMiniView}
label="Show graph minimap"
/>
// ...
<RGSlotOnView>
{showMiniView && <RGMiniView />}
</RGSlotOnView>
这个片段展示了 SCSS 如何消费运行时颜色和 class 元数据来为卡片设定样式。
.my-industy-node {
width: 160px;
border-radius: 5px;
background-color: #ffffff;
border: var(--rg-node-color) solid 1px;
.my-card-header {
background-color: var(--rg-node-color);
color: #fff;
}
.my-card-body {
color: var(--rg-node-color);
}
}
这个示例的独特之处
与附近的 open-by-level、investment、layout-tree 和 layout-folder2 等示例相比,这个示例异常专注于构建一个单一的业务阅读界面,而不是用于布局实验或渐进式数据增长。图谱始终保持在同一种自上而下的树形配置中,并利用这个稳定布局来清晰地呈现一份大型内联分类体系。
它的主要区别在于把三种行为组合成了一个统一模式。第一,深度变化是预设命令,而不是手动逐分支导航。第二,同一个计算得到的 node.lot.level 会同时驱动展开状态、颜色和 CSS class。第三,查看器可以在不改变核心层级模型的前提下按需加入 RGMiniView。
这使它不同于 open-by-level,后者虽然也共享基于层级的展开模式,但并没有把层级元数据转化为自定义的语义卡片设计。它也不同于 investment,后者的主要重点是惰性加载的股权展开;同样也不同于 layout-tree 或 layout-folder2,因为那些示例强调的是布局调优,而不是一个固定的产业分类展示界面。
这种模式还适用于哪里
这种模式同样适用于其他只读层级查看器,在这些场景中,用户需要在总览深度和细节深度之间快速切换,而不必编辑数据集。
可能的迁移目标包括供应商层级地图、制造产线能力拆解、多层级产品目录、法规或标准分类体系,以及组织知识地图。在每一种情况下,最关键的可复用思路都是先让 relation-graph 计算一次树层级,再利用这个派生层级统一协调展开规则、语义标签和视觉重点。