树形菜单应该是很常见的了,类似下面这种,通常我们的做法是后端从数据库中查询出来数据,然后将其转为树的结构,丢给前端,前端就渲染到树组件中;
那么返给前端的数据是什么样的呢?如下所示
[ { id: 1, label: '一级 1', children: [{ id: 4, label: '二级 1-1', children: [{ id: 9, label: '三级 1-1-1' }, { id: 10, label: '三级 1-1-2' }] }] }, {xxx} ]
那么对应在数据库中的表示什么样子的呢?Subject表下图所示,如果parent_id为0的,表示是一级菜单,其他多个二级菜单的parent_id等于某个一级菜单的id,依次类推,可有有很多级别菜单
下面用java代码实现
1.工具类:
package com.protagonist.edu.utils; import com.protagonist.edu.bo.SubjectTreeNodeBO; import com.protagonist.edu.entity.Subject; import com.protagonist.responseVO.StatusCode; import com.protagonist.servicebase.exception.ProtagonistException; import org.springframework.beans.BeanUtils; import org.springframework.util.CollectionUtils; import java.util.*; /** * 用于构建树形结构 */ public class TreeUtil { /** * 默认树形菜单最顶层的pid是"0" * @param list 所有数据 * @return 树形的数据 */ public static List<SubjectTreeNodeBO> buildTree(List<Subject> list) { if (CollectionUtils.isEmpty(list)){ throw new ProtagonistException(StatusCode.ERROR,"查询的菜单数据为空,不能转为树形"); } return buildTree(list, "0"); } /** * 获取树形菜单结构 * 思路:首先遍历一次将所有的数据转化为前端需要的数据类型,然后放入到map中,以id->T对应关系 * 然后再遍历一次,这次的话判断parentId是否为0(这里暂时可以特使pid为0时表示一级菜单),是的话就是一级菜单,就放到rootTree中; * 不为0的话,说明不是一级菜单,我们就需要获取它的父菜单 * 根据pid去map中获取,然后将当前菜单放入其父菜单的子菜单中,等遍历完之后树形菜单就ok了,这种做法可以完成多级子菜单变成树形 * @param list 所有的数据 * @param pid 父id * @return 树形数据 */ public static List<SubjectTreeNodeBO> buildTree(List<Subject> list, String pid) { if (CollectionUtils.isEmpty(list)){ throw new ProtagonistException(StatusCode.ERROR,"查询的菜单数据为空,不能转为树形"); } List<SubjectTreeNodeBO> allTreeNode = new ArrayList<>(); List<SubjectTreeNodeBO> rootTree = new ArrayList<>(); Map<String, SubjectTreeNodeBO> nodeMap = new HashMap<>(); //将所有的数据都放入到map中一份 for (Subject item : list) { SubjectTreeNodeBO nodeBO = new SubjectTreeNodeBO(); BeanUtils.copyProperties(item,nodeBO); allTreeNode.add(nodeBO); nodeMap.put(item.getId(),nodeBO); } for (SubjectTreeNodeBO t : allTreeNode) { //如果父id等于传进来的pid,那么该菜单是最顶级的菜单,放入到rootTree中 if (Objects.equals(t.getParentId(), pid)){ rootTree.add(t); //如果不是顶级菜单,那就获取父菜单,然后嫁给你本BO设置到父菜单的children中 }else { SubjectTreeNodeBO parentNode = nodeMap.get(t.getParentId()); parentNode.getChildren().add(t); } } return rootTree; } }
2.subject类:
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("subject") @ApiModel(value="Subject对象", description="课程科目") public class Subject implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "课程类别ID") @TableId(value = "id", type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty(value = "类别名称") private String title; @ApiModelProperty(value = "父ID") private String parentId; }
3.SubjectTreeNodeBO类:
package com.protagonist.edu.bo; import lombok.Data; import java.util.ArrayList; import java.util.List; @Data public class SubjectTreeNodeBO { private String id; private String title; private String parentId; private List<SubjectTreeNodeBO> children = new ArrayList<>(); }
4. 测试,成功,然后配合element的Tree 树形控件一起使用,就行了