[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:node-css-skin-and-checked-state":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 使用 CSS 换肤和选中状态的默认树节点\n\n## 这个示例构建了什么\n\n这个示例构建了一个小型的从左到右树形查看器，保留了 relation-graph 内置的矩形节点，并通过极简的白色视觉风格对其重新定制。画布铺满屏幕，连线使用克制的深色曲线，节点的大部分装饰性外观被移除，剩余的视觉特征主要来自黑色单行标签和自定义的选中状态填充。\n\n用户主要是在查看一棵已准备好的层级树，而不是编辑它。一个浮动辅助窗口可以拖动、最小化、展开为设置面板，并可用于将当前图导出为图片。这个示例的核心价值在于展示：在切换到基于插槽渲染的节点主体之前，仅通过 graph options 和作用域 SCSS，可以把默认节点渲染器定制到什么程度。\n\n## 数据是如何组织的\n\n图数据以内联方式声明为一个 `RGJsonData` 对象，其中包含 `rootId`、一个扁平的 `nodes` 数组，以及一个扁平的 `lines` 数组。示例内容使用了 `a`、`b1-4`、`c3` 这类通用 id 和标签，使结构刻意保持中性，从而更便于单独观察样式模式。\n\n在调用 `setJsonData(...)` 之前没有任何预处理。这个示例将静态数据集直接传入 relation-graph，然后进行居中和适配。在实际应用中，同样的数据形态可以表示组织层级、审批树、产品分类、决策分支，或任何其他仍然适合使用默认文本节点渲染器的轻量级层级结构。\n\n## relation-graph 是如何使用的\n\n`RGProvider` 包裹整个页面，`RGHooks.useGraphInstance()` 提供实时的 graph 实例。局部定义的 `graphOptions` 配置了一个从左侧生长的 `tree` 布局，增大节点间距以提高可读性，将节点形状切换为 `RGNodeShape.rect`，保持节点填充和边框透明，使用 `RGLineShape.StandardCurve` 路由连线，并将内置工具栏水平放置在右下角。\n\n这个示例没有定义 `RGSlotOnNode`，也没有定义任何自定义节点主体组件。相反，它保留 relation-graph 的默认渲染器，并通过 options 和作用域 SCSS 改变最终效果。样式表将 `.rg-node-text` 强制为单行显示，将文字颜色设为黑色，并重写 `.rg-node-peel.rg-node-checked`，使选中节点使用半透明灰色填充，而不是默认的强调样式。\n\n共享的辅助窗口补充了其余运行时行为。`CanvasSettingsPanel` 通过 `RGHooks.useGraphStore()` 读取当前图配置，调用 `graphInstance.setOptions(...)` 切换滚轮和拖拽行为，并使用 `prepareForImageGeneration()` 配合 `restoreAfterImageGeneration()` 将图画布导出为图片。这些控件是实用的通用工具，但本地这个示例特有的技术点，是对默认节点进行换肤的方案。\n\n## 关键交互\n\n- 图会在挂载时加载一次，然后调用 `moveToCenter()` 和 `zoomToFit()`，使整棵树在初始状态下立即完整可见。\n- 用户以查看模式检查一棵固定的树；本地源码没有添加结构编辑、节点创建工具或由点击驱动的自定义状态变化。\n- 浮动辅助窗口可以在页面中拖动、最小化，并重新展开为设置覆盖层。\n- 设置覆盖层可在 scroll、zoom 和 none 之间切换 `wheelEventAction`，并可在 selection、move 和 none 之间切换 `dragEventAction`。\n- 辅助窗口可以通过共享的截图流程将当前画布导出为图片。\n- 样式表定义了选中节点的外观，但本地示例代码没有添加自己的选中状态处理器。\n\n## 关键代码片段\n\n这段 options 配置展示了该示例如何保持使用 relation-graph 的默认渲染器，并通过选项组合构建一个克制的树形查看器基础样式。\n\n```tsx\nconst graphOptions: RGOptions = {\n    backgroundColor: '#ffffff',\n    defaultLineColor: '#444',\n    defaultNodeColor: 'transparent',\n    defaultNodeBorderWidth: 0,\n    defaultNodeBorderColor: 'transparent',\n    defaultNodeShape: RGNodeShape.rect,\n    defaultLineShape: RGLineShape.StandardCurve,\n    defaultJunctionPoint: RGJunctionPoint.lr,\n    toolBarDirection: 'h',\n    toolBarPositionH: 'right',\n    toolBarPositionV: 'bottom',\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 310,\n        treeNodeGapV: 70,\n    }\n};\n```\n\n这段片段说明，数据集是一个直接内联的树结构，在进入图之前没有经过任何预处理层。\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'a' },\n        { id: 'b', text: 'b' },\n        { id: 'b1', text: 'b1' },\n        { id: 'b1-1', text: 'b1-1' }\n        // ...\n    ],\n    lines: [\n        { from: 'a', to: 'b', text: '' },\n        { from: 'b', to: 'b1', text: '' },\n        { from: 'b1', to: 'b1-1', text: '' }\n        // ...\n    ]\n};\n```\n\n这段初始化代码证明了该示例的生命周期非常简单：加载预设树、将其居中，并让它适配视口。\n\n```tsx\nconst initializeGraph = async () => {\n    await graphInstance.setJsonData(myJsonData);\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n};\n\nuseEffect(() => {\n    initializeGraph();\n}, []);\n```\n\n这段 SCSS 片段是核心样式手法：保留默认节点标记结构，然后通过作用域选择器微调文本行为和选中状态反馈。\n\n```scss\n.relation-graph {\n    --rg-checked-item-bg-color: rgba(63, 62, 62, 0.34);\n\n    .rg-node {\n        .rg-node-text {\n            white-space: nowrap;\n            color: #000000;\n        }\n    }\n\n    .rg-node-peel.rg-node-checked {\n        .rg-node {\n            box-shadow: none;\n            background-color: var(--rg-checked-item-bg-color);\n        }\n    }\n}\n```\n\n这段共享设置代码展示了辅助窗口如何将 graph-instance API 转换为运行时查看控制。\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这段图片导出流程展示了共享截图过程：准备 graph DOM、进行截图、下载 blob，并在之后恢复 graph。\n\n```tsx\nconst downloadImage = async () => {\n    const canvasDom = await graphInstance.prepareForImageGeneration();\n    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;\n    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n        graphBackgroundColor = '#ffffff';\n    }\n    const imageBlob = await domToImageByModernScreenshot(canvasDom, {\n        backgroundColor: graphBackgroundColor\n    });\n    if (imageBlob) {\n        downloadBlob(imageBlob, 'my-image-name');\n    }\n    await graphInstance.restoreAfterImageGeneration();\n};\n```\n\n## 这个示例的独特之处\n\n从对比数据来看，最明显的差异在于：这个示例保留了内置的矩形文本节点，但仍然主要通过 options 和 SCSS 达到了定制化的视觉效果。相比 `node-style4`，它没有引入 `RGSlotOnNode`、图标模板或更强的品牌化主题。相比 `node-style3`，它保留了有序的树布局和普通文本标签，而不是切换到力导布局以及由元数据驱动的图标节点。\n\n这种更收敛的选择，使它具有明确的检索价值。当团队希望在 relation-graph 默认节点渲染器上进行低成本 CSS 换肤时，它是更合适的起点，尤其适用于可读的不换行标签和克制的选中状态反馈。稀有度数据也支持这一点：白色工具型外壳、宽松的从左到右树间距、透明节点外观、深色曲线连线以及半透明选中填充，这种组合在示例集合中并不常见。\n\n共享的辅助窗口、运行时交互切换和图片导出都是体验的一部分，但它们不是这里独有的主张，因为附近的示例也复用了相同的脚手架。更稳妥的区别点，是它强调在不离开默认渲染器的前提下，对排版和选中状态进行细化。\n\n## 这种模式还适用于哪里\n\n这种模式很适合内部层级查看器、审批或职责树、产品分类浏览器，以及规则树或决策树等场景。在这些场景中，图需要比默认主题更精致，但又不足以支撑引入一整套自定义节点组件系统。\n\n对于那些需要选择反馈和标签控制、但不希望增加逐节点模板的团队来说，它同样是一个有用的起点。如果领域数据本身已经接近 `id`、`text` 和 `from`/`to` 关系，那么用同样的方法就能以较低的实现成本交付一个可读且带有品牌感的树图。\n",false,500,1782615371867]