【业务功能篇69】Springboot 树形菜单栏功能设计

news2025/1/13 20:02:05

业务场景: 系统的界面,前端设计的时候,一般会给一个菜单栏,顶部横向以及左侧纵向的导航栏菜单,这里后端返回菜单栏的时候,就涉及层级父子项的问题,所以返回数据的时候,我们需要按照树化形式返回菜单栏数据,方便用户进行解析读取 

 菜单栏的数据表设计

CREATE TABLE `dwr_quality_management_menu_bar` (
  `uid` int NOT NULL AUTO_INCREMENT,
  `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '树节点id',
  `parent_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '父id,树化时使用',
  `resource_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '唯一资源id',
  `name_cn` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '中文名',
  `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '路径',
  `level` tinyint DEFAULT NULL COMMENT '菜单层级',
  `show_order` int DEFAULT NULL COMMENT '显示顺序',
  `description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '描述',
  `is_home_page` tinyint(1) DEFAULT '2' COMMENT '首页标识:1:是,2:否',
  `update_date` datetime DEFAULT NULL COMMENT '更改时间',
  `picture_id` varchar(255) DEFAULT NULL,
  `picture_url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3845 DEFAULT CHARSET=utf8 COMMENT='菜单栏配置';

控制层

@RestController
@RequestMapping(value = "/management/menu", produces = {"application/json;charset=UTF-8"})
@Validated
public class ManagementMenuController {
    @Autowired(required=false) 
    private ManagementMenuDelegate delegate;  

    @RequestMapping(
    		value = "/getAllMenu", 
    		produces = { "application/json" }, 
    		method = RequestMethod.GET)
    public ResponseVo getAllMenu( @RequestParam(value = "p", required = false) String p) 
            {
    	    
		return delegate.getAllMenu(p);
    }

}

 

服务层 接口

public interface ManagementMenuDelegate {
    ResponseVo getAllMenu(String pdu) ;
    	
}

 

服务层 接口实现 

@Service
public class ManagementMenuDelegateImpl implements ManagementMenuDelegate {

    @Resource
    private MenuBarService menuBarService;

    /**
     * getAllMenu
     * 
     * @param p 
     * @return ResponseVo
     */
    @Override
    public ResponseVo getAllMenu(String p) {
        return ResponseUtils.successResponse(menuBarService.getCompleteMenu(p), "");
    }

}

 

服务层 具体接口

public interface MenuBarService {

    /**
     * 获取所有菜单
     *
     * @param p
     * @return List<ManagementMenuBarVo>
     */
    List<ManagementMenuBarVo> getCompleteMenu(String p);

}

服务层 具体接口实现

@Service
@Slf4j
public class MenuBarServiceImpl implements MenuBarService {

    @Resource
    private ManagementMenuBarMapper menuBarMapper;

    /**
     * getCompleteMenu
     * 
     * @param p
     * @return List<ManagementMenuBarVo>
     */
    @Override
    public List<ManagementMenuBarVo> getCompleteMenu(String p) {
        List<ManagementMenuBarVo> result = TreeUtils.treeing(getAllFlattingMenu());
        if (ObjectUtils.isNotEmpty(p)) {
            for (ManagementMenuBarVo barVo : result) {
                if (barVo.getNameCn().equals(p)) {
                    return barVo.getChildren();
                }
            }
            return new ArrayList<>();
        }
        return result;
    }


        /**
     * getAllFlattingMenu
     * 
     * @return List<ManagementMenuBarVo>
     */
    private List<ManagementMenuBarVo> getAllFlattingMenu() {
        QueryWrapper<ManagementMenuBar> queryWrapper = new QueryWrapper<>();
        // 排序规则与getBarRootNode()一致
        queryWrapper.lambda()
            .orderBy(true, true, ManagementMenuBar::getLevel, ManagementMenuBar::getParentId,
                ManagementMenuBar::getShowOrder);
        List<ManagementMenuBar> menuBarList = menuBarMapper.selectList(queryWrapper);

        return convertToVo(menuBarList);
    }

        /**
     * convertToVo
     * 
     * @param list list
     * @return List<ManagementMenuBarVo>
     */
    private List<ManagementMenuBarVo> convertToVo(List<ManagementMenuBar> list) {
        List<ManagementMenuBarVo> voList = new LinkedList<>();
        for (ManagementMenuBar menuBar : list) {
            ManagementMenuBarVo vo = new ManagementMenuBarVo();
            MyBeanUtils.shallowCopy(menuBar, vo);
            vo.initSuperNodeId();
            vo.initSuperParentNodeId();
            voList.add(vo);
        }
        return voList;
    }

}

dao层 mapper

@Mapper
public interface ManagementMenuBarMapper extends BaseMapper<ManagementMenuBar> {


}

实体类DO表


@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@NoArgsConstructor
@TableName("dwr_quality_management_menu_bar")
public class ManagementMenuBar implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "uid" ,type = IdType.AUTO)
    private String uid;

    /**
     * 树节点id
     */
    @TableField("id")
    private String id;

    /**
     * 父id,树化时使用
     */
    @TableField("parent_id")
    private String parentId;

    /**
     * 唯一资源id
     */
    @TableField("resource_id")
    private String resourceId;

    /**
     * 图片ID
     */
    @TableField("picture_id")
    private String pictureId;

    /**
     * 中文名
     */
    @TableField("name_cn")
    private String nameCn;

    /**
     * 路径
     */
    @TableField("url")
    private String url;

    /**
     * 图片访问路径url
     */
    @TableField("picture_url")
    private String pictureUrl;

    /**
     * 菜单层级
     */
    @TableField("level")
    private Integer level;

    /**
     * 显示顺序
     */
    @TableField("show_order")
    private Integer showOrder;

    /**
     * 描述
     */
    @TableField("description")
    private String description;

    /**
     * 首页标识:1:是,2:否
     */
    @TableField("is_home_page")
    private Integer isHomePage;



    /**
     * 更改时间
     */
    @TableField("update_date")
    private Date updateDate;


}

 

