JavaScript is required

搜索节点并定位视图

这个示例构建人物关系查看器,带悬浮搜索面板,可从已加载图中提取可选姓名并将视口聚焦到选中节点。它异步加载静态 RGJsonData,渲染头像节点和带标签的直线多重连线,并结合 `focusNodeById(...)` 与共享画布设置和图片导出控制。

搜索关系图并聚焦选中的节点

这个示例构建了什么

这个示例构建了一个全高的人物关系查看器,在图谱画布上方带有一个浮动搜索面板。图中展示了基于头像的人物节点、带标签的直线关系,以及一个可拖拽、可最小化、也可切换到设置视图的共享工具窗口。

用户可以输入部分人名,选择过滤后的结果,然后看到视口通过一个简短动画跳转到对应节点。这个示例的重点不是图编辑或路径分析,而是定位器模式:从已加载的图中派生出搜索 UI,再利用这个 UI 快速导航画布。

数据是如何组织的

图数据来自 mock-data-api.ts 中本地静态的 RGJsonData 负载。它包含 rootIdnodes 数组和 lines 数组。每条节点记录都带有 idtextcolorborderColor 以及一个 data 对象,其中包含人物属性,例如 genderisGoodMan,还有供自定义头像插槽使用的 icon URL。每条连线记录使用 fromtotextcolorfontColordata.type,因此图可以直接在边上渲染具名关系标签。

图加载前的预处理非常少。fetchJsonData() 只是用一个简短的异步延迟包装了这份静态负载,更重要的转换发生在 setJsonData(...) 之后:示例调用 graphInstance.getNodes(),把已渲染的节点转换为搜索面板使用的 { text, value } 选项。在真实产品中,同样的结构可以表示员工、调查对象、客户、供应商、设备,或关系图中的任何其他具名实体。

relation-graph 是如何使用的

index.tsx 通过 RGProvider 提供图上下文,MyGraph.tsx 则通过 RGHooks.useGraphInstance() 接管控制。图配置让场景更像一块关系看板,而不是一个通用节点画布:直线连线、圆形节点、在线路路径上显示标签、针对重复边设置 multiLineDistance = 20、使用较低斥力的 force 布局,以及位于右下角的纵向工具栏。随后由实例 API 驱动完整生命周期:loading()setJsonData(...)clearLoading()moveToCenter()zoomToFit()getNodes()focusNodeById(...)clearChecked(),以及聚焦跳转前后临时开启的动画辅助方法。

这个示例通过 RGSlotOnNode 自定义节点渲染,用圆形头像和下方标签替代默认节点主体。它在审阅的源码中没有使用编辑 API 或视口插槽,因此这仍然是一个偏查看器的示例。在图本体之外,QueryFormPanel 提供搜索 UI,共享的 DraggableWindow 组件则提供浮动外壳、设置覆盖层以及导出图片操作。在这个共享窗口内部,CanvasSettingsPanel 使用 RGHooks.useGraphStore() 配合 graphInstance.setOptions(...),在运行时切换滚轮和拖拽行为。

my-relation-graph.scss 中的样式表也是实现的一部分,而不只是装饰。它修改了节点名称颜色,把线标签变成白色标牌,增加了更明显的选中节点高亮,重新着色了选中连线的标签,并保持自定义头像节点为圆形。

关键交互

  • 在搜索框中输入时,会按可见标签文本不区分大小写地过滤节点列表。
  • 选择一个搜索结果后,会关闭下拉框、清空关键词状态,并触发通过 focusNodeById(...) 导航到目标节点的动画效果。
  • 在已选择结果后再次输入时,会清除之前选中的值,使输入框重新成为一个新的搜索框。
  • 点击空白画布区域会调用 clearChecked(),从而重置图中被选中的元素。
  • 当设置覆盖层未打开时,浮动面板可以通过标题栏拖动并被最小化。
  • 设置按钮会在同一窗口内打开一个覆盖层,点击半透明背景会关闭它,并且该面板可以在运行时切换滚轮和拖拽模式。
  • 同一个共享覆盖层还可以把当前图导出为图片。

