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

基于OrgChart库动态生成JSON树形结构实现组织架构图

问题描述

我正在基于bubble.io开发应用,使用OrgChart库构建组织架构图插件,需要从应用数据源动态生成带父子关系的节点结构。任意节点的子节点数量不固定,得通过循环和递归函数实现动态生成。

我尝试过动态创建JSON对象,但没能写出循环内的递归函数来程序化生成子节点。目前已经从data_source中获取到首个节点,但后续还有数千条数据,其中children_text字段(可包含多个条目)存储着父节点的唯一ID,也可以根据解决方案要求添加parent字段。

组织架构数据示例

当前代码

var len = properties.data_source.length();
var i;
var j;
let objj = {};
let hello = [];

let user = properties.data_source.get(0, len);

//extracting string properties from data_source
var name = user[0].get("name_text");
var title = user[0].get("designation_text");
var image = user[0].get("picture_image");

$(function() {

  var styleTag = $(' <style> .orgchart { background: #fff; }.orgchart td.left, .orgchart td.right, .orgchart td.top { border-color: #aaa; } .orgchart td>.down { background-color: #aaa; } .orgchart .middle-level .title { -color: 699; }.orgchart .middle-level .content { border-color: #006699; }.orgchart .product-dept .title { background-color: #009933; } .orgchart .product-dept .content { border-color: #009933; } .orgchart .rd-dept .title { background-color: #993366; } .orgchart .rd-dept .content { border-color: #993366; } .orgchart .pipeline1 .title { background-color: #996633; } .orgchart .pipeline1 .content { border-color: #996633; } .orgchart .frontend1 .title { background-color: #cc0066; }.orgchart .frontend1 .content { border-color: #cc0066; } .orgchart .second-menu-icon {      transition: opacity .5s;      opacity: 0;      right: -20px;      top: 80px;  transform: rotate(270deg);    z-index: 2;      position: relative;    }    .orgchart .second-menu-icon::before { background-color: rgba(68, 157, 68, 0.5); }    .orgchart .second-menu-icon:hover::before { background-color: #449d44; }    .orgchart .node:hover .second-menu-icon { opacity: 1; }    .orgchart .node .second-menu {      display: none;      position: absolute;      top: 110px;  transform: scaleX(-1)rotate(90deg);    right: -70px;      border-radius: 35px;      box-shadow: 0 0 10px 1px #999;      background-color: #fff;      z-index: 1;    }    .orgchart .node .second-menu .avatar {      width: 60px;      height: 60px;      border-radius: 30px;      float: left;      margin: 5px;   } </style>');
  $('html > head').append(styleTag);

  var datascource = {
    'name': name,
    'title': title,
    'children': [{
        'name': 'Bo Miao',
        'title': 'department manager',
        'className': 'middle-level',
        'children': [{
            'name': 'Li Jing',
            'title': 'senior engineer',
            'className': 'product-dept'
          },
          {
            'name': 'Li Xin',
            'title': 'senior engineer',
            'className': 'product-dept',
            'children': [{
                'name': 'To To',
                'title': 'engineer',
                'className': 'pipeline1'
              },
              {
                'name': 'Fei Fei',
                'title': 'engineer',
                'className': 'pipeline1'
              },
              {
                'name': 'Xuan Xuan',
                'title': 'engineer',
                'className': 'pipeline1'
              }
            ]
          }
        ]
      },
      {
        'name': 'Su Miao',
        'title': 'department manager',
        'className': 'middle-level',
        'children': [{
            'name': 'Pang Pang',
            'title': 'senior engineer',
            'className': 'rd-dept'
          },
          {
            'name': 'Hei Hei',
            'title': 'senior engineer',
            'className': 'rd-dept',
            'children': [{
                'name': 'Xiang Xiang',
                'title': 'UE engineer',
                'className': 'frontend1'
              },
              {
                'name': 'Dan Dan',
                'title': 'engineer',
                'className': 'frontend1'
              },
              {
                'name': 'Zai Zai',
                'title': 'engineer',
                'className': 'frontend1'
              }
            ]
          }
        ]
      }
    ]
  };

  $('#chart-container').orgchart({
    'data': datascource,
    'visibleLevel': 2,
    'nodeContent': 'title',
    'direction': 'l2r',
    'nodeID': 'id',
    'createNode': function($node, data) {
      var secondMenuIcon = $(' <i>', {
        'class': 'oci oci-info-circle second-menu-icon',
        click: function() {
          $(this).siblings('.second-menu').toggle();
        }
      });
      var secondMenu = '<div class="second-menu"><img class="avatar" src=' + image + '></div>';
      $node.append(secondMenuIcon).append(secondMenu);
    }
  });
});
解决方案

步骤1:预处理数据源,构建节点映射

先把所有数据转换成易于查询的结构,用节点ID作为键存储每个节点的完整信息,同时处理children_text字段拆分父ID:

// 转换数据源为标准数组,并构建ID映射表
const allNodes = [];
const nodeMap = {};

for (let k = 0; k < user.length; k++) {
  const item = user[k];
  const node = {
    id: item.get("id"), // 替换为数据源实际的唯一ID字段名
    name: item.get("name_text"),
    title: item.get("designation_text"),
    image: item.get("picture_image"),
    // 拆分children_text为父ID数组(假设是逗号分隔格式)
    parentIds: item.get("children_text")?.split(",").map(id => id.trim()) || []
  };
  allNodes.push(node);
  nodeMap[node.id] = node;
}

步骤2:递归生成树形结构

编写递归函数,从根节点开始为每个节点匹配子节点:

// 递归构建子节点树
function buildChildren(node) {
  // 找到所有父ID包含当前节点ID的子节点
  const children = allNodes.filter(child => child.parentIds.includes(node.id));
  
  // 为每个子节点递归生成子树
  return children.map(child => {
    const childNode = {...child};
    childNode.children = buildChildren(childNode);
    // 无子女则移除children字段(可选,OrgChart兼容空数组)
    if (childNode.children.length === 0) delete childNode.children;
    return childNode;
  });
}

// 定位根节点(假设根节点无有效父ID)
const rootNode = allNodes.find(node => 
  node.parentIds.length === 0 || !node.parentIds.some(id => nodeMap[id])
);

// 生成OrgChart所需的动态数据源
const dynamicDataSource = {
  ...rootNode,
  children: buildChildren(rootNode)
};

步骤3:替换原代码中的数据源

把硬编码的datascource替换为动态生成的结构,并修正节点图片的取值逻辑:

$('#chart-container').orgchart({
  'data': dynamicDataSource,
  'visibleLevel': 2,
  'nodeContent': 'title',
  'direction': 'l2r',
  'nodeID': 'id',
  'createNode': function($node, data) {
    var secondMenuIcon = $(' <i>', {
      'class': 'oci oci-info-circle second-menu-icon',
      click: function() {
        $(this).siblings('.second-menu').toggle();
      }
    });
    // 使用当前节点的image字段,而非固定取第一个节点的图片
    var secondMenu = '<div class="second-menu"><img class="avatar" src=' + data.image + '></div>';
    $node.append(secondMenuIcon).append(secondMenu);
  }
});

注意事项

  • 确保替换item.get("id")为数据源实际的唯一ID字段名
  • 如果children_text不是逗号分隔格式,需调整拆分逻辑匹配实际数据格式
  • 根节点的判断逻辑可根据实际业务调整(比如特定ID标识根节点)

内容的提问来源于stack exchange,提问作者ibrar Adil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 07:45:36