Java中树形菜单分析和实现

背景概述

  • 最近在搭建基于Spring Boot(2.0.2)种子项目框架时,遇到了树形菜单加载问题。特此把解决的问题方案记录下去,供其他小伙伴参考和日后回顾。

方案分析

  • 方案选择
    • 一次性加载完,返回前台需要的数据结构
    • 点击加载,默认记载根层级的菜单。后续点击那一级菜单加载其下的子节点
  • 方案实现
    • 在这里只分析一次性加载实现。第二种实现起来更简单。在这里略过。有兴趣可以自己研究一下。

具体实现

  • 表、数据等基础资料
    • 表结构
      这里写图片描述
    • 测试数据(忽略内容啊)
      这里写图片描述
    • Vo对象字段

      private String informationTypeId; // 菜单id
      private Integer informationTypeNum;
      private String informationTypeCode;
      private String informationTypeUrl;
      private String informationTypeName;
      private String informationTypePicBosKey;
      private Integer informationTypeLevel;
      private String type;
      private String model;
      private Boolean isEdit;
      private String parentId; // 父菜单id
      private String remark;
      private String createUserId;
      private Date createTime;
      private String modifyUserId;
      private Date modifyTime;
      private Date timeStamp;
      private List<InformationTypeEntity> childNodes = new ArrayList<>(); // 子菜单list
  • 查出所有菜单列表
    /**
     * 查询资讯菜单(一次性加载)
     *
     * @throws CustomException
     */
    @RequestMapping(value = "/findAllTypeList")
    public List<InformationTypeEntity> findAllTypeList() throws CustomException {
        // 加载所有菜单列表
        List<InformationTypeEntity> rootList = informationTypeService.findInfoTypeByType("1", null);
        List<InformationTypeEntity> list = new ArrayList<>();
        for (int i = 0; i < rootList.size(); i++) {
            InformationTypeEntity typeEntity = rootList.get(i);
            if ("-1".equals(typeEntity.getParentId())) {
                list.add(typeEntity);
            }
        }

        for (InformationTypeEntity entity : list) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return list;
    }
  • 找出根节点菜单列表
    /**
     * 查询资讯菜单(一次性加载)
     *
     * @throws CustomException
     */
    @RequestMapping(value = "/findAllTypeList")
    public List<InformationTypeEntity> findAllTypeList() throws CustomException {
        List<InformationTypeEntity> rootList = informationTypeService.findInfoTypeByType("1", null);
        List<InformationTypeEntity> list = new ArrayList<>();
        for (int i = 0; i < rootList.size(); i++) {
            InformationTypeEntity typeEntity = rootList.get(i);
            // 找出根级菜单,(约定所有根级菜单的parentId属性为-1)
            if ("-1".equals(typeEntity.getParentId())) {
                list.add(typeEntity);
            }
        }

        for (InformationTypeEntity entity : list) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return list;
    }
  • 循环找出每个子节点的菜单列表
    /**
     * 查询资讯菜单(一次性加载)
     *
     * @throws CustomException
     */
    @RequestMapping(value = "/findAllTypeList")
    public List<InformationTypeEntity> findAllTypeList() throws CustomException {
        List<InformationTypeEntity> rootList = informationTypeService.findInfoTypeByType("1", null);
        List<InformationTypeEntity> list = new ArrayList<>();
        for (int i = 0; i < rootList.size(); i++) {
            InformationTypeEntity typeEntity = rootList.get(i);
            // 找出根级菜单,(约定所有根级菜单的parentId属性为-1)
            if ("-1".equals(typeEntity.getParentId())) {
                list.add(typeEntity);
            }
        }

        // 循环根级菜单,为每个根级菜单下的子菜单list赋值。
        for (InformationTypeEntity entity : list) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return list;
    }
  • 递归解决无限层级嵌套(里面的注释我写的很清楚,英文的,大家凑合看吧。大概的思路就是除了当前子菜单后,还要考虑子菜单的子菜单,考虑用递归处理。递归结束的条件就是没有子菜单,也就是list的size为0.此种方式支持无线的嵌套)
    /**
     * Return the list of children node from percent menu id
     *
     * @param id  current menu id
     * @param rootList  all list of menus
     * @return
     * @throws CustomException
     */
    private List<InformationTypeEntity> getChildNodes(String id, List<InformationTypeEntity> rootList) throws CustomException {
        // The list of child nodes
        List<InformationTypeEntity> childList = new ArrayList<>();
        // Fill the list of child'nodes which parent id equal params of id
        for (InformationTypeEntity typeEntity : rootList) {
            if (StringUtils.isNotBlank(typeEntity.getParentId())) {
                if (id.equals(typeEntity.getParentId())) {
                    childList.add(typeEntity);
                }
            }
        }
        if (childList.size() == 0) {
            return null;
        }
        // Look up it's child node and fill
        for (InformationTypeEntity entity : childList) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return childList;
    }

