java实现树形菜单

Posted 今朝花落悲颜色

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java实现树形菜单相关的知识,希望对你有一定的参考价值。

最近要开发一个菜单接口,结构是父菜单下面有子菜单,子菜单下面还有子菜单,要实现树形结构返给前端。

数据表设计

CREATE TABLE `platform_menu`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单id',
  `menu_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单名称',
  `menu_value` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单值(前端路由)',
  `parent_id` bigint NULL DEFAULT NULL COMMENT '上级菜单id',
  `parent_path` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单全路径(用/隔开)',
  `func_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '功能类型(0菜单1按钮2区块)',
  `order_num` int NULL DEFAULT NULL COMMENT '排序',
  `icon` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '图标',
  `created_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `created_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `updated_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
  `updated_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单信息 ' ROW_FORMAT = Dynamic;

创建树形结构工具类

import cn.hutool.core.collection.CollUtil;
import org.apache.commons.compress.utils.Lists;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author ws
 * @date 2021/9/5
 */
public class BaseTreeHelper {

    /**
     * @param <T> 节点类型
     * @param <E> 节点id的类型
     * @return 树形结构列表
     * @author ws
     * @date 2021/9/5
     * 根据所有树节点列表,生成含有所有树形结构的列表
     **/
    public static <T extends TreeNode<E, T>, E> List<T> generateTrees(List<T> data) {
        if (CollUtil.isEmpty(data)) {
            return Lists.newArrayList();
        }
        //将集合中所有数据按照父Id进行分组,放入Map中,Map<parntId, List<T>>
        Map<String, List<T>> groupByParentIdMap = data.stream().collect(Collectors.groupingBy(item -> item.root() ? "" : Objects.toString(item.getPid())));
        //将集合中所有数据以数据Id为key,放入Map中,Map<id,T>
        Map<String, T> dataMap = data.stream().collect(Collectors.toMap(item -> item.getId().toString(), t -> t));
        List<T> resp = Lists.newArrayList();
        //遍历数据,将子节点放入对应父节点Children属性中
        groupByParentIdMap.forEach((parentId, values) -> {
            if (dataMap.containsKey(parentId)) {
                List<T> child = dataMap.get(parentId).getChildren();
                if (CollUtil.isEmpty(child)) {
                    child = Lists.newArrayList();
                }
                child.addAll(values);
                dataMap.get(parentId).setChildren(child);
            } else {
                resp.addAll(values);
            }
        });
        return resp;
    }


    /**
     * @param parent 父节点
     * @param <T>    实际节点类型
     * @return 叶子节点
     * @author ws
     * @date 2021/9/5
     * 获取指定树节点下的所有叶子节点
     **/
    public static <T extends TreeNode<E, T>, E> List<T> getLeafs(T parent) {
        List<T> leafs = new ArrayList<>();
        fillLeaf(parent, leafs);
        return leafs;
    }

    /**
     * @param parent 父节点
     * @param leafs  叶子节点列表
     * @param <T>    实际节点类型
     * @author ws
     * @date 2021/9/5
     * 将parent的所有叶子节点填充至leafs列表中
     **/
    private static <T extends TreeNode<E, T>, E> void fillLeaf(T parent, List<T> leafs) {
        List<T> children = parent.getChildren();
        // 如果节点没有子节点则说明为叶子节点
        if (CollUtil.isEmpty(children)) {
            leafs.add(parent);
            return;
        }
        // 递归调用子节点,查找叶子节点
        for (T child : children) {
            fillLeaf(child, leafs);
        }
    }
}

 创建UserMenusVo对象,里面可以只写需要返回的属性字段就行。

import com.knight.common.core.utils.tree.TreeNode;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

/**
 * @author ws
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserMenusVo implements TreeNode<Long, UserMenusVo> {

    private static final long serialVersionUID = 1L;

    /**
     * id
     */
    @ApiModelProperty("菜单id")
    private Long id;


    /**
     * 菜单名称
     */
    @ApiModelProperty("菜单名称")
    private String name;

    /**
     * 菜单值(前端路由)
     */
    @ApiModelProperty("菜单值")
    private String menuValue;

    /**
     * 上级菜单id
     */
    @ApiModelProperty("上级菜单")
    private Long pid;

    /**
     * 菜单全路径(用/隔开)
     */
    @ApiModelProperty("菜单全路径")
    private String parentPath;

    /**
     * 功能类型(0菜单1按钮2区块)
     */
    @ApiModelProperty("功能类型")
    private String funcType;

  

    /**
     * 排序
     */
    @ApiModelProperty("排序")
    private Integer orderNum;

    /**
     * 图标
     */
    @ApiModelProperty("图标")
    private String icon;

    @ApiModelProperty("子菜单")
    private List<UserMenusVo> children;

    @Override
    @ApiModelProperty("父节点")
    public Boolean root() {
        return pid == null;
    }
}

controller,返回的pid就是parentId。


 public Result<List<UserMenusVo>> listMenuAll() {
//查询所有菜单
  List<UserMenusVo> menus = this.listMenuAll();
//返回树形结构
  return Result.ok(BaseTreeHelper.generateTrees(menus));
}

 

 

以上是关于java实现树形菜单的主要内容,如果未能解决你的问题,请参考以下文章

java树形菜单实现

java实现树形菜单

java如何实现二级树形菜单动态显示。要求加载页面时显示一级,点击一级菜单再去数据库查找出二级菜单

使用Java8轻松实现菜单树形数据

生成树形菜单

(记录贴)java后台实现树形菜单+分页返回数据