基于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
相关产品推荐
相关产品推荐

