[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:basic-custom-node-and-minimap":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 构建一个带常驻小地图的基础自定义节点查看器\n\n## 这个示例构建了什么\n\n这个示例构建了一个全高度的 relation graph 查看器，用于展示一个虚构的小型公司网络。完成后的场景会在中间显示一个更大的根节点，并将其渲染为带动画的圆形徽标；周围节点则显示为更小的图标圆点，标签放在节点下方。像 `Invest` 和 `Executive` 这样的连线标签会绘制在连接路径上，同时小地图会始终显示在图视图内部。\n\n由于图谱会在挂载时完成加载、居中并适配视口，用户一打开就可以直接查看网络。这个示例最重要的教学点并不是那些业务名称本身，而是在一个很小的 React 示例里，紧凑地组合了基于 provider 作用域的图初始化、自定义节点渲染，以及内嵌总览部件。\n\n## 数据是如何组织的\n\n数据保存在 `MyGraph.tsx` 中一个内联的 `staticJsonData` 常量里。它使用标准的 `RGJsonData` 结构，包含 `rootId`、`nodes` 和 `lines`。当前分析到的载荷包含 19 个节点和 18 条带标签的连线。大多数节点使用全局默认尺寸 `60 x 60`，而根节点则在数据中直接设置了 `width: 100` 和 `height: 100`，以便自定义根节点渲染器拥有更大的展示空间。\n\n在调用 `setJsonData()` 之前没有任何预处理步骤。组件直接在代码中声明最终的图数据载荷，并在启动期间把它直接传给图实例。这里可复用的部分是按节点存储元数据的模式：每个节点都在 `data.myicon` 中保存一个图标键，自定义节点组件再把这个字段转换成不同的 Lucide 图标。在真实应用中，同样的结构可以表示组织、系统、人员、资产或工作流步骤，而 `data.myicon` 也可以替换为类型、状态或分类信息。\n\n## relation-graph 是如何使用的\n\n`index.tsx` 用 `RGProvider` 包裹整个示例，`MyGraph.tsx` 则通过 `RGHooks.useGraphInstance()` 从 provider 上下文中访问当前活动实例。一个在挂载时执行的 `useEffect()` 会运行 `initializeGraph()`，加载内联 JSON，然后调用 `moveToCenter()` 和 `zoomToFit()`，从而让查看器在打开时就处于可用状态，而不需要用户额外操作。\n\n图配置保持得很精简，但表达得很明确。示例启用了调试模式，使用圆形节点，把 `defaultNodeWidth` 和 `defaultNodeHeight` 设置为 `60`，让连线文本绘制在路径上，并配置了内置的 `center` 布局，参数为 `maxLayoutTimes: 3000`。它还设置了 `defaultExpandHolderPosition: 'right'` 和 `reLayoutWhenExpandedOrCollapsed: true`，尽管当前这个本地示例仍然是只读查看器，而不是编辑器。\n\n主要定制来自两个 relation-graph 插槽。`RGSlotOnNode` 用 `CustomNode` 替换了默认节点主体，后者会在专用根节点模板和基于图标的子节点模板之间分支。`RGSlotOnView` 挂载了 `RGMiniView`，因此小地图直接位于图场景内部，而不是外部面板中。随后，本地 SCSS 又重写了 relation-graph 的选中状态类，为被选中的节点、节点标签、连线描边和连线标签应用了洋红色强调色。\n\n## 关键交互\n\n- 图谱会在挂载时自行初始化：加载 JSON 数据载荷、让画布居中，并将视口缩放到合适范围。\n- 常驻的 `RGMiniView` 为用户提供了始终可见的总览，以及同一图场景中的第二个导航界面。\n- 节点点击和连线点击只用于查看。它们会把被点击的对象输出到控制台并返回 `true`，但不会更新 React state，也不会修改图数据。\n\n## 关键代码片段\n\n这个包装器表明，示例在使用任何图实例 API 之前，依赖于 provider 上下文。\n\n```tsx\nconst MyApp: React.FC = () => {\n    return (\n        \u003CRGProvider>\n            \u003CMyGraph />\n        \u003C/RGProvider>\n    );\n};\n```\n\n这个内联数据集证明了图数据载荷是直接在组件中组装的，同时图标选择来自节点元数据。\n\n```tsx\nconst staticJsonData: RGJsonData = {\n    rootId: '2',\n    nodes: [\n        { id: '2', text: 'Initrode', width: 100, height: 100, data: { myicon: 'delivery_truck' } },\n        { id: '1', text: 'Paper Street Soap Co.', data: { myicon: 'fries' } },\n        { id: '3', text: 'Cyberdyne Systems', data: { myicon: 'football' } },\n        // ...\n    ],\n    lines: [\n        { from: '7', to: '71', text: 'Invest' },\n```\n\n这个启动序列是核心的引导模式：在挂载后先加载 JSON，再让图谱居中并适配视口。\n\n```tsx\nconst graphInstance = RGHooks.useGraphInstance();\n\nconst initializeGraph = async () => {\n    await graphInstance.setJsonData(staticJsonData);\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n};\n\nuseEffect(() => {\n    initializeGraph();\n}, []);\n```\n\n这个配置块展示了示例在布局和默认图样式上的选择。\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: true,\n    defaultLineShape: 1,\n    defaultNodeShape: RGNodeShape.circle,\n    defaultNodeWidth: 60,\n    defaultNodeHeight: 60,\n    defaultLineTextOnPath: true,\n    layout: { layoutName: 'center', maxLayoutTimes: 3000 },\n    defaultExpandHolderPosition: 'right',\n    reLayoutWhenExpandedOrCollapsed: true\n};\n```\n\n这个图主体表明，自定义节点渲染和小地图都是通过 relation-graph 插槽接入的。\n\n```tsx\n\u003CRelationGraph\n    options={graphOptions}\n    onNodeClick={onNodeClick}\n    onLineClick={onLineClick}\n>\n    \u003CRGSlotOnNode>\n        {({ node, checked, dragging }: RGNodeSlotProps) => (\n            \u003CCustomNode node={node} checked={checked} dragging={dragging} />\n        )}\n    \u003C/RGSlotOnNode>\n    \u003CRGSlotOnView>\n        \u003CRGMiniView />\n    \u003C/RGSlotOnView>\n\u003C/RelationGraph>\n```\n\n`CustomNode` 中的这个分支证明了根节点和子节点有意采用了不同的视觉模板。\n\n```tsx\nconst CustomNode: React.FC\u003CRGNodeSlotProps> = ({ node }) => {\n    if (node.id === '2') {\n        return (\n            \u003Cdiv className=\"my-node-animation-01 z-[555] h-full w-full rounded-full relative text-lg flex place-items-center justify-center overflow-hidden\">\n                \u003Cdiv className=\"py-2 w-full text-center text-white bg-gray-100 bg-opacity-40 border-t border-b border-gray-500\">\n                    {node.text}\n                \u003C/div>\n            \u003C/div>\n        );\n    }\n```\n\n这个子节点片段展示了同一个插槽渲染器如何把 `node.data.myicon` 转换成基于图标的圆形节点，并在下方放置分离式标签。\n\n```tsx\nreturn (\n    \u003Cdiv className=\"h-full w-full rounded-full flex place-items-center justify-center shadow-md\">\n        \u003CIconSwitcher iconName={node.data?.myicon} size={30} />\n        \u003Cdiv\n            className=\"bg-gray-200 text-black px-2 rounded-lg absolute my-node-text\"\n            style={{ marginTop: '100%', transform: 'translateY(15px)' }}\n        >\n            {node.text}\n        \u003C/div>\n    \u003C/div>\n);\n```\n\n这个 SCSS 片段证明，示例定制了 relation-graph 的选中状态样式，而不是保留默认的选中颜色。\n\n```scss\n.rg-node-peel.rg-node-checked {\n    .rg-node {\n        color: #f43ce5;\n        .my-node-text {\n            color: #f43ce5;\n        }\n    }\n}\n\n.rg-line-peel.rg-line-checked {\n    .rg-line {\n        stroke: #f43ce5;\n```\n\n## 这个示例的独特之处\n\n对比数据将这个示例描述为一个低门槛起步示例，而不是菜单示例、提示示例或工具栏示例。它最鲜明的特点是：在同一个只读查看器中，以非常紧凑的方式组合了基于 provider 作用域的启动方式、一个内联 `RGJsonData` 数据载荷、根节点与子节点之间的自定义渲染差异、选中状态样式重写，以及始终挂载的小地图。\n\n与 `node-menu-2` 和 `node-menu` 相比，这个示例通过 `RGMiniView` 使用 `RGSlotOnView` 作为被动导航辅助，而不是作为上下文操作界面。与 `node-tips` 相比，它强调的是常驻总览导航和更强的视觉识别，而不是悬停查看。与 `toolbar-buttons` 相比，它的主要教学重点是自定义节点组合，而不是自定义图谱外壳或小地图开关。\n\n这里需要把结论保持在较窄范围内。对比记录并不支持声称这是唯一使用自定义节点插槽的示例，也不支持声称这是唯一带小地图的示例。它真正支持的说法是：当团队希望找到一个足够小的基线，同时把这些部分组合在一起，又不额外引入菜单状态、提示状态或工具栏状态时，这个示例是一个特别紧凑的参考。\n\n## 这种模式还能用于哪里\n\n这种模式很适合轻量级归属关系图、服务依赖查看器、生态关系图、组织结构图，以及产品关系类页面，在这些场景中，当前最直接的需求是一个清晰、带品牌感的查看器，而不是编辑器。保持启动序列不变的前提下，同样的内联数据加插槽结构也可以替换为 API 数据。\n\n它也可以扩展到更丰富的界面。团队可以保留相同的 `RGProvider` 设置、图实例初始化流程和 `RGSlotOnNode` 定制方式，然后在后续再加入详情抽屉、悬停面板、过滤控件，或业务特定的点击动作。这个示例之所以有用，恰恰是因为这些扩展目前都还没有实现，因此这个基线仍然很容易复制和扩展。\n",false,500,1782615370605]