JavaScript is required

连线标签与端点编辑

这个示例展示如何选中已有连线、编辑其标签文本、拖拽标签到新位置,并借助 relation-graph 内置编辑控制器重连端点。它还演示了选中态切换、运行时画布设置、图片导出和 toast 反馈,在紧凑编辑工作区内完成。

使用内置控制器编辑现有连线标签和端点

这个示例构建了什么

这个示例构建了一个紧凑的编辑工作区,用于维护小型图中的现有连接。页面展示了一个全高的 relation-graph 画布、一个浮动说明窗口、绿色曲线以及点状背景,使整个界面看起来更像一个编辑器,而不是静态演示页。

用户可以点击一条连线以显示端点控制器,拖动这些控制器重新连接该连线,双击连线标签修改其文本,并将标签拖到新的位置。这个示例的核心关注点是边的维护,尤其是标签文本和标签位置,而不是扩展成一个完整的节点与边创作工具或路径编辑器。

数据是如何组织的

图数据以内联方式声明为一个 RGJsonData 对象。它包含一个 rootId、一个仅含 idtext 的扁平 nodes 数组,以及一个 lines 数组,其中每条连接都具有稳定的 idtextfromto 字段。

在调用 setJsonData() 之前没有任何预处理。组件在 initializeGraph() 内部构造数据集,在挂载时直接将其加载到图实例中,然后调用 zoomToFit(),使初始视图可以立即使用。

在生产系统中,同样的结构可以表示工作流流转、审批链接、依赖边或数据血缘关系,其中连线标签承载业务语义,并且可能需要在图渲染完成后进行人工修正。

relation-graph 是如何使用的

这个示例没有配置自定义布局算法。它将一个小型内联数据集加载到 RelationGraph 中,依赖库对这份数据的默认排布,然后通过 zoomToFit() 让视口适配内容。

图选项被刻意收窄:defaultLineShape 被设置为 RGLineShape.StandardCurvedefaultLineColor 被设置为 #00a63e。这样可以让示例重点集中在编辑行为上,而不是布局或样式复杂度。

RGProvider 包裹整个页面,以便 RGHooks.useGraphInstance() 获取当前激活的图实例。这个实例被用于 setJsonDatazoomToFittoggleEditingNodesetEditingNodessetEditingLinegetNodesInSelectionViewclearCheckedaddLines。在共享的浮动设置面板中,RGHooks.useGraphStore()setOptions() 被用来暴露滚轮和拖拽行为的运行时开关,同一个面板还会调用 prepareForImageGeneration()restoreAfterImageGeneration() 将图导出为图片。

实际的编辑 UI 挂载在 RGSlotOnView 中,而不是通过自定义连线渲染实现。RGEditingConnectController 为选中的连线提供端点控制器,而 RGEditingLineControllerpathEditable={false} 挂载,并附带文本变更和标签拖拽的回调钩子。这意味着路径几何形状保持固定,而标签编辑仍然通过本示例所使用的控制器行为提供。

本地样式表定制的是编辑器外壳,而不是数据模型。它基于 relation-graph 的 CSS 变量构建了一个随缩放变化的点状画布背景,使用每条连线当前的颜色来渲染连线标签,并把选中连线的标签变成填充徽标,以便更容易识别当前激活的连接。共享的 DraggableWindowSimpleGlobalMessage 工具则为图周围补充了浮动说明、设置面板、导出操作以及类似 toast 的编辑反馈。

关键交互

  • 点击一条连线会将该连线设为当前编辑目标,并显示内置的端点和标签编辑控制器。
  • 双击连线标签会启动行内文本编辑,完成编辑后会触发一个包含更新后文本的成功 toast。
  • 拖动连线标签会改变其位置,并在拖拽结束时触发一个警告 toast。
  • 拖动端点控制器会重新连接选中的连线,而该示例会通过 onLineBeCreated 将发出的 lineJson 持久化回图中。
  • 点击节点且不带修饰键时,会用该节点替换当前的编辑节点集合;而 ShiftCtrlMeta 点击则会切换该节点在当前编辑节点集合中的成员状态。
  • 框选会通过调用 getNodesInSelectionView(...) 将选区矩形转换为节点集合。
  • 点击空白画布区域会清空编辑节点、清除当前编辑连线,并移除选中态高亮。
  • 打开浮动设置面板后,用户可以切换滚轮行为、切换画布拖拽行为,并下载当前图的图片。

关键代码片段

这个片段展示了该示例使用一个小型内联 RGJsonData 数据集,并为连线显式定义标签。

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'Border color' },
        { id: 'a1', text: 'No border' },
        { id: 'a2', text: 'Plain' },
        { id: 'a1-1', text: 'Text Node' },
        // ... more nodes ...
    ],