总结

  • 关于树形菜单的实现方案有很多,我只是列举出比较典型的2种方案。
  • 举例的这种方案可以支持无限级嵌套
  • 举例的这种只发送一次sql,可以在一定程度上调高效率,减少数据库压力
  • 理解思路,上面的代码好看优化哈,以后有时间再补充上

测试结果如下

[
  {
    "informationTypeId": "4",
    "informationTypeNum": 1,
    "informationTypeCode": "001",
    "informationTypeUrl": "/index",
    "informationTypeName": "资讯管理",
    "informationTypePicBosKey": "pic",
    "informationTypeLevel": null,
    "type": "1",
    "model": "1",
    "isEdit": null,
    "parentId": "-1",
    "remark": null,
    "createUserId": null,
    "createTime": null,
    "modifyUserId": null,
    "modifyTime": null,
    "timeStamp": "2018-06-15 10:17:51",
    "childNodes": [
      {
        "informationTypeId": "1",
        "informationTypeNum": 1,
        "informationTypeCode": "001001",
        "informationTypeUrl": "/index1",
        "informationTypeName": "十九大管理",
        "informationTypePicBosKey": "pic1",
        "informationTypeLevel": null,
        "type": "1",
        "model": "1",
        "isEdit": null,
        "parentId": "4",
        "remark": null,
        "createUserId": null,
        "createTime": "2018-06-01 10:15:35",
        "modifyUserId": null,
        "modifyTime": null,
        "timeStamp": "2018-06-15 10:17:56",
        "childNodes": [
          {
            "informationTypeId": "5",
            "informationTypeNum": 1,
            "informationTypeCode": "001001001",
            "informationTypeUrl": "/index/index1",
            "informationTypeName": "中国梦",
            "informationTypePicBosKey": "pic",
            "informationTypeLevel": null,
            "type": "1",
            "model": "2",
            "isEdit": null,
            "parentId": "1",
            "remark": null,
            "createUserId": null,
            "createTime": null,
            "modifyUserId": null,
            "modifyTime": null,
            "timeStamp": "2018-06-15 10:18:53",
            "childNodes": [
              {
                "informationTypeId": "8",
                "informationTypeNum": 1,
                "informationTypeCode": "001001001001",
                "informationTypeUrl": "/index/index/index",
                "informationTypeName": "白日梦",
                "informationTypePicBosKey": "pic",
                "informationTypeLevel": null,
                "type": "1",
                "model": "1",
                "isEdit": null,
                "parentId": "5",
                "remark": null,
                "createUserId": null,
                "createTime": null,
                "modifyUserId": null,
                "modifyTime": null,
                "timeStamp": "2018-06-15 11:21:50",
                "childNodes": [
                  {
                    "informationTypeId": "10",
                    "informationTypeNum": 1,
                    "informationTypeCode": "001001001001001",
                    "informationTypeUrl": "/index/index/index2",
                    "informationTypeName": "李白的白日梦",
                    "informationTypePicBosKey": "pic",
                    "informationTypeLevel": null,
                    "type": "1",
                    "model": "1",
                    "isEdit": null,
                    "parentId": "8",
                    "remark": null,
                    "createUserId": null,
                    "createTime": null,
                    "modifyUserId": null,
                    "modifyTime": null,
                    "timeStamp": "2018-06-15 11:24:05",
                    "childNodes": [ { "informationTypeId": "12", "informationTypeNum": 1, "informationTypeCode": "000013123123123123123", "informationTypeUrl": "12341234123123", "informationTypeName": "李白的白日梦儿子1", "informationTypePicBosKey": "23213", "informationTypeLevel": null, "type": "1", "model": "1", "isEdit": null, "parentId": "10", "remark": null, "createUserId": null, "createTime": null, "modifyUserId": null, "modifyTime": null, "timeStamp": "2018-06-15 11:32:36", "childNodes": [ { "informationTypeId": "14", "informationTypeNum": 2, "informationTypeCode": "242424", "informationTypeUrl": "24243234234", "informationTypeName": "l李白的白日梦儿子1的儿子11111", "informationTypePicBosKey": "2424", "informationTypeLevel": null, "type": "1", "model": "2", "isEdit": null, "parentId": "12", "remark": null, "createUserId": null, "createTime": null, "modifyUserId": null, "modifyTime": null, "timeStamp": "2018-06-15 11:36:28", "childNodes": null } ] }, { "informationTypeId": "13", "informationTypeNum": 2, "informationTypeCode": "2424234234234234", "informationTypeUrl": "2424242424", "informationTypeName": "李白的白日梦儿子2", "informationTypePicBosKey": "23424", "informationTypeLevel": null, "type": "1", "model": "1", "isEdit": null, "parentId": "10", "remark": null, "createUserId": null, "createTime": null, "modifyUserId": null, "modifyTime": null, "timeStamp": "2018-06-15 11:33:07", "childNodes": null } ] },
                  {
                    "informationTypeId": "11",
                    "informationTypeNum": 2,
                    "informationTypeCode": "0010001001001002",
                    "informationTypeUrl": "/index/index/index2",
                    "informationTypeName": "杜甫的白日梦",
                    "informationTypePicBosKey": "pic",
                    "informationTypeLevel": null,
                    "type": "1",
                    "model": "2",
                    "isEdit": null,
                    "parentId": "8",
                    "remark": null,
                    "createUserId": null,
                    "createTime": null,
                    "modifyUserId": null,
                    "modifyTime": null,
                    "timeStamp": "2018-06-15 11:24:58",
                    "childNodes": null }
                ]
              },
              {
                "informationTypeId": "9",
                "informationTypeNum": 2,
                "informationTypeCode": "001001001002",
                "informationTypeUrl": "/index/index/index2",
                "informationTypeName": "黑夜梦",
                "informationTypePicBosKey": "pic",
                "informationTypeLevel": null,
                "type": "1",
                "model": "2",
                "isEdit": null,
                "parentId": "5",
                "remark": null,
                "createUserId": null,
                "createTime": null,
                "modifyUserId": null,
                "modifyTime": null,
                "timeStamp": "2018-06-15 11:22:30",
                "childNodes": null
              }
            ]
          },
          {
            "informationTypeId": "6",
            "informationTypeNum": 2,
            "informationTypeCode": "001001002",
            "informationTypeUrl": "/index/index2",
            "informationTypeName": "扶贫",
            "informationTypePicBosKey": "pic",
            "informationTypeLevel": null,
            "type": "1",
            "model": "2",
            "isEdit": null,
            "parentId": "1",
            "remark": null,
            "createUserId": null,
            "createTime": null,
            "modifyUserId": null,
            "modifyTime": null,
            "timeStamp": "2018-06-15 10:20:23",
            "childNodes": null
          }
        ]
      },
      {
        "informationTypeId": "2",
        "informationTypeNum": 2,
        "informationTypeCode": "001002",
        "informationTypeUrl": "/index2",
        "informationTypeName": "党代会议管理",
        "informationTypePicBosKey": "pic2",
        "informationTypeLevel": null,
        "type": "1",
        "model": "2",
        "isEdit": null,
        "parentId": "4",
        "remark": null,
        "createUserId": null,
        "createTime": "2018-06-13 05:48:42",
        "modifyUserId": null,
        "modifyTime": null,
        "timeStamp": "2018-06-15 10:17:54",
        "childNodes": [
          {
            "informationTypeId": "7",
            "informationTypeNum": 1,
            "informationTypeCode": "001002001",
            "informationTypeUrl": "/index/index",
            "informationTypeName": "党会管理",
            "informationTypePicBosKey": "pic111",
            "informationTypeLevel": null,
            "type": "1",
            "model": "3",
            "isEdit": null,
            "parentId": "2",
            "remark": null,
            "createUserId": null,
            "createTime": null,
            "modifyUserId": null,
            "modifyTime": null,
            "timeStamp": "2018-06-15 10:20:59",
            "childNodes": null
          }
        ]
      },
      {
        "informationTypeId": "3",
        "informationTypeNum": 3,
        "informationTypeCode": "001003",
        "informationTypeUrl": "/index3",
        "informationTypeName": "两学一做",
        "informationTypePicBosKey": "pic3",
        "informationTypeLevel": null,
        "type": "1",
        "model": "1",
        "isEdit": null,
        "parentId": "4",
        "remark": null,
        "createUserId": null,
        "createTime": null,
        "modifyUserId": null,
        "modifyTime": null,
        "timeStamp": "2018-06-15 10:17:53",
        "childNodes": null
      }
    ]
  }
]

猜你喜欢

转载自blog.csdn.net/weixin_39723544/article/details/80735877
今日推荐