JavaScript is required

按指定层级展开树图

这个示例将固定子系统层级加载为左到右树图,并允许用户通过悬浮预设选择器切换可见展开深度。它是使用计算得到的 `node.lot.level` 配合 `updateNode()`、`doLayout()` 与视口重适配来全局重置展开状态的聚焦参考。

预设层级展开的从左到右树形图

此示例构建了什么

本示例构建了一个用于子系统地图的只读层级查看器。图谱以 ALTXX 为根节点,按从左到右的树形结构展开,使用带标签的曲线连线和默认的矩形节点,使整体呈现尽量贴近 relation-graph 的基础视觉风格。

用户可以通过一个小型悬浮选择器切换可见的展开深度,可以拖动或最小化辅助窗口,打开共享的画布设置浮层,并将当前图谱下载为图片。这个示例最重要的行为不是在本地分支上点击展开,而是通过一次全局重置,从某个预设层级重新定义整棵树的折叠边界。

数据是如何组织的

数据在 initializeGraph() 内以内联方式声明为一个 RGJsonData 对象。它使用 rootId: '2'、扁平的 nodes 数组,以及带有明确连线 ID 且重复使用 Subsystem 标签的扁平 lines 数组,因此层级关系是通过父子连线编码的,而不是通过嵌套的 children 字段表达。

在调用 setJsonData() 之前没有任何预处理。示例会直接加载这份数据集,然后在后续执行关键状态逻辑:从已挂载图谱中读取计算后的 node.lot.level 值,并据此判断哪些节点会成为当前所选深度下的折叠边界。

在真实应用中,这种结构同样可以表示产品分解、物料清单、部门层级、能力树,或流程分类体系等场景,在这些场景里,源数据本身就已经以 ID 和连线的形式存在。

relation-graph 是如何使用的

页面在 index.tsx 中由 RGProvider 包裹,图谱逻辑则在 MyGraph 内通过 RGHooks.useGraphInstance() 使用。共享的悬浮工具窗口同样使用 relation-graph hooks,因此数据加载、选项修改和图片导出都作用于同一个当前激活的图实例。

graphOptions 配置了一个从左到右的树形图:layout.layoutName = 'tree'from = 'left',并设置 levelGaps = [400, 400, 400, 400]。它还启用了 reLayoutWhenExpandedOrCollapsed = true,将展开控制器放在右侧,使用矩形节点,并通过 RGLineShape.Curve2RGJunctionPoint.lr 绘制带路径文字的左右曲线连接线。

运行时流程比较直接。组件挂载后会调用 setJsonData(),再通过 openByLevel(openLevel) 应用当前预设,然后将视口居中并缩放到适配范围。之后每次深度变化都会复用 getNodes()updateNode()doLayout()moveToCenter()zoomToFit(),从而保持可见展开深度与视口状态同步。

这个演示没有添加自定义节点插槽、连线插槽或画布插槽。它依赖默认的 RelationGraph 渲染器,而 DraggableWindow 提供了悬浮外壳、设置浮层和图片导出控制。局部 SCSS 文件主要是一些选择器脚手架,因此视觉结果大多来自图谱选项而不是自定义样式。

关键交互

  • 预设选择器可以在 Root Node Only 与层级 123 之间切换。
  • 每次切换预设时,都会先展开所有节点,然后折叠那些 Math.abs(node.lot.level) 与所选层级匹配的节点,从而将该层级设为新的全局边界。
  • 每次重置深度后,图谱都会重新布局,并通过 moveToCenter()zoomToFit() 重新适配视口。
  • 悬浮辅助窗口可以被拖动、最小化,也可以切换为画布设置浮层。
  • 共享设置浮层可以修改滚轮行为、修改画布拖拽行为,并将当前渲染的图谱导出为图片。

关键代码片段

下面这个片段展示了图谱如何被配置为一个从左到右的树形图,具有较大的层级间距、曲线连接线以及默认的矩形节点。

const graphOptions: RGOptions = {
    reLayoutWhenExpandedOrCollapsed: true,
    defaultExpandHolderPosition: 'right',
    defaultNodeShape: RGNodeShape.rect,
    defaultNodeBorderWidth: 1,
    defaultLineShape: RGLineShape.Curve2,
    defaultJunctionPoint: RGJunctionPoint.lr,
    defaultLineTextOnPath: true,
    layout: {
        layoutName: 'tree',
        from: 'left',
        levelGaps: [400, 400, 400, 400]
    }
};