传输前端类VO

  • 作为传输转换类,其中就需要继承我们自定义的一个树化类结构 


@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ManagementMenuBarVo extends TreeNode<ManagementMenuBarVo> implements Serializable {

    private static final long serialVersionUID = 1L;

    private String uid;
    private String id;

    private String parentId;

    private String resourceId;

    private String pictureId;

    private String pictureUrl;

    private String nameCn;

    private String url;

    private Integer level;

    private String description;

    private Integer isHomePage;

    private Integer isAccessible;

    private String functionName;

    private Integer showOrder;

    /**
     *
     */

    /**
     * initSuperNodeId
     * 
     */
    @Override
    public void initSuperNodeId() {
        super.setNodeId(this.id);
    }

    /**
     * 初始化TreeNode的parentNodeId属性
     */
    @Override
    public void initSuperParentNodeId() {
        super.setParentNodeId(this.parentId);
    }

}

 

树化工具节点类


/**
 * 树化工具实体
 *
 */
@Data
public abstract class TreeNode<T> {

    // 仅用于树化,序列化时忽略
    @JsonIgnore
    private String nodeId;

    @JsonIgnore
    private String parentNodeId;

    @JsonIgnore
    private T parent;

    private List<T> children = new ArrayList<>();

    /**
     * 初始化TreeNode的NodeId属性
     */
    public abstract void initSuperNodeId();

    /**
     * 初始化TreeNode的parentNodeId属性
     */
    public abstract void initSuperParentNodeId();

}

树化操作工具类 

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

package com.xxx.utils;


/**
 * 树化工具
 *
 */
public class TreeUtils {

    /**
     * 树化
     * @param treeNodes 扁平节点数据
     * @return 树化的节点列表(列表中每一节点都是一棵树)
     */
    public static <T extends TreeNode<T>> List<T> treeing(List<T> treeNodes) {
        Map<String, T> nodeMap = new HashMap<>(treeNodes.size());
        treeNodes.forEach(t -> nodeMap.put(t.getNodeId(), t));

        List<T> result = new ArrayList<>();
        for (T treeNode : treeNodes) {
            T parent = nodeMap.get(treeNode.getParentNodeId());
            if (parent != null) {
                parent.getChildren().add(treeNode);
                treeNode.setParent(parent);
                continue;
            }
            // 仅添加顶层节点
            result.add(treeNode);
        }

        return result;
    }

