Java代码实现项目两级菜单展示(图文教程)

(142) 2024-05-18 21:01:01

本微博只适用于两层的菜单展示,写作的初衷是为了介绍递归做铺垫。

详细说明都写在了代码的注释上,不做单独说明了,希望对你有所帮助。

1.数据库设计

Java代码实现项目两级菜单展示(图文教程) (https://mushiming.com/)  第1张

为了方便大家练习,提供建表SQL如下:

-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
  `menu_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
  `name` varchar(50) DEFAULT NULL COMMENT '菜单名称',
  `url` varchar(200) DEFAULT NULL COMMENT '菜单URL',
  `perms` varchar(500) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
  `type` int(11) DEFAULT NULL COMMENT '类型   0:目录   1:菜单   2:按钮',
  `icon` varchar(50) DEFAULT NULL COMMENT '菜单图标',
  `order_num` int(11) DEFAULT NULL COMMENT '排序',
  `gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
  `gmt_modified` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='菜单管理';

2.实体类MenuDO

该实体类是在工作中总结出来的,字段较全,适用场景也比较多。但实际上,最主要的字段只有前几个:menuId、parentId、name、url。

import java.io.Serializable;
import java.util.Date;

public class MenuDO implements Serializable {
    private static final long serialVersionUID = 1L;
    // 主键
    private Long menuId;
    // 父菜单ID,一级菜单为0
    private Long parentId;
    // 菜单名称
    private String name;
    // 菜单URL
    private String url;
    // 授权(多个用逗号分隔,如:user:list,user:create)
    private String perms;
    // 类型 0:目录 1:菜单 2:按钮
    private Integer type;
    // 菜单图标
    private String icon;
    // 排序
    private Integer orderNum;
    // 创建时间
    private Date gmtCreate;
    // 修改时间
    private Date gmtModified;

    ......
    // 省略get()、set()等方法
}

3.业务类Tree<T>

public class Tree<T> {
    // 节点ID
    private String id;
    // 显示节点文本
    private String text;
    // 节点状态,open closed
    private Map<String, Object> state;
    // 节点是否被选中 true false
    private boolean checked = false;
    // 节点属性
    private Map<String, Object> attributes;
    // 节点的子节点
    private List<Tree<T>> children = new ArrayList<Tree<T>>();
    // 父ID
    private String parentId;
    // 是否有父节点
    private boolean hasParent = false;
    // 是否有子节点
    private boolean hasChildren = false;

    ......
    // 省略get()、set()等方法
}

4.service层调用的方法

返回的list结果,就是满足逻辑的tree型数据,可以直接返回给页面使用。

    public List<Tree<MenuDO>> listMenuTree() {
        String idParam = "0";		                           // 自定义顶级结点
        List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();  // 存放结果
        List<MenuDO> menuDOs = menuMapper.queryList();             // 取出数据

        // 1. 初步处理:让原始数据具备我们自定的的tree的特征
        for (MenuDO sysMenuDO : menuDOs) {
            Tree<MenuDO> tree = new Tree<MenuDO>();
            tree.setId(sysMenuDO.getMenuId().toString());
            tree.setParentId(sysMenuDO.getParentId().toString());
            tree.setText(sysMenuDO.getName());
            Map<String, Object> attributes = new HashMap<>(16);
            attributes.put("url", sysMenuDO.getUrl());
            attributes.put("icon", sysMenuDO.getIcon());
            tree.setAttributes(attributes);
            trees.add(tree);
        }

        // 2. 递归处理:根据数据库实际情况调整顶级结点的parentId值,默认为0
        List<Tree<MenuDO>> list = BuildTree.buildList(trees, idParam);

        return list;
    }

5.util类

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 递归树形结构类
 */
public class BuildTree {

    /**
     * 自定义顶级结点的parentId,返回递归树形结构
     */
    public static <T> List<Tree<T>> buildList(List<Tree<T>> nodes, String idParam) {
        // 1.非空判断
        if (nodes == null) {
            return null;
        }

        // 2.定义返回数据类型
        List<Tree<T>> topNodes = new ArrayList<Tree<T>>();

        // 3.取出每一个元素,判断它有没有父类
        for (Tree<T> children : nodes) {
            String pid = children.getParentId();

            // 3.1 pid(parentId)为空,或者等于父节点,则没有父类,直接返回
            if (pid == null || idParam.equals(pid)) {
                topNodes.add(children);
                continue;
            }

            // 3.2 否则,遍历一遍集合,找它的父类,原则:子类的parentId = 父类的id
            for (Tree<T> parent : nodes) {
                String id = parent.getId();
                if (id != null && id.equals(pid)) {

                    // 3.2.1 将子类添加到父类的children属性下
                    parent.getChildren().add(children);

                    // 3.2.2 设置子节点闭合状态:true - 关闭
                    children.setHasParent(true);
                    parent.setChildren(true);
                }
            }
        }

        // 4. 返回结果集
        return topNodes;
    }

}

6.结果展示

基于BootDo成熟的架构,小编自己搭建了一个开发框架,使用递归展示菜单栏,效果如下:

Java代码实现项目两级菜单展示(图文教程) (https://mushiming.com/)  第2张

 

更多精彩,请关注我的"今日头条号":Java云笔记
随时随地,让你拥有最新,最便捷的掌上云服务

THE END

发表回复