这个片段展示了数据创建与图渲染之间没有预处理步骤,数据被直接加载。

// setJsonData is async
await graphInstance.setJsonData(myJsonData);
graphInstance.zoomToFit();

这个片段展示了节点点击如何驱动编辑节点状态,同时清除先前激活的编辑连线。

const onNodeClick = (nodeObject: RGNode, $event: RGUserEvent) => {
    if ($event.shiftKey || $event.ctrlKey || ($event.metaKey && !$event.altKey)) {
        graphInstance.toggleEditingNode(nodeObject);
    } else {
        graphInstance.setEditingNodes([nodeObject]);
    }
    graphInstance.setEditingLine(null);
};

这个片段展示了如何将框选结果转换为下一组编辑节点。

const onCanvasSelectionEnd = (selectionView: RGSelectionView) => {
    const willSelectedNodes = graphInstance.getNodesInSelectionView(selectionView) || [];
    graphInstance.setEditingNodes(willSelectedNodes);
};

这个片段展示了端点编辑为何通过连线创建回调进行持久化,因为端点变更会发出一个重新创建后的连线载荷。

const onLineBeCreated = (lineInfo: { lineJson: JsonLine }) => {
    graphInstance.addLines([lineInfo.lineJson]);
};

这个片段展示了标签编辑与标签拖拽被视为两个独立的交互结果,并分别绑定了反馈钩子。

const onLineTextChanged = (line: RGLine) => {
    SimpleGlobalMessage.success('Line Text Changed:' + line.text);
};
const onLineTextDragEnd = (line: RGLine) => {
    SimpleGlobalMessage.warning('Line Text Drag End:' + line.text);
};

这个片段展示了精确的覆盖层配置:启用了端点编辑,禁用了路径编辑,并挂接了连线标签相关回调。

<RGSlotOnView>
    <RGEditingConnectController />
    <RGEditingLineController
        pathEditable={false}
        onLineTextChanged={onLineTextChanged}
        onLineTextDragEnd={onLineTextDragEnd}
    />
</RGSlotOnView>

这个片段展示了用于构建点状编辑器表面和已选中连线标签高亮的样式表。

background-position: var(--rg-canvas-offset-x) var(--rg-canvas-offset-y);
background-size: calc(var(--rg-canvas-scale) * 15px) calc(var(--rg-canvas-scale) * 15px);
background-image: radial-gradient(circle, rgb(197, 197, 197) calc(var(--rg-canvas-scale) * 1px), transparent 0);

.rg-line-peel.rg-line-checked {
    .rg-line-label {
        background-color: var(--rg-line-color);
        color: #fff;
    }
}

这个示例的独特之处

对比数据表明,这个示例与 change-line-verticeschange-line-pathcustomize-line-toolbar 属于同一类,但它的重点更加收敛。相比 change-line-vertices,它保留了相同的“选中连线后进行编辑”的流程,同时在仅维护端点之外进一步加入了行内标签编辑和可拖拽的标签定位。相比 change-line-path,它保持 pathEditable={false},并把连线标签而不是路径几何形状作为主要可编辑区域。

它在 VIP 编辑器风格示例中也很突出,因为它在一个紧凑的工作区里组合了多种少见特性:浮动说明与设置窗口、在节点集合与单一激活连线之间基于选择进行切换、内置端点控制器、行内标签编辑、拖拽重定位标签文本,以及针对标签内容修改与标签拖拽分别提供的 toast 反馈。这样的组合使它成为一个很实用的起点,适用于团队需要维护现有边,而又不想进入更完整工作流编辑器复杂度的场景。

对比文件也清楚地划定了这个示例不涉及的范围。它不是自定义连线工具栏示例,不是由节点驱动的连线创建示例,也不是路径编辑示例。它的独特价值在于:通过内置的 relation-graph 控制器,直接维护一条已存在连线的文本、文本位置和端点。

这种模式还适用于哪里

这种模式很适合工作流或审批工具,其中连接标签用于描述条件、责任或流转名称,而这些标签可能需要在图已经布局完成后偶尔修正。它也适用于依赖关系图和数据血缘视图,在这些场景里,用户需要重新连接某段关系或移动一个重叠标签,而无需编辑整个图结构。

第二个扩展方向是轻量级维护工具。团队可以复用同样的“选中连线”工作流,将标签编辑持久化到后端、审计端点变更,或通过领域规则限制重新连接,同时仍然避免完整图谱创作界面的复杂性。