Web端常用界面 树形菜单
- 1. 说明
- 2. 表设计
- 3. 普通递归
- 4. 流式递归语法糖
1. 说明
树形菜单在web后台管理系统, 权限管理中基本上很容易见到。如:csdn的个人后台管理系统。
只不过这个目录只有两层。三层的如下:
甚至可以套n层。
为什么要递归?
因为数据库表的设计,菜单会用一张表设计。菜单表的两个关键列,主键id和其父级id。使用父级id引用主键id 来作为父级菜单。即主键自关联。
2. 表设计
create table acl_permission
(id char(19) default '' not null comment '编号' primary key,pid char(19) default '' not null comment '所属上级',name varchar(20) default '' not null comment '名称',type tinyint(3) default 0 not null comment '类型(1:菜单,2:按钮)',permission_value varchar(50) null comment '权限值',path varchar(100) null comment '访问路径',component varchar(100) null comment '组件路径',icon varchar(50) null comment '图标',status tinyint null comment '状态(0:禁止,1:正常)',is_deleted tinyint(1) unsigned default 0 not null comment '逻辑删除 1(true)已删除, 0(false)未删除',gmt_create datetime null comment '创建时间',gmt_modified datetime null comment '更新时间'
)comment '权限';
实体类
package top.bitqian.rye.acl.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;import java.io.Serializable;
import java.util.Date;
import java.util.List;import lombok.Data;/*** 权限* * @author echo lovely* @date 2021-01-21 19:31:47*/
@Data
@TableName("acl_permission")
public class PermissionEntity implements Serializable {private static final long serialVersionUID = 1L;/*** 编号*/@TableIdprivate String id;/*** 所属上级*/private String pid;/*** 名称*/private String name;/*** 类型(1:菜单,2:按钮)*/private Integer type;/*** 权限值*/private String permissionValue;/*** 访问路径*/private String path;/*** 组件路径*/private String component;/*** 图标*/private String icon;/*** 状态(0:禁止,1:正常)*/private Integer status;/*** 逻辑删除 1(true)已删除, 0(false)未删除*/@TableField(fill = FieldFill.INSERT)private Integer isDeleted;/*** 创建时间*/@TableField(fill = FieldFill.INSERT)private Date gmtCreate;/*** 更新时间*/@TableField(fill = FieldFill.INSERT_UPDATE)private Date gmtModified;/*** 等级 根节点为0*/@TableField(exist = false)private String level;/*** 子级结点*/@TableField(exist = false)private List<PermissionEntity> children;}
3. 普通递归
@Testvoid contextLoad1() {// 表中所有数据List<PermissionEntity> list = permissionService.list();List<PermissionEntity> nodeList = new ArrayList<>();for (PermissionEntity item : list) {if ("1".equals(item.getPid())) {// 一级父节点nodeList.add(item);// 为每个父结点设置 childrenitem.setChildren(collectTreeData(item, list));}}nodeList.forEach(System.out::println);}/**** @param permission 每个父级结点* @param list 所有数据* @return 封装好的数据 子节点*/private List<PermissionEntity> collectTreeData(PermissionEntity permission, List<PermissionEntity> list) {List<PermissionEntity> tmpList = new ArrayList<>();// 遍历所有元素for (PermissionEntity item : list) {// 找到子节点if (item.getPid().equals(permission.getId())) {// 将儿子收集tmpList.add(item);// 继续递归collectTreeData(item, list);}}// 设置父级结点的儿子permission.setChildren(tmpList);return tmpList;}
4. 流式递归语法糖
public List<PermissionEntity> getMenuTree() {// 所有树形菜单List<PermissionEntity> list = this.list();// pid = 1 的根节点List<PermissionEntity> nodeList = list.stream().filter(r -> "1".equals(r.getPid())).collect(Collectors.toList());// 为 pid=1 设置子节点。递归。return nodeList.stream().peek(r -> {r.setLevel("0");// 为每个根结点 设置 childrenList<PermissionEntity> dataList = collectTreeData(r, list);r.setChildren(dataList);}).collect(Collectors.toList());}private List<PermissionEntity> collectTreeData(PermissionEntity permission, List<PermissionEntity> list) {return list.stream().filter(r -> r.getPid().equals(permission.getId())).peek(r -> {// set value..r.setChildren(collectTreeData(r, list));}).collect(Collectors.toList());}