    /**
     * 扁平化
     * @param treeNodes 树化数据
     * @return 扁平数据
     */
    public static <T extends TreeNode<T>> List<T> flattening(List<T> treeNodes) {
        if (!CollectionUtils.isEmpty(treeNodes)) {
            return new ArrayList<>();
        }
        List<T> newList = new LinkedList<>();
        Queue<T> queue = new LinkedList<>(treeNodes);
        while (!queue.isEmpty()) {
            T pollMenuBar = queue.poll();
            List<T> children = pollMenuBar.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                queue.addAll(children);
            }
            newList.add(pollMenuBar);
        }
        return newList;
    }
}

  •  返回前端的JSON数据格式如下
  • 每个菜单栏层级就比较清晰,通过children集合嵌套当前菜单栏的子菜单栏

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/897095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【水文学法总结】河道内生态流量计算方法(含MATLAB实现代码)

生态流量&#xff08;Ecological Flow, EF&#xff09; 是指维持河道内生态环境所需要的水流流量。生态流量计算方法众多&#xff0c;主要分为水文学方法、栖息地模拟法、水力学方法、整体法等&#xff0c;各方法多用于计算维持河道生态平衡的最小生态流量&#xff08;Minimum …

容斥原理 博弈论(多种Nim游戏解法)

目录 容斥原理容斥原理的简介能被整除的数&#xff08;典型例题&#xff09;实现思路代码实现扩展&#xff1a;用DPS实现 博弈论博弈论中的相关性质博弈论的相关结论先手必败必胜的证明Nim游戏&#xff08;典型例题&#xff09;代码实现 台阶-Nim游戏&#xff08;典型例题&…

STM32/AT32 MCO管脚输出时钟配置

前言&#xff1a;最近在学以太网通讯&#xff0c;发现RMII接口配置的时钟管脚有MCU自己输出&#xff0c;想要看看是怎么输出的&#xff0c;对此进行记录 1、交接项目项目上使用的是PA8管脚来输出时钟50MHZ&#xff0c;提供给上面refclk。 先看手册 PA8的复用功能具备将MCU时钟…

控制方法笔记

基于模型的控制&#xff1a;LQR&#xff0c;模型建立如果不准确&#xff0c;会给控制带来不确定性。 运动学和动力学&#xff1f; 大货车很多参数不了解的话&#xff0c;有时候不如用运动学。所以说&#xff0c;建模不精准不如用运动学。 LQR 模型是状态空间线性的。目标函…

SpringBoot + Vue 微人事(十)

职位管理前后端接口对接 先把table中的数据展示出来&#xff0c;table里面的数据实际上是positions里面的数据&#xff0c;就是要给positions:[] 赋上值 可以在methods中定义一个initPosition方法 methods:{//定义一个初始化positions的方法initPositions(){//发送一个get请求…

GB28181设备接入侧如何对接外部编码后音视频数据并实现预览播放

技术背景 我们在对接GB28181设备接入模块的时候&#xff0c;遇到这样的技术诉求&#xff0c;好多开发者期望能提供编码后&#xff08;H.264/H.265、AAC/PCMA&#xff09;数据对接&#xff0c;确保外部采集设备&#xff0c;比如无人机类似回调过来的数据&#xff0c;直接通过模…

《Go 语言第一课》课程学习笔记(八)

基本数据类型 Go 原生支持的数值类型有哪些&#xff1f; Go 语言的类型大体可分为基本数据类型、复合数据类型和接口类型这三种。 其中&#xff0c;我们日常 Go 编码中使用最多的就是基本数据类型&#xff0c;而基本数据类型中使用占比最大的又是数值类型。 整型 Go 语言的…

MVCC 是否彻底解决了事物的隔离性 ?

目录 1. 什么是 MVCC 2. MVCC 是否彻底解决了事物的隔离性 3. MySQL 中如何实现共享锁和排他锁 4. MySQL 中如何实现悲观锁和乐观锁 1. 什么是 MVCC MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09;是一种多版本并发控制机制&…

QGraphicsItem 实例4 图元的旋转、缩放、切变和位移

