JavaScript is required

插槽系统概览

relation-graph 的插槽用于替换或扩展图谱内部的可视层。它们不是简单的“HTML 挂载点”,而是分别挂在节点层、连线层、画布坐标层、视口固定层和背景层上。不同插槽所在层级不同,坐标系、缩放行为、事件处理方式也不同。

如果你只是调整颜色、线宽、圆角、字体,优先使用 RGOptions、节点/连线数据字段或 CSS 变量;如果你需要自定义 DOM/SVG 结构、在节点上放业务按钮、给连线增加多段文本、在画布上放分组框、在视口上放工具栏,再使用插槽。

1. 插槽总览

插槽 Vue 3 / Vue 2 React Svelte 所在层级 是否跟随画布缩放/平移 主要用途
节点内容 #node <RGSlotOnNode>nodeSlot slot="node" 节点外壳 .rg-node 内部 卡片节点、头像节点、状态徽标、业务按钮
节点展开按钮 #node-expand-button nodeExpandButtonSlot slot="node-expand-button" 节点外壳内,默认展开按钮位置 替换展开/收起按钮
连线内容 #line <RGSlotOnLine>lineSlot slot="line" SVG 连线层 自定义 path、箭头、线条标签、连线状态
画布底层 #canvas 或默认插槽 <RGSlotOnCanvas> 或普通 children slot="canvas" 或默认插槽 节点/连线下方 分组框、泳道、坐标辅助线、图谱底层业务标记
画布前景 #canvas-above <RGSlotOnCanvasAbove> slot="canvas-above" 节点/连线上方 框选提示、悬浮锚点、画布坐标中的覆盖层
视口层 #view <RGSlotOnView> slot="view" 图谱视口最上层 固定工具栏、右键菜单、浮层面板、缩略图
背景层 #background <RGBackground> slot="background" .rg-map-background 背景图、水印、主题底纹

说明:

  • Vue3 当前公开包名在站点文档中统一写作 @relation-graph/vue;Vue2 为 @relation-graph/vue2
  • React 既可以使用 <RGSlotOnNode> / <RGSlotOnLine> 等插槽组件,也可以直接通过 nodeSlot / lineSlot / nodeExpandButtonSlot 属性传入渲染函数。两种写法不要混用同一个插槽,否则源码会抛出错误。
  • Vue3/React/Svelte 的默认子内容都会被放到画布底层,等价于 canvas 层,适合放跟随图谱移动的辅助内容。

2. 渲染层级

图谱内部的主要层级可以理解为:

从下到上 内部容器 公开插槽/内容 行为
1 .rg-map-background background 视口固定,不随画布缩放;通常无交互
2 .rg-map-canvas.rg-canvas-behind canvas / 默认子内容 使用画布坐标,跟随 canvasOffsetcanvasZoom
3 EasyView / 缩略辅助层 内部使用 用于性能模式或简化视图
4 .rg-map-canvas 节点、连线、节点/连线插槽 主图谱层,节点和连线都在这里渲染
5 .rg-map-canvas.rg-canvas-above canvas-above 使用画布坐标,但显示在节点/连线之上
6 .rg-graph-plugs .rg-view-slot view 视口固定,不随画布缩放

canvascanvas-above、节点、连线所在的 .rg-map-canvas 会应用如下变换:

transform: translate(canvasOffset.x, canvasOffset.y) scale(canvasZoom / 100);
transform-origin: 0 0;

因此这些内容的 left/top/x/y 都应当使用画布坐标。viewbackground 不应用这层变换,它们的 left/top 是 RelationGraph 组件视口坐标。

3. 坐标系

relation-graph 中最常接触三类坐标:

坐标系 示例 说明
客户端坐标 MouseEvent.clientX/clientY 浏览器窗口坐标,一般来自鼠标或触摸事件
视口坐标 graphInstance.getViewXyByEvent(e) 相对于 RelationGraph 组件左上角的坐标,适合放 view 层菜单
画布坐标 graphInstance.getCanvasXyByViewXy(viewPoint) 图谱内部坐标,节点 x/y、画布插槽定位、连线端点都使用它