关键代码片段

这个片段展示了挂载时的图初始化顺序,以及从图中派生搜索选项列表的过程:

graphInstance.loading();
await graphInstance.setJsonData(myJsonData);
graphInstance.clearLoading();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
setNodeItems(graphInstance.getNodes().map(n => ({ text: n.text || '', value: n.id })));

这个片段展示了示例如何把一次搜索结果选择转换成一次带短暂动画的视口跳转:

const focusOnNodeWithAnimation = async (nodeId: string) => {
    graphInstance.enableCanvasAnimation();
    focusOnNode(nodeId);
    await graphInstance.sleep(300); // Wait for animation to complete then turn off animation
    graphInstance.disableCanvasAnimation();
};

这个片段表明,下拉搜索是大小写不敏感的,并且基于节点标签工作,而不是基于硬编码的数字位置:

const filtered = data.filter((item) =>
    item.text.toLowerCase().includes(keyword.toLowerCase())
);

这个片段展示了在已有选择后继续输入时,如何清除该选择并重新打开下拉框以进行新的搜索:

onChange={(e) => {
    if (value) {
        onListItemClick(null);
    }
    setKeyword(e.target.value);
    setOpen(true);
}}

这个片段展示了自定义头像节点插槽:它使用 node.data.icon,并把名字放在头像下方:

<RGSlotOnNode>
    {({ node }: RGNodeSlotProps) => (
        <div className="w-12 h-12 flex place-items-center justify-center">
            <div className="my-node-avatar" style={{ backgroundImage: `url(${node.data?.icon})` }} />
            <div className="my-node-name absolute transform translate-y-[35px]">{node.text}</div>
        </div>
    )}
</RGSlotOnNode>

这个片段展示了共享设置覆盖层如何在运行时修改 relation-graph 的交互行为:

<SettingRow
    label="Wheel Event:"
    options={[
        { label: 'Scroll', value: 'scroll' },
        { label: 'Zoom', value: 'zoom' },
        { label: 'None', value: 'none' },
    ]}
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

这个片段展示了共享浮动窗口使用的导出流程:

const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
    graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
    backgroundColor: graphBackgroundColor
});
await graphInstance.restoreAfterImageGeneration();

这个示例的独特之处

对比分析结果把这个示例放在 scene-relationshipfocus-node-by-idfind-min-path 附近,但这里的重点更窄。与 scene-relationship 相比,这个示例把浮动面板用作节点定位器,而不是图级元数据过滤器。与 find-min-path 相比,它只要求一个目标节点,跳过了路径计算、变暗处理和路径强调。与 focus-node-by-id 相比,它用一个由 graphInstance.getNodes() 派生出的搜索框取代了固定 id 选择器,因此聚焦行为可以基于已加载关系图中的实时标签工作。

因此,这里最强的一组组合特征非常具体:异步加载关系图、从节点实时派生表单选项、可拖拽的白色搜索面板、头像节点插槽、带标签的直线重复连线,以及由搜索触发的动画聚焦跳转。真正独特的部分不是共享设置覆盖层或图片导出,因为这些来自其他示例也在复用的通用辅助代码。真正独特的是叠加在更密集人物关系场景之上的、面向产品使用的定位流程。

这个模式还适用于哪里

这个模式非常适合那些操作人员需要快速找到某个已知人物或实体、而不想手动在整个画布上平移查找的关系查看器。适合迁移的目标包括组织架构图、调查看板、客户账户关系图、合作伙伴生态图、基础设施依赖视图以及知识图谱浏览器。

它也可以自然扩展到更丰富的检索工作流中。同样这种由图驱动的选项派生方式,可以用于自动完成、命令面板、后端辅助搜索服务,或带权限过滤的选择器,同时保留相同的聚焦动画和共享画布控制方式。