实现图元的旋转、缩放、切变和位移 效果&#xff1a; mainwindow.h #ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QGraphicsView> #include <QGraphicsScene> #include <QFrame> #include <QGraphicsView> #inclu…

部门用户权限应用设计及创建&一些实用小细节

前言 之前边做应用程序边完善数据库表&#xff0c;应用程序做出来了&#xff0c;但是数据库表也面目全非了&#xff0c;很多数据库字段都要重新设计&#xff0c;踩了个坑但是也是一种学习&#xff1a;每次代码开发也好&#xff0c;应用程序搭建也好&#xff0c;先做好数据库表…

深入探究 Java 8 新特性:Optional 类允许为空的对象类

深入探究 Java 8 新特性&#xff1a;Optional 类 概念说明 ​ 在 Java 8 中&#xff0c;引入了许多令人兴奋的新特性&#xff0c;其中之一就是 Optional 类。这个类主要用于解决空指针异常&#xff08;NullPointerException&#xff09;的问题&#xff0c;提供了一种更安全和…

kali换源之后更新

kali换源之后更新 1.更新索引 apt-get update 2.更新软件 apt-get upgrade 3.删除缓存包 apt-get clean

【网络教程】如何创建/添加钉钉机器人以及如何获取机器人的Token/Secret

文章目录 创建钉钉机器人添加钉钉机器人获取机器人的Token/Secret相关网站创建钉钉机器人 这里以PC端的操作为例,按照如下操作进行 访问 钉钉开放平台选择机器人选项卡,点击右上角的创建应用,这里会有一个弹窗,我这里选择的是继续使用旧版,如图按照要求填写相关信息创建自…

【Influxdb数据迁移,从windos移到linux】

前提——保证两边的版本不要相差太多 1、windows的导出G:\influxdb\2为暂存的目录 D:\influxdb-1.8.3_windows_amd64\influxdb-1.8.3-1>influxd backup -portable -database mydb G:\influxdb\2导出之后会有一堆文件 全部上传到/var/lib/influxdb这个目录下。这个应该是默…

vue3中使用第三方插件mitt实现任意组件通讯

vue3中使用第三方插件mitt实现任意组件通讯 组件通讯是vue3组合式开发的核心之一&#xff0c;现在我在写代码时&#xff0c;一个组件的代码超过了200行&#xff0c;基本都会拆分组件。组件拆分后&#xff0c;组件之间的通讯就很重要&#xff0c;总结了一下&#xff0c;目前有这…

阿里云席明贤:明天的视频云2.0

编者按 本文是“解构多媒体新常态”系列文章的第二篇&#xff0c;LiveVideoStack对话了阿里云视频云负责人席明贤&#xff08;花名右贤&#xff09;。面对风云变幻的内外环境&#xff0c;阿里云在视频云赛道是坚定向前的&#xff0c;在与右贤的接触中&#xff0c;他给我留下非常…

回归预测 | MATLAB实现GWO-BP灰狼算法优化BP神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现GWO-BP灰狼算法优化BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GWO-BP灰狼算法优化BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍程序…

基于医疗领域数据微调LLaMA——ChatDoctor模型

文章目录 ChatDoctor简介微调实战下载仓库并进入目录创建conda环境并配置环境&#xff08;安装相关依赖&#xff09;下载模型文件微调数据微调过程全量微调基于LoRA的微调基于微调后的模型推理 ChatDoctor简介 CHatDoctor论文&#xff1a; ChatDoctor: A Medical Chat Model F…

JavaScript:交集和差集的应用场景

在集合A和集合B中&#xff0c;属于集合A&#xff0c;同时也属于集合B的元素组成的集合&#xff0c;就是交集。 在A中所有不属于集合B元素&#xff0c;组合成集合&#xff0c;就是差集。 那么在平时的开发中&#xff0c;如何使用差集和交集来解决问题呢&#xff1f; 现在有这…

Fixed Price Incentive Fee (FPIF)

总价加激励费用 (FPIF)。这种总价合同为买方和卖方提供了一定的灵活性&#xff0c;允许一定的绩 效偏离&#xff0c;并对实现既定目标给予相关的财务奖励&#xff08;通常取决于卖方的成本、进度或技术 绩效&#xff09;。FPIF 合同中会设置价格上限&#xff0c;高于此价格上限…