下面这个片段展示了该示例如何加载一份内联的扁平层级数据,其中节点和连线记录都是显式定义的。

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '2', text: 'ALTXX' }, { id: '3', text: 'CH2 TTN' }, { id: '4', text: 'CH1 AlCu' },
        { id: '5', text: 'MainFrame' }, { id: '6', text: 'TestMainSys' }, { id: '7', text: 'Automotive Parts' },
        { id: '8', text: 'Automotive Process' }, { id: '9', text: 'Process Quality Inspection' },
        { id: '10', text: 'Zhuoli Manufacturing' }, { id: '11', text: 'Piezoelectric Switch' }
    ],
    lines: [
        { id: 'l1', from: '2', to: '5', text: 'Subsystem' },
        { id: 'l2', from: '2', to: '6', text: 'Subsystem' }
    ]
};

下面这个片段展示了该示例的核心模式:在加载完成后,通过检查计算得到的 node.lot.level 值,并批量更新展开状态来控制深度。

const openByLevel = async (level: number) => {
    graphInstance.getNodes().forEach((node: RGNode) => {
        graphInstance.updateNode(node, { expanded: true });
    });

    graphInstance.getNodes().forEach((node: RGNode) => {
        if (node.lot.level !== undefined && Math.abs(node.lot.level) === level) {
            graphInstance.updateNode(node, { expanded: false });
        }
    });

    await graphInstance.doLayout();
    graphInstance.moveToCenter();
    graphInstance.zoomToFit();
};

下面这个片段展示了主控制界面其实只是悬浮辅助窗口中的一个小型预设选择器,而不是自定义节点插槽或编辑器工具栏。

<DraggableWindow>
    <div className="c-option-name">Expand to level:</div>
    <SimpleUISelect
        data={[
            { value: '0', text: 'Root Node Only' },
            { value: '1', text: '1' },
            { value: '2', text: '2' },
            { value: '3', text: '3' }
        ]}
        currentValue={openLevel.toString()}
        onChange={(newValue: string) => {
            setOpenLevel(parseInt(newValue));
        }}
    />
</DraggableWindow>

下面这个片段展示了共享辅助窗口如何在不改变图数据模型的情况下,把当前画布导出为可下载的图片。

const downloadImage = async () => {
    const canvasDom = await graphInstance.prepareForImageGeneration();
    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
        graphBackgroundColor = '#ffffff';
    }
    const imageBlob = await domToImageByModernScreenshot(canvasDom, {
        backgroundColor: graphBackgroundColor
    });
    if (imageBlob) {
        downloadBlob(imageBlob, 'my-image-name');
    }
};

这个示例的独特之处

expand-gradually 相比,这个示例通过一个预设选择器重新计算整棵树的状态,而不是等待用户点击节点后一次只展开一个被折叠的分支。因此,当需求是“直接跳转到某个可见深度”而不是“逐步打开下一个分支”时,它是一个更直接的参考。

industry-chain 相比,它同样利用了加载完成后计算出的层级元数据,但应用方式要收敛得多。这里的 node.lot.level 只用于定义折叠边界并触发重新布局,而不会用于重新着色节点、分配语义层级样式类,或驱动自定义卡片式层级视图。

tree-datagenerate-image-with-watermark 相比,它不只是被动地渲染树形结构或提供导出导向的脚手架,而是把布局后深度选择作为主要的可复用模式。内联的子系统层级数据、具有 400 像素层级间距的从左到右曲线树形图,以及可拖动的悬浮控制窗口,这种组合在示例集中相对少见,尤其是在没有添加自定义插槽或编辑工具的情况下。

这种模式还适用于哪里

这种模式非常适合只读层级查看器,在这类场景中,用户需要在概览深度与细节深度之间快速切换,而不必手动展开每一个分支。

典型的迁移目标包括子系统分解、产品模块树、服务依赖拆解、政策或标准分类体系、知识大纲以及组织能力地图。其可复用的核心思路是:先让 relation-graph 计算一次结构层级,然后在每次切换预设后,利用这个派生层级重置展开状态、重新布局树结构,并重新适配视口。