基于数据库数组构建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
相关产品推荐
相关产品推荐

