[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:corporate-group-relationship-map":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 支持实时切换节点皮肤的企业集团关系图\n\n## 本示例构建了什么\n本示例在单个居中的画布上构建了一个以阅读为主的企业关系查看器。它加载了一张预构建的图，包含 111 个节点和 209 条带标签的关系，将控制人类型的人物节点与公司节点混合展示，并以稠密网络而不是层级结构的方式呈现。用户可以在同一份已加载图数据上切换圆形与矩形节点表现形式，开启或关闭自定义 slot 渲染，打开浮动工具面板，修改滚轮与拖拽行为，并将当前画布导出为图片。这里最有价值的技术点是，这些展示变化发生在运行中的图实例上，而不是通过重建数据集实现。\n\n## 数据是如何组织的\n数据位于 `my-data.ts` 中，由异步辅助函数 `getMyJsonData()` 返回一个静态内联的 `RGJsonData` 对象。这个对象包含 `rootId`、`nodes` 数组和 `lines` 数组。每个节点都带有 `id`、`text`、`color` 和 `borderColor` 等展示字段，大多数节点还包含 `data.nodetype`，以便渲染器区分 `ctrler` 实体和 `ent` 实体；数据集中还包含一个 `main` 节点类型。每条连线都包含 `from`、`to`、`text` 和 `fontColor`，而这些标签把百分比与董事、经理等高管角色文本混合在一起，因此这张图表达的是更广义的关系看板，而不是纯粹的股权树。\n\n在 `setJsonData(...)` 之前几乎没有真正的预处理。`initializeGraph()` 只是等待 `getMyJsonData()` 返回，然后将对象直接传给 relation-graph。在生产系统中，相同的结构可以来自股东分析、董事会控制数据、关联网络、合作方持股数据或合规审查输出。\n\n## relation-graph 是如何使用的\n入口文件使用 `RGProvider` 包裹该示例，使 hooks 能解析当前活动的图实例。在 `MyGraph.tsx` 内，`RGHooks.useGraphInstance()` 驱动主流程：加载静态数据集、将视口居中、让图适配画布、修改运行时选项，以及更新已经渲染出的节点。该图初始配置为 `layoutName: 'center'`、`RGLineShape.StandardCurve`、`RGJunctionPoint.border`、无默认节点边框，以及默认 `60 x 60` 的节点尺寸。\n\n关键实现细节是形状切换。`defaultNodeShape` 绑定到 React state，但代码在加载之后还会调用 `getNodes()` 和 `updateNode(...)`，因为仅修改默认值并不会重设图中已存在节点的样式。当用户切换到矩形节点时，示例还会把连接点模式从 `border` 改为 `ltrb`，让连线以更适合新几何形状的方式挂接。\n\n`RGSlotOnNode` 是可选的，而不是强制的。启用后，同一份数据会通过自定义的业务化标记结构渲染：圆形模式会在节点中心显示一个图标，并在其下方显示分离式标签；矩形模式则会显示一个白色卡片，包含图标、文本，以及复用 `var(--rg-node-color)` 的边框。图标的选择取决于 `node.data.nodetype`，因此控制人节点与企业节点在视觉上始终保持区分。\n\n浮动辅助窗口来自共享的 `DraggableWindow` 组件。它的设置覆盖层使用 `RGHooks.useGraphStore()` 读取当前交互选项，并通过图实例 API 切换 `wheelEventAction` 和 `dragEventAction`。同一个共享面板还通过 `prepareForImageGeneration()`、`modern-screenshot` 和 `restoreAfterImageGeneration()` 执行导出流程。本地 SCSS 覆盖样式会保持画布背景为白色，并为分离式节点文本提供样式，尤其是在选中状态下。\n\n## 关键交互\n`Rect` 和 `Circle` 选择器会在已加载节点上实时改变节点几何形状和连线挂接行为。\n\n复选框会挂载或卸载 `RGSlotOnNode`，因此用户可以在不改变底层数据的前提下，对比自定义业务节点皮肤与内置节点渲染效果。\n\n浮动说明窗口可以被拖动、最小化，也可以通过齿轮按钮切换到设置覆盖层。\n\n在设置覆盖层内，滚轮行为可以在 `scroll`、`zoom` 和 `none` 之间切换，而画布拖拽行为可以在 `selection`、`move` 和 `none` 之间切换。\n\n`Download Image` 操作会通过共享截图辅助工具捕获当前图画布，并将结果下载为文件。\n\n## 关键代码片段\n这段代码表明，图会加载一份预先准备好的数据集，然后重新居中并让视口适配内容，而不是逐步展开分支。\n\n```tsx\nconst initializeGraph = async () => {\n    const myJsonData = await getMyJsonData();\n    await graphInstance.setJsonData(myJsonData);\n    graphInstance.moveToCenter(); // Center on canvas slot content\n    graphInstance.zoomToFit(); // Zoom to fit canvas slot content\n};\n```\n\n这段代码证明，形状切换是应用在运行中的图实例上的，而不只是影响未来新增的节点。\n\n```tsx\ngraphInstance.getNodes().forEach(node => {\n    graphInstance.updateNode(node, {\n        nodeShape: nodeShape,\n        width: nodeShape === RGNodeShape.circle ? 60 : 0,\n        height: nodeShape === RGNodeShape.circle ? 60 : 0\n    })\n});\n```\n\n这段代码说明，自定义 slot 渲染是可选的，并且可以通过示例 UI 打开或关闭。\n\n```tsx\n\u003CSimpleUIBoolean\n    label=\"Use Node Slot to Render Node Content\"\n    currentValue={enableNodeSlot}\n    onChange={setEnableNodeSlot}\n/>\n\u003C/div>\n\u003C/DraggableWindow>\n\u003CRelationGraph options={graphOptions}>\n    {enableNodeSlot && \u003CRGSlotOnNode>\n```\n\n这段代码展示了 slot 渲染器如何根据节点形状和业务实体类型改变节点内容。\n\n```tsx\nnodeShape === RGNodeShape.circle ? (\n    \u003Cdiv className=\"w-full h-full flex place-items-center justify-center\">\n        \u003Cdiv className=\"text-white w-full h-full flex place-items-center justify-center\">\n            {node.data.nodetype === 'ctrler' ? \u003CUserCircleIcon size={32} /> : \u003CBuilding2Icon size={32} />}\n        \u003C/div>\n        \u003Cdiv className=\"my-node-text absolute transform translate-y-[45px]\">{node.text}\u003C/div>\n    \u003C/div>\n) : (\n```\n\n这段代码来自共享辅助工具，展示了示例如何在不重建图的情况下提供导出能力。\n\n```tsx\nconst canvasDom = await graphInstance.prepareForImageGeneration();\nlet graphBackgroundColor = graphInstance.getOptions().backgroundColor;\nif (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n    graphBackgroundColor = '#ffffff';\n}\nconst imageBlob = await domToImageByModernScreenshot(canvasDom, {\n    backgroundColor: graphBackgroundColor\n});\nif (imageBlob) {\n    downloadBlob(imageBlob, 'my-image-name');\n}\n```\n\n## 这个示例的独特之处\n与 `scene-org`、`investment-penetration` 和 `investment` 这类相近的业务示例相比，这个示例始终停留在一张预加载、居中的网络画布上，而不是把场景转换成树、组织架构图或懒加载展开流程。当需求是在同一时间检查大量交叉连接和混合关系标签时，这一点很重要。\n\n与 `css-theme` 和 `drag-and-wheel-event` 这类偏运行时风格的相邻示例相比，这里的控件直接服务于业务场景的可读性。这里的主要经验不是通用主题切换或画布测试，而是如何在保持一张稠密企业关系图已加载的同时，让用户在内置节点和自定义 slot 渲染的业务皮肤之间切换，并且对已经渲染出的图实时改变节点几何形状。\n\n对比数据也表明，这种组合本身并不常见：中心布局、稠密的带标签连线、按角色着色的人物与公司实体、运行时节点形状切换、可选的 `RGSlotOnNode`，以及同页共享的浮动设置或导出外壳。这个组合使该示例相比编辑器或强调层级优先的图表，更适合作为紧凑型业务网络查看器的起点。\n\n## 这种模式还适用于哪里\n这种模式很适合迁移到股东控制关系审查界面、关联网络分析、董事会与高管关系查看器，以及需要单张稠密关系看板而不是方向性树的尽调仪表盘。\n\n它也适用于产品团队希望在同一份图数据上提供两种展示模式的场景，例如紧凑的图标优先总览，以及更易读的标签优先卡片视图。\n\n实时 `updateNode(...)` 重设样式的方法，尤其适合任何允许用户在加载后改变视觉编码的图产品，例如在不重新获取数据集的情况下，在风险视图、实体类别或面向不同受众的展示模式之间切换。\n",false,500,1782615399132]