You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现GoJS树形图:含子节点水平排布,叶子节点垂直排布?

实现GoJS混合排布树形图(含子节点水平、叶子节点垂直)

核心需求:所有层级中,含子节点的父节点,其子节点采用水平排布;无子女的叶子节点,同级节点采用垂直排布,同时保持AlignmentStart对齐模式。

实现方案:自定义混合树形布局

通过继承GoJS的TreeLayout,重写节点排布逻辑,区分不同类型节点的布局规则。

1. 定义节点与链接模板

// 节点模板
myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, "RoundedRectangle", { strokeWidth: 2, fill: "#ffffff" },
      new go.Binding("fill", "color")),
    $(go.TextBlock, { margin: 8, font: "14px sans-serif" },
      new go.Binding("text", "key"))
  );

// 链接模板(正交折线)
myDiagram.linkTemplate =
  $(go.Link, go.Link.Orthogonal,
    $(go.Shape, { strokeWidth: 2, stroke: "#666666" }));

2. 自定义混合布局类

function MixedTreeLayout() {
  go.TreeLayout.call(this);
  this.alignment = go.TreeLayout.AlignmentStart; // 保持AlignmentStart对齐
}
go.Diagram.inherit(MixedTreeLayout, go.TreeLayout);

// 重写commitNodes方法,自定义子节点排布逻辑
MixedTreeLayout.prototype.commitNodes = function() {
  // 先执行父类的基础布局逻辑
  go.TreeLayout.prototype.commitNodes.call(this);

  // 遍历所有节点顶点,调整子节点排布规则
  this.network.vertexes.each(vertex => {
    const currentNode = vertex.node;
    if (!currentNode) return;

    const childVertices = vertex.treeChildren;
    if (childVertices.count === 0) return;

    // 判断当前节点的所有子节点是否都是叶子节点
    const allChildrenAreLeaves = childVertices.every(v => v.node.children.count === 0);

    if (allChildrenAreLeaves) {
      // 所有子节点都是叶子:垂直排布
      childVertices.forEach(v => {
        v.direction = 90; // 垂直向下方向
        v.spacing = new go.Size(20, 60); // 水平间距20,垂直间距60
      });
    } else {
      // 子节点包含非叶子节点:水平排布
      childVertices.forEach(v => {
        v.direction = 0; // 水平向右方向
        v.spacing = new go.Size(60, 20); // 水平间距60,垂直间距20
      });
    }
  });

  // 重新计算布局
  this.network.commitLayers();
};

3. 初始化Diagram与示例数据

// 初始化画布
const myDiagram = $(go.Diagram, "myDiagramDiv", {
  layout: new MixedTreeLayout(),
  initialContentAlignment: go.Spot.Center,
  allowZoom: true,
  allowPan: true
});

// 动态树形数据(支持多层嵌套)
myDiagram.model = new go.TreeModel([
  { key: "Root", color: "#F5B041" },
  { key: "A", parent: "Root", color: "#85C1E9" },
  { key: "A1", parent: "A", color: "#82E0AA" },
  { key: "A2", parent: "A", color: "#82E0AA" },
  { key: "B", parent: "Root", color: "#85C1E9" },
  { key: "B1", parent: "B", color: "#82E0AA" },
  { key: "B2", parent: "B", color: "#82E0AA" },
  { key: "B3", parent: "B", color: "#82E0AA" },
  { key: "C", parent: "Root", color: "#85C1E9" },
  { key: "C1", parent: "C", color: "#82E0AA" },
  { key: "C1a", parent: "C1", color: "#F8C471" },
  { key: "C1b", parent: "C1", color: "#F8C471" }
]);

关键逻辑说明

  • AlignmentStart对齐:通过布局初始化时设置this.alignment = go.TreeLayout.AlignmentStart,确保所有层级的节点沿起始边对齐(水平布局时左对齐,垂直布局时顶部对齐)
  • 动态适配:每次节点增删或数据更新时,布局会重新计算,自动适配新的树形结构
  • 场景覆盖:
    • 父节点含非叶子子节点:子节点水平排布
    • 父节点所有子节点都是叶子:子节点垂直排布
    • 多层嵌套结构:递归适配每个层级的布局规则

内容的提问来源于stack exchange,提问作者Tanmay Shah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:25:17