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

基于数据库数组构建win_name为父节点的PHP TreeView求助

搞定TreeView:从数据库数组到父子树形结构

我太懂这种折腾半天却没结果的挫败感了!你手里握着数据库返回的数组,想把它转成以win_name为父节点、item_name为子节点的TreeView,试了各种方法都卡壳——别慌,咱们从最基础的地方入手,先把数据理清楚,再一步步实现,不管用原生JS还是插件都能搞定。

第一步:先把原始数据转换成树形结构对象

不管用什么方式渲染TreeView,正确的数据结构都是核心。数据库返回的是扁平数组,咱们先把它转换成「父节点对应子节点数组」的结构。

假设你的数据库返回的数组是这样的(我模拟一个示例):

// 模拟数据库返回的扁平数组
const dbFlatData = [
  { win_name: "用户管理窗口", item_name: "新增用户" },
  { win_name: "用户管理窗口", item_name: "编辑用户" },
  { win_name: "用户管理窗口", item_name: "删除用户" },
  { win_name: "权限设置窗口", item_name: "角色分配" },
  { win_name: "权限设置窗口", item_name: "菜单权限" },
  { win_name: "数据统计窗口", item_name: "月度报表" }
];

咱们用简单的forEach把它转成树形结构:

// 转换为父子结构的对象
const treeStructure = {};

dbFlatData.forEach(item => {
  // 如果当前父节点(win_name)还没在对象里,先初始化一个空数组
  if (!treeStructure[item.win_name]) {
    treeStructure[item.win_name] = [];
  }
  // 把对应的子节点(item_name)推到数组里
  treeStructure[item.win_name].push(item.item_name);
});

转换后treeStructure的结构会是这样:

{
  "用户管理窗口": ["新增用户", "编辑用户", "删除用户"],
  "权限设置窗口": ["角色分配", "菜单权限"],
  "数据统计窗口": ["月度报表"]
}

第二步:用原生JS生成TreeView(不用插件)

如果不想依赖第三方插件,用原生JS就能快速渲染出带折叠/展开功能的TreeView。

首先在页面上准备一个容器:

<!-- 页面上的TreeView容器 -->
<div id="tree-container"></div>

然后写渲染函数:

function renderTreeView(treeData) {
  const container = document.getElementById('tree-container');
  const rootUl = document.createElement('ul');

  // 遍历每个父节点
  Object.keys(treeData).forEach(parentName => {
    // 创建父节点的li元素
    const parentLi = document.createElement('li');
    // 父节点标题(可点击折叠)
    const parentTitle = document.createElement('span');
    parentTitle.textContent = parentName;
    parentTitle.style.cssText = "cursor: pointer; font-weight: bold;";
    
    // 创建子节点的ul元素
    const childUl = document.createElement('ul');
    // 遍历子节点并添加
    treeData[parentName].forEach(childName => {
      const childLi = document.createElement('li');
      childLi.textContent = childName;
      childUl.appendChild(childLi);
    });

    // 添加折叠/展开事件
    parentTitle.addEventListener('click', () => {
      childUl.style.display = childUl.style.display === 'none' ? 'block' : 'none';
    });

    // 把标题和子节点ul加到父li里
    parentLi.appendChild(parentTitle);
    parentLi.appendChild(childUl);
    rootUl.appendChild(parentLi);
  });

  // 把整个树加到容器里
  container.appendChild(rootUl);
}

// 调用渲染函数
renderTreeView(treeStructure);

这样就能得到一个基础的可折叠TreeView了,样式可以根据自己的需求调整。

如果想用jstree插件:适配它的数据格式

如果你之前试jstree没成功,大概率是数据格式没匹配上。jstree需要特定的text和children字段结构,咱们转换一下:

// 转换为jstree需要的数据格式
const jstreeCompatibleData = [];

// 先收集所有不重复的父节点
const uniqueParents = [...new Set(dbFlatData.map(item => item.win_name))];

uniqueParents.forEach(parentName => {
  jstreeCompatibleData.push({
    text: parentName,
    children: dbFlatData
      .filter(item => item.win_name === parentName)
      .map(childItem => ({ text: childItem.item_name }))
  });
});

// 初始化jstree
$('#tree-container').jstree({
  'core': {
    'data': jstreeCompatibleData
  }
});

这样配置后jstree就能正确识别父子结构了。

为什么之前的方法可能没成功?

  • 数据结构没处理对:很多插件或自定义渲染都需要层级化的数据,而不是扁平数组,这是最常见的问题;
  • 插件字段映射错误:比如jstree默认用text显示节点名称,如果你直接传win_name和item_name而没映射,它就识别不了;
  • 循环逻辑有漏洞:手动写foreach/while时,可能没处理重复的父节点,导致生成重复的父节点。

先把数据转对,再去做渲染,问题就迎刃而解啦!

内容的提问来源于stack exchange,提问作者Jeff Petersen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:00:32