JavaScript is required

地图联动仓库商品概览

这个示例在 `RGSlotOnCanvas` 中组合美国地图背景、仓库卡片和商品卡片,再通过 `RGConnectTarget` 锚点与假连线高亮当前选择。它主要用于参考地图背景下的 DOM 端点连线与主从联动,而非稳定的库存数据模型。

借助 Canvas-Slot 连接目标实现地图联动的仓库与商品高亮

这个示例构建了什么

这个示例构建的是一个组合式仪表盘场景,而不是传统的节点连线图。画布中展示了一个半透明的美国 SVG 地图、放置在固定坐标上的脉冲位置标记、左侧的仓库列表以及右侧的商品列表。蓝色的曲线高亮线会把当前选中的仓库同时连接到它在地图上的标记以及动态生成的商品卡片。

用户既可以点击地图标记,也可以点击仓库卡片来切换当前激活的选择。他们还可以打开悬浮的辅助窗口,调整滚轮和画布拖拽行为,或将当前画布导出为图片。这个示例最主要的实现启示是:这里的 relation-graph 被用作普通 HTML 面板与标记之间的连接叠加层,而不是可见业务节点的主要渲染器。

数据是如何组织的

主场景状态被拆分成两种轻量级数据结构。MyWarehouseGroup 保存 groupIdgroupNamepercent,以及包含 xy 坐标的绝对 locationMyProduct 保存 idnameunitcount。仓库记录直接在 MyGraph 中完成初始化,而商品列表则由 my-data-api.tsx 中的 fetchWarehouseProducts(...) 生成。

在 relation-graph 开始绘制前,这个示例先做了两个小的预处理步骤。它先为左侧列表和地图标记层初始化六条固定位置记录,然后 initializeGraph() 选中分组 b,并调用 moveToCenter()zoomToFit(),使这个组合场景在初始加载时就能完整可见。之后,每次 activeGroupId 发生变化,都会触发一次新的商品列表生成和 fake line 重建。

一个重要限制是,当前的商品获取器会忽略传入的仓库 id,并且每次都会随机返回 8 到 30 条模拟商品数据。在真实的仓库仪表盘中,这种结构可以自然映射为仓库元数据加上每个仓库对应的库存明细行,并配有稳定的地理坐标;但这个 demo 有意聚焦在连线接入与高亮行为上,而不是稳定的领域数据。

relation-graph 是如何使用的

入口组件使用 RGProvider 包裹整个 demo,从而在示例内部启用基于 hook 的图实例访问。在 MyGraph 中,RGHooks.useGraphInstance() 提供图实例,图配置为 layout.layoutName = 'fixed'defaultJunctionPoint = RGJunctionPoint.lr。这个固定布局很关键,因为可见场景是通过绝对定位的 DOM 坐标手动摆放的,而不是依赖可拖拽的图节点。

RelationGraph 承载了一个 RGSlotOnCanvas 场景。这个 slot 内包含静态的美国地图背景、绝对定位的位置标记、仓库卡片和商品卡片。每个可见端点都被包裹在带有稳定 targetIdRGConnectTarget 中,因此 relation-graph 可以把 fake line 附着到普通 HTML 元素上。这里有三类目标端点:

  • location-* 用于地图标记
  • group-* 用于仓库卡片
  • product-* 用于商品卡片

当选择发生变化时,这个示例不会去更新普通图节点或连线。相反,它会先短暂等待 slot 内容挂载完成,清空图实例,插入一个不可见的占位节点,然后通过 addFakeLines() 重绘当前连接器集合。这使 relation-graph 表现得像一个覆盖在预构建仪表盘组合之上的关系高亮层。

共享的 DraggableWindow 组件补充了运行时工具能力。借助 RGHooks.useGraphStore()useGraphInstance(),它暴露了滚轮模式切换、画布拖拽模式切换,以及通过 prepareForImageGeneration()restoreAfterImageGeneration() 实现的图片导出能力。这个示例没有结构化编辑流程;它本质上仍然是一个带运行时控制能力的查看器。

样式大多应用在外围 DOM 上,而不是 relation-graph 内部。自定义 SCSS 定义了脉冲式 .c-i-location 标记样式,以及静态地图资源 .wuxia-map-svg4us 的填充和悬停样式。relation-graph 的样式覆盖代码块虽然存在,但实际上基本没有被使用。

关键交互

点击仓库卡片会改变 activeGroupId,高亮该卡片,重新生成右侧商品列表,并为新的选择重绘全部 fake line。点击地图标记会执行同样的选择流程,因此左侧面板和地图会围绕同一个激活状态保持同步。