常用换算 API:

API 入参 返回 用途
getViewXyByEvent(e) 鼠标/触摸事件 { x, y } 视口坐标 根据事件位置显示视口层菜单
getViewXyByClientXy({ x, y }) 浏览器客户端坐标 { x, y } 视口坐标 从外部事件换算到组件内部
getCanvasXyByClientXy({ x, y }) 浏览器客户端坐标 { x, y } 画布坐标 根据鼠标位置创建节点
getCanvasXyByViewXy({ x, y }) 视口坐标 { x, y } 画布坐标 将固定层操作映射到画布
getViewXyByCanvasXy({ x, y }) 画布坐标 { x, y } 视口坐标 把节点/业务点映射到固定浮层

示例:在鼠标位置新增一个节点时,应当使用画布坐标:

const canvasPoint = graphInstance.getCanvasXyByClientXy({
  x: event.clientX,
  y: event.clientY
});

graphInstance.addNodes([
  {
    id: 'new-node',
    text: '新节点',
    x: canvasPoint.x,
    y: canvasPoint.y
  }
]);

4. 插槽参数类型

节点、连线和展开按钮插槽会传入结构化参数。

节点插槽 #node

export type RGNodeSlotProps = {
  node: RGNode;
  defaultExpandHolderPosition?: string;
  dragging?: boolean;
  checked?: boolean;
};
字段 类型 说明
node RGNode 当前渲染的运行时节点对象。包含 idtexttypedata、位置、样式、状态等字段
defaultExpandHolderPosition string | undefined 全局默认展开按钮位置,来源于 options.defaultExpandHolderPosition
dragging boolean | undefined 当前节点是否处于拖拽状态
checked boolean | undefined 当前节点是否是内部“checked”节点,用于编辑/控制器状态

节点展开按钮插槽 #node-expand-button

export type RGNodeExpandHolderProps = {
  node: RGNode;
  expandOrCollapseNode: (e: MouseEvent | TouchEvent) => void;
  expandHolderPosition?: string;
};
字段 类型 说明
node RGNode 当前节点
expandOrCollapseNode (e) => void 内置展开/收起逻辑,通常在自定义按钮点击时调用
expandHolderPosition string | undefined 实际展开按钮位置,常见值为 leftrighttopbottomhide

连线插槽 #line

export type RGLineSlotProps = {
  lineConfig: RGGenerateLineConfig;
  checked?: boolean;
  defaultLineTextOnPath?: boolean;
  graphInstanceId?: string;
};
字段 类型 说明
lineConfig RGGenerateLineConfig 已由实例根据 RGLineRGFakeLine 计算出的连线渲染配置
checked boolean | undefined 当前线是否是内部“checked”线
defaultLineTextOnPath boolean | undefined 全局默认是否让线条文本沿路径显示
graphInstanceId string | undefined 当前图谱实例 ID,常用于生成唯一的 SVG path id

RGGenerateLineConfig 的核心字段:

字段 类型 说明
line RGLine | RGFakeLine 当前要渲染的线。普通线来自数据模型,假线来自拖拽/编辑状态
from RGLineTarget | undefined 起点目标信息,可能是节点、节点点位、画布点等
to RGLineTarget | undefined 终点目标信息
totalLinesBetweenNodes number | undefined 两个节点之间的线条总数,用于多线偏移
currentLineIndex number | undefined 当前线在多线组中的序号
defaultOptions any 当前渲染所需的默认配置快照

5. 事件处理原则

图谱内部容器为了让拖拽、框选、连线点击等行为稳定,很多外层元素默认设置了 pointer-events: none。这意味着你在 canvascanvas-aboveview 中放的交互元素,如果需要点击,需要显式启用事件。

推荐做法:

<button class="rg-events-all">操作</button>

或:

<button style="pointer-events: auto;">操作</button>

