JavaScript is required

企业关系四象限固定布局

这个示例在 relation-graph 固定布局模式下构建企业关系四象限图。它根据类别列表生成 RGJsonData,通过独立布局步骤计算手工坐标,并允许用户在悬浮控制窗中实时调整间距。

构建一个使用固定坐标的四象限企业关系图

此示例构建了什么

此示例会在画布中心渲染一家公司,并在其四周放置四类关系:投资方、人物、对外投资和分支机构。每个类别都有自己的分组节点、自己的颜色以及自己的连线处理方式,因此最终呈现出来的是一种象限式企业关系图,而不是通用树图。

这里用户不会编辑图结构。主要交互是对手动布局进行实时调参:一个浮动面板允许用户修改连线长度和节点间距,图会立即重新定位。关键点在于,relation-graph 在这里被当作固定坐标的渲染载体使用,而每个节点的坐标都由自定义代码决定。

数据如何组织

数据起始于 initializeGraph() 中的内联数组:一个根公司,加上四个类别列表。generateGraphData(...) 会把这些列表转换为 RGJsonData,先创建中心节点、四个分组节点,以及所有叶子节点和连线,然后再加载到图中。

重要的预处理步骤是:初始 JSON 并不包含最终坐标。相反,生成器会添加 typequadrantKeyindex 之类的元数据,之后由 applyLayout(...) 利用这些元数据计算 x 和 y 坐标。在真实系统中,同样的结构也可以表示股东、高管、子公司、办公室、供应商、分销商,或围绕某个主实体的任意分类网络。

relation-graph 的使用方式

该图运行在 fixed 布局模式下,因此节点位置不是由 relation-graph 自动决定的。RelationGraph 被配置为矩形节点、默认无边框、左右连接点,以及默认直线,而生成出来的连线数据会覆盖各条连线的具体路径,从而把中心的曲线连接与叶子节点的正交连线组合起来。

RGProvider 提供图上下文,RGHooks.useGraphInstance() 是主要的集成入口。示例通过它使用 setJsonData(...) 加载 JSON 数据,使用 getNodes() 读取当前节点列表,使用 updateNode(...) 回写坐标,然后再通过 moveToCenter()zoomToFit() 让视口居中并适配。共享的 CanvasSettingsPanel 也会使用图实例,在运行时修改 wheelEventActiondragEventAction,并把画布导出为图片。

此示例中没有图插槽,也没有结构编辑工具。额外的 UI 来自共享子组件:DraggableWindow 提供浮动外壳,CanvasSettingsPanel 提供画布模式切换和图片导出功能。样式则通过生成的类名(如 my-nodes-invs)以及针对节点颜色、线标签背景、展开控制器和选中态连线样式的 SCSS 覆盖来自定义。

关键交互

两个范围输入驱动了该示例的特定行为。修改 leafLineLengthleafNodeGap 会更新 React state,随后一个 effect 会基于当前图节点重新执行自定义坐标计算。

辅助窗口本身可以拖动、最小化,并可切换到设置视图。在设置视图中,用户可以把滚轮行为切换为滚动、缩放或无;把拖拽行为切换为框选、移动或无;并将当前图导出为图片。

节点和连线点击处理器是存在的,但它们只会记录被点击的对象,不会控制选择、高亮或编辑。

关键代码片段

这段配置代码表明,该图被有意设置为固定模式,并使用矩形节点与左右连接点作为默认值。

const userGraphOptions: RGOptions = {
    layout: {
        layoutName: 'fixed'
    },
    defaultNodeBorderWidth: 0,
    defaultNodeShape: RGNodeShape.rect,
    defaultJunctionPoint: RGJunctionPoint.lr,
    defaultLineShape: RGLineShape.StandardStraight
};

这段代码展示了图的结构 JSON 会先被组装出来,同时每个叶子节点都会保留象限元数据,供后续布局阶段使用。

nodes.push({
    ...item,
    className: `my-nodes-${rule.nodeClassSuffix}`,
    data: { type: 'leaf', quadrantKey: rule.key, index: index }
});

lines.push(createLine(rule.rootId, item.id, {
    text: item.desc || '',
    color: rule.color,
    showEndArrow: false,
    lineShape: RGLineShape.SimpleOrthogonal
}));

这段坐标代码是手动布局逻辑的核心:它会根据类别让叶子节点向上或向下生长,并把它们放置在分组节点的左侧或右侧。

if (rule.isTop) {
    leafY = groupOriginY - (index * (LEAF_NODE_H + leafNodeGap)) + 30;
} else {
    leafY = groupOriginY + (index * (LEAF_NODE_H + leafNodeGap));
}

const leafX = rule.isLeft
    ? groupOriginX - leafLineLength - node.el_W
    : (groupOriginX + 140) + leafLineLength;

nodesPosition.push({ nodeId: node.id, x: leafX, y: leafY });

这段运行时代码展示了如何将自定义坐标回写到当前的 relation-graph 实例中。

const nodes = graphInstance.getNodes();
const nodesPosition = applyLayout(
    nodes,
    leafNodeInitialWidth,
    leafLineLength,
    leafNodeGap
);
for (const nodePos of nodesPosition) {
    graphInstance.updateNode(nodePos.nodeId, {
        x: nodePos.x,
        y: nodePos.y
    });
}

这个示例的独特之处

根据已有的对比数据,这个示例的独特之处在于,它直接展示了确定性的手动坐标分配,而不是 relation-graph 内建布局预设的效果示例。与 layout-center 相比,当节点摆放规则来自业务逻辑并且必须以显式 x、y 值回写时,它是更强的起点。与 bothway-treebothway-tree2 相比,它并不是在讲树形朝向或分支重样式,而是在讲固定模式下按类别进行四象限摆放。与 deep-each 相比,这里真正有意义的实时交互是参数化重布局,而不是点击驱动的焦点高亮。

它少见的组合也很重要:企业关系场景、围绕单个公司节点布置的四个带颜色区分的矩形象限、中心曲线连线加叶子正交带标签连线,以及一个带运行时画布工具的白色浮动辅助窗口。这个组合让它成为一个很实用的桥接示例,适合那些需要为每个类别保留可预测布局区域,而不是接受由库自动选择布局的团队。

这种模式还能应用到哪里

这种模式很适合迁移到股东图谱、企业控制关系图、供应商或客户关系概览、分支机构或区域分布图,以及要求每个类别都拥有稳定视觉区域的合规看板。generateGraphData(...)applyLayout(...) 之间的这种分离方式,也适用于坐标来自外部服务或内部算法,而不是手写四象限规则的场景。

对于把既有布局计算迁移到 relation-graph 的团队来说,这也是一种有用的迁移模式。你可以先生成一次 RGJsonData,再通过独立步骤计算坐标,最后把这些坐标推送到当前图实例中,而无需重写渲染层。