连接器逻辑会把一次选择扇出为多条关系。当前所有商品卡片都会生成一条回连到所选仓库卡片的曲线,而所选仓库本身还会生成一条更粗的曲线,连接到它固定的地图位置。这样就在三个不同的 DOM 表面之间形成了紧凑的主从明细联动行为。

悬浮辅助窗口补充了次级交互。用户可以把鼠标滚轮切换为滚动、缩放或禁用;把画布拖拽切换为选择、移动或禁用;还可以把当前图画布导出为图片。当前实现中的商品卡片只是表现层端点,因此并不支持从商品反向探索仓库。

关键代码片段

下面这个片段展示了图如何使用固定布局和左右连接点,从而让绝对定位的 DOM 锚点始终与地图场景对齐。

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.lr,
    layout: {
        layoutName: 'fixed'
    }
};

下面这个片段展示了核心模式:一个激活选择会被转换成多条从商品回连到所选仓库的 fake line。

if (activeGroupId) {
    warehouseProducts.forEach((product) => {
        myFakeLines.push({
            id: `line-${product.id}`,
            from: 'product-' + product.id,
            to: 'group-' + activeGroupId,
            color: 'rgb(59 130 246)',
            lineShape: RGLineShape.StandardCurve
        });
    });
}

下面这个片段展示了可见场景位于 RGSlotOnCanvas 内,而地图标记是作为连接目标注册的,而不是作为图节点存在。

<RGSlotOnCanvas>
    <div style={{ zIndex: 1, position: 'absolute', left: -900, top: 100, height: 800, width: 900, opacity: 0.6 }}>
        <MapSvg4US />
    </div>
    {/* ... */}
    <RGConnectTarget
        targetId={`location-${group.groupId}`}
        junctionPoint={RGJunctionPoint.lr}
        disableDrag={true}
        disableDrop={true}
    >

下面这个片段展示了悬浮辅助窗口并不只是装饰:它会改变图的运行时行为,并支持图片导出。

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

下面这个片段展示了当前商品面板是有意使用 mock 数据驱动的,并且每次都会重新生成,而不是按稳定的每仓库存数据加载。

export const fetchWarehouseProducts = async(myWarehouseId: string) => {
    return generateProducts();
};

const generateProducts = (min: number = 8, max: number = 30): MyProduct[] => {
    const productCount = Math.floor(Math.random() * (max - min + 1)) + min;
    const products: MyProduct[] = [];

这个示例的独特之处

与附近那些同样基于 canvas-slot 的示例相比,这个示例的突出点在于,它把三类端点组合在同一个复合面板中:固定位置的地图标记、带利用率条的仓库卡片,以及带数量和单位徽标的商品卡片。相比那些只是把单个列表连接到单个地图表面的简单 DOM 端点 demo,这种组合让示例更有仪表盘的特征。

预先整理的比较还显示出它与 interest-group 之间一个明确的行为差异。两个示例共享相同的大体接线模式,但这个版本更偏单向:选择仓库或地图标记会重新填充商品列表并重绘连接器,而商品卡片不会提供反向探索。这使它更适合作为主从明细高亮的起点,而不是对称关系浏览的起点。

相较于 scene-network-use-canvas-slot,这个示例也把交互推得更进一步。它不是在启动后挂载一组固定连接器,而是持续依据当前选择状态重建整套连线。因此,它更适合作为一种更有状态的模式,用在需要跨地图、摘要列表和明细列表进行联动聚焦的仪表盘中。

就代码实际行为而言,文章标题和源文件路径都显得有些泛化。这个实现更适合被理解为一种地图联动的 DOM 端点组合模式,尤其是因为一些标签和辅助文本仍然保留了较旧的 interest-group 表述。这种命名与实现之间的不完全匹配,也进一步说明这个示例更适合作为技术接线参考,而不是一个领域准确的库存模型。

这种模式还适用于哪里

这种模式很适合物流仪表盘:当用户选中某个仓库、配送中心或线路枢纽时,需要在不把整个页面都变成图节点的前提下,同时高亮相关库存行以及它的地理位置。

它也适用于零售区域规划、现场服务协同、区域销售覆盖和应急响应看板等场景。在这些场景中,地图上的空间锚点需要与侧边面板中的摘要卡片和明细行保持同步。

更广泛地说,只要产品团队已经拥有一个自定义 HTML 仪表盘,并希望在现有布局之上使用 relation-graph 增加关系强调、焦点同步以及可导出的视觉轨迹,这都会是一个很好的起点。