注意:

  • 节点插槽位于 .rg-node 内部,节点外壳已经处理了拖拽开始事件。节点内按钮如果不希望触发节点拖拽,需要在按钮事件上 stopPropagation
  • 连线插槽位于 SVG 层,默认连线使用 .rg-line-bg 承接点击,实际可见 path 常常保持较细线宽。自定义连线时也建议保留一个较粗的透明点击路径。
  • view 层外壳 .rg-graph-plugs 默认 pointer-events: none,固定工具栏、菜单、面板本身要开启 pointer-events

6. 数据字段、CSS 与插槽的职责边界

推荐分工:

层级 负责内容 示例
数据字段 语义性外观、状态、业务数据 node.colorline.colorline.lineShapenode.data.status
RGOptions 全局默认值和交互规则 defaultNodeColordefaultLineWidthdefaultLineShapedisableDragNode
CSS / CSS 变量 主题、状态样式、局部覆盖 .rg-node-selected.rg-line-checked--rg-node-color
插槽 DOM/SVG 结构、复杂内容、业务控件 卡片节点、多文本连线、分组框、工具栏
实例 API 数据变更、布局、视口、事件协作 addNodesupdateLinegetCanvasXyByClientXy

不要把所有逻辑都写进插槽中。例如线条颜色这种影响主图、缩略图和导出一致性的内容,优先放在 line.colordefaultLineColor;节点类型、状态、标签等业务信息放在 node.data;插槽只负责把这些信息渲染出来。

7. 平台写法速查

Vue 3

<RelationGraph :options="graphOptions" :initial-data="graphData">
  <template #node="{ node, checked, dragging }">
    <div class="my-node">{{ node.text }}</div>
  </template>

  <template #line="{ lineConfig, checked, graphInstanceId }">
    <MyLine :line-config="lineConfig" :checked="checked" :graph-instance-id="graphInstanceId" />
  </template>

  <template #canvas>
    <div class="group-box" style="position:absolute;left:0;top:0;">分组</div>
  </template>

  <template #view>
    <div class="toolbar rg-events-all">工具栏</div>
  </template>
</RelationGraph>

React

import {
  RelationGraph,
  RGSlotOnNode,
  RGSlotOnLine,
  RGSlotOnCanvas,
  RGSlotOnView
} from '@relation-graph/react';

<RelationGraph options={graphOptions} initialData={graphData}>
  <RGSlotOnNode>
    {({ node, checked, dragging }) => (
      <div className="my-node">{node.text}</div>
    )}
  </RGSlotOnNode>

  <RGSlotOnLine>
    {({ lineConfig, checked, graphInstanceId }) => (
      <MyLine lineConfig={lineConfig} checked={checked} graphInstanceId={graphInstanceId} />
    )}
  </RGSlotOnLine>

  <RGSlotOnCanvas>
    <div className="group-box">分组</div>
  </RGSlotOnCanvas>

  <RGSlotOnView>
    <div className="toolbar rg-events-all">工具栏</div>
  </RGSlotOnView>
</RelationGraph>

Svelte

<RelationGraph {options} initialData={graphData}>
  <div slot="node" let:node let:checked let:dragging class="my-node">
    {node.text}
  </div>

  <div slot="canvas" class="group-box">
    分组
  </div>

  <div slot="view" class="toolbar rg-events-all">
    工具栏
  </div>
</RelationGraph>

8. 常见场景选择

需求 推荐插槽/方式 原因
节点显示头像、标签、按钮 node 节点外壳仍由组件处理定位、拖拽和状态
替换树节点展开按钮 node-expand-button 能复用 expandOrCollapseNode,不需要自己维护树状态
给不同类型连线画不同图形 line 可使用 lineConfig.line.type 分发模板
给连线添加多个标签 line + RGLineText 或自定义 HTML/SVG 默认文本只渲染一个标签
画泳道、分组框、网格 canvas 跟随画布坐标移动缩放
画框选提示、拖拽辅助锚点 canvas-above 跟随画布坐标,同时显示在图元上方
固定工具栏、右键菜单、属性面板 view 不受画布缩放影响
水印、背景图、纯装饰底纹 background 不影响节点和连线层级

9. 下一步阅读