[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:initially-collapsed-tree":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 构建一个初始折叠并可渐进展开的树\n\n## 这个示例构建了什么\n\n这个示例构建了一个小型的从左到右树形查看器，并且初始状态下部分分支处于折叠状态。用户首先看到的是一个紧凑的橄榄黄色层级结构，包含矩形节点、正交连线以及位于右侧的展开控件；随后可以通过点击已折叠节点来显示隐藏分支。这个示例的重点并不是通用的树渲染，而是一套明确的启动流程：一次性加载完整数据集，在代码中折叠选定节点，然后让图谱以渐进披露浏览器的方式运行。\n\n## 数据是如何组织的\n\n图数据以内联 `RGJsonData` 的形式声明，包含 `rootId: 'a'`、一个扁平的 `nodes` 数组以及一个扁平的 `lines` 数组。这个示例没有使用嵌套的 `children`；每一条关系都通过明确的 `from` 和 `to` id 表达，因此按节点 id 执行折叠步骤会很容易定位。\n\n在 `setJsonData()` 之后有一个重要的预处理步骤：代码使用 `getNodeById()` 查找 `a`、`b` 和 `c`，并在第一次稳定布局之前先折叠这些节点。这意味着查看器的初始状态并不是原始的完整可见图，而是一个经过刻意设计的信息披露状态。\n\n在生产场景中，同样的数据结构可以表示组织分支、课程大纲、产品分类树、故障排查流程，或者任何完整图谱已经已知、但不应一次性全部展示出来的层级结构。\n\n## relation-graph 是如何使用的\n\n该图使用 `layoutName: 'tree'` 并设置 `from: 'left'`，因此层级会以从左到右的方式水平阅读。`defaultLineShape` 为 `RGLineShape.StandardOrthogonal`，`defaultJunctionPoint` 为 `RGJunctionPoint.lr`，`defaultExpandHolderPosition` 为 `'right'`，从而使整体视觉语言与水平树保持一致。示例还为节点和连线设置了匹配的橄榄黄色默认样式，并通过本地 SCSS 覆盖确保节点文字在较深的填充色上仍保持白色。\n\n`RGHooks.useGraphInstance()` 是主要的集成入口。示例通过图实例加载内联数据、按 id 定位特定节点、折叠这些节点、执行 `doLayout()`、将结果居中、缩放到适应视口，并在后续的 `onNodeClick` 中展开已折叠分支。由于图选项中启用了 `reLayoutWhenExpandedOrCollapsed`，因此在点击处理器内部不需要为分支展开编写手动布局代码。\n\n这里没有使用自定义节点、连线、画布或视口插槽，也没有编辑工作流。相反，外围 UI 来自一个共享的 `DraggableWindow` 辅助组件。该辅助组件增加了一个浮动说明窗口、一个基于 `RGHooks.useGraphStore()` 的设置面板、对滚轮和拖拽行为的实时 `setOptions()` 调用，以及一个基于 `prepareForImageGeneration()` 和 `restoreAfterImageGeneration()` 的导出流程。这些工具很有用，但它们属于共享脚手架，而不是这个示例最核心的独特行为。\n\n## 关键交互\n\n- 只有当 `node.expanded === false` 时点击节点才会展开它，因此这个示例的行为是单向展开查看器，而不是完整的点击切换树。\n- 由于启用了 `reLayoutWhenExpandedOrCollapsed`，树在展开或折叠状态变化后会自动重新排布。\n- 浮动辅助窗口可以拖动或最小化，因此说明和工具始终可用，同时不会持续占据画布空间。\n- 设置面板可以在运行时切换滚轮和拖拽行为，并将当前图谱下载为图片。\n\n## 关键代码片段\n\n这段代码展示了该 demo 被配置为一个水平正交树，并在右侧使用内置展开控件。\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: false,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n    },\n    defaultNodeShape: RGNodeShape.rect,\n    defaultLineShape: RGLineShape.StandardOrthogonal,\n    defaultJunctionPoint: RGJunctionPoint.lr,\n    defaultExpandHolderPosition: 'right',\n    reLayoutWhenExpandedOrCollapsed: true,\n```\n\n这段代码说明完整数据集会先被加载，然后再按节点 id 有选择地折叠节点。\n\n```tsx\nawait graphInstance.setJsonData(myJsonData);\n['a', 'b', 'c'].forEach(nodeId => {\n    const node = graphInstance.getNodeById(nodeId);\n    if (node) {\n        graphInstance.collapseNode(node);\n    }\n});\n```\n\n这段代码展示了显式的启动顺序，它会在交互开始之前稳定初始折叠视图。\n\n```tsx\nawait graphInstance.doLayout();\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\n这段代码表明，节点点击只会显示已折叠分支，而不会再次折叠已经展开的分支。\n\n```tsx\nconst onNodeClick = async (node: RGNode, $event: RGUserEvent) => {\n    const isExpanded = node.expanded;\n    if (isExpanded === false) {\n        graphInstance.expandNode(node);\n    }\n};\n```\n\n这段代码展示了浮动设置面板如何通过实时更新选项来改变 relation-graph 行为，而不是重新构建图谱。\n\n```tsx\n\u003CSettingRow\n    label=\"Wheel Event:\"\n    options={[\n        { label: 'Scroll', value: 'scroll' },\n        { label: 'Zoom', value: 'zoom' },\n        { label: 'None', value: 'none' },\n    ]}\n    value={wheelMode}\n    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}\n/>\n```\n\n## 这个示例的独特之处\n\n这个示例之所以独特，是因为它聚焦于一种非常具体的信息披露模式：整个树在初始数据集中就已经完整存在，选定分支会在 `setJsonData()` 之后立即被折叠，并且初始视图会在用户开始浏览之前先完成稳定布局。因此，它是一个关于程序化启动折叠的聚焦参考，而不是通用树渲染示例。\n\n与 `open-by-level` 相比，它强调的是通过直接点击节点逐支显示分支，而不是为整棵树重新计算披露层级深度。与 `expand-button` 相比，它显示的是已经加载好的后代节点，而不是之后再惰性加载新的子节点。与 `graph-instance-api` 和 `tree-data` 相比，它不只是一个简单的实例方法 demo 或被动的树加载示例，而是把启动折叠加节点点击展开作为核心教学流程。\n\n浮动辅助窗口及其设置/导出控件虽然有用，但并不是这里的核心差异点，因为这套外壳也被其他 demo 共享。这个示例最有代表性的组合是：预加载的扁平树、选择性的启动折叠、单向点击展开，以及在一个紧凑的从左到右查看器中的自动重新布局。\n\n## 这种模式还适用于哪里\n\n这种模式适用于这样一些场景：完整层级已经可用，但如果一开始就显示所有分支，会让用户不堪重负。典型例子包括入门知识地图、部门浏览器、课程模块树、产品分类导航以及故障隔离指南。\n\n当初始界面需要只突出主要决策，同时让细节按需可发现时，这种模式也很合适。如果后续版本需要远程加载分支，这个示例可以作为 UI 基线，然后再把显示逻辑切换为基于 `onNodeExpand` 的数据获取流程。\n",false,500,1782615427965]