Spring Boot + Mybatis Plus实现树状菜单的方法

下面我会详细讲解一下“Spring Boot + Mybatis Plus实现树状菜单的方法”的完整攻略。

一、实现思路

  1. 首先,在数据库中准备好菜单表,并设计好菜单表的结构,一般会包含菜单id、父级菜单id、菜单名称、菜单路径等字段。

  2. 使用Mybatis Plus的父子关系注解,将菜单表转化成实体类,并继承Mybatis Plus提供的Model类。

  3. 编写Mapper层的接口以及对应的XML文件,实现对菜单表的增删改查操作。

  4. 定义一个常量ROOT_ID代表顶级菜单的父级id,然后使用递归算法查询并组装出一个树形结构的菜单列表。

  5. 在前端页面中使用递归算法将树形结构的菜单列表渲染出来。

二、代码实现

1. 菜单表结构

菜单表的结构需要包含以下字段:

id          bigint  not null comment '菜单id',
parent_id   bigint  comment '父级菜单id',
name        varchar comment '菜单名称',
url         varchar comment '菜单路径',
primary key (id)

2. 菜单实体类

使用Mybatis Plus提供的@TableName@TableId@TableField注解,将菜单表转化成实体类,并继承Mybatis Plus提供的Model类。

@Data
@TableName("menu")
public class Menu extends Model<Menu> {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableField(value = "parent_id")
    private Long parentId;

    @TableField(value = "name")
    private String name;

    @TableField(value = "url")
    private String url;
}

3. Mapper层接口以及对应的XML文件

public interface MenuMapper extends BaseMapper<Menu> {
}
<mapper namespace="com.example.mapper.MenuMapper">

    <select id="selectTreeMenu" resultType="com.example.entity.Menu">
        select * from menu where parent_id = #{id};
    </select>

</mapper>

4. 递归算法查询并组装出树形结构的菜单列表

@Service
public class MenuService {

    private static final Long ROOT_ID = 0L;

    @Autowired
    private MenuMapper menuMapper;

    /**
     * 查询树形结构的菜单列表
     */
    public List<Menu> selectTreeMenu() {
        // 查询顶级菜单列表
        List<Menu> rootMenuList = menuMapper.selectList(
                new QueryWrapper<Menu>()
                        .lambda()
                        .eq(Menu::getParentId, ROOT_ID));

        // 递归查询子菜单
        for (Menu rootMenu : rootMenuList) {
            List<Menu> childMenuList = selectChildMenu(rootMenu);
            rootMenu.setChildren(childMenuList);
        }

        return rootMenuList;
    }

    /**
     * 递归查询子菜单
     */
    private List<Menu> selectChildMenu(Menu menu) {
        List<Menu> childMenuList = menuMapper.selectList(
                new QueryWrapper<Menu>()
                        .lambda()
                        .eq(Menu::getParentId, menu.getId()));

        for (Menu childMenu : childMenuList) {
            List<Menu> grandchildrenMenuList = selectChildMenu(childMenu);
            childMenu.setChildren(grandchildrenMenuList);
        }

        return childMenuList;
    }
}

5. 前端页面渲染

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>树形菜单</title>
    <style>
        ul {
            list-style: none;
        }

        li {
            padding: 10px;
            margin: 5px 0;
            cursor: pointer;
        }

        li:hover {
            background-color: lightgray;
        }

        .open {
            background: url('../img/down_arrow.png') no-repeat;
            background-position: 10px center;
        }

        .close {
            background: url('../img/right_arrow.png') no-repeat;
            background-position: 10px center;
        }
    </style>
</head>
<body>
<div id="treeMenu"></div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    Vue.component('TreeMenuNode', {
        props: ['node'],
        template: `
            <li :class="{'open': node.children&&node.children.length>0, 'close': !node.children||node.children.length===0}" @click="toggle">
                {{node.name}}
                <ul v-if="node.children">
                    <tree-menu-node v-for="child in node.children" :key="child.id" :node="child"></tree-menu-node>
                </ul>
            </li>
        `,
        methods: {
            toggle() {
                if (this.node.children && this.node.children.length > 0) {
                    this.$set(this.node, 'children', []);
                } else {
                    axios.get('/treeMenu/' + this.node.id).then(res => {
                        this.$set(this.node, 'children', res.data);
                    });
                }
            }
        }
    });

    var app = new Vue({
        el: '#treeMenu',
        data() {
            return {
                treeMenu: []
            };
        },
        mounted() {
            axios.get('/treeMenu').then(res => {
                this.treeMenu = res.data;
            });
        }
    });
</script>
</body>
</html>

三、示例说明

1. 递归查询子菜单过程

假设有如下的菜单列表数据:

id parent_id name url
1 0 一级菜单1 /menu/1
2 0 一级菜单2 /menu/2
3 1 二级菜单1 /menu/1/1
4 3 三级菜单1 /menu/1/1/1
5 3 三级菜单2 /menu/1/1/2
6 2 二级菜单2 /menu/2/1

刚开始查询顶级菜单列表时,SQL语句为:

select * from menu where parent_id = 0;

执行结果为:

id parent_id name url
1 0 一级菜单1 /menu/1
2 0 一级菜单2 /menu/2

在递归查询一级菜单的子菜单时,将当前菜单的id作为查询条件,SQL语句为:

select * from menu where parent_id = 1;

执行结果为:

id parent_id name url
3 1 二级菜单1 /menu/1/1

然后继续递归查询二级菜单的子菜单,将当前菜单的id作为查询条件,SQL语句为:

select * from menu where parent_id = 3;

执行结果为:

id parent_id name url
4 3 三级菜单1 /menu/1/1/1
5 3 三级菜单2 /menu/1/1/2

此时三级菜单已经是最底层的菜单了,递归结束,将查询出的子菜单列表赋值给二级菜单的children属性:

List<Menu> grandchildrenMenuList = selectChildMenu(childMenu);
childMenu.setChildren(grandchildrenMenuList);

这样在递归回到一级菜单时,二级菜单的children属性已经被赋值,此时一级菜单的子菜单列表就是二级菜单的列表。

2. 渲染树形列表

使用Vue组件,并使用递归算法将树形结构的菜单列表渲染出来。

首先,在HTML中定义Vue根节点的id为treeMenu,在Vue的data中定义一个treeMenu数组,然后在mounted钩子中使用axios发送异步请求,获取后端返回的树形菜单列表赋值给treeMenu

var app = new Vue({
    el: '#treeMenu',
    data() {
        return {
            treeMenu: []
        };
    },
    mounted() {
        axios.get('/treeMenu').then(res => {
            this.treeMenu = res.data;
        });
    }
});

接下来定义一个名为TreeMenuNode的Vue组件,这个组件包含一个名为node的prop,用于接收从父组件传递过来的菜单节点,然后在组件的模板中,使用v-if判断当前节点是否存在子菜单列表,如果有,则渲染一个ul列表,并用v-for递归渲染子菜单。

在点击li标签时判断当前节点是否已经展开,如果已经展开,则将节点的children属性设置为空数组,否则向后端发送异步请求获取子菜单列表,并将列表赋值给节点的children属性。

Vue.component('TreeMenuNode', {
    props: ['node'],
    template: `
        <li :class="{'open': node.children&&node.children.length>0, 'close': !node.children||node.children.length===0}" @click="toggle">
            {{node.name}}
            <ul v-if="node.children">
                <tree-menu-node v-for="child in node.children" :key="child.id" :node="child"></tree-menu-node>
            </ul>
        </li>
    `,
    methods: {
        toggle() {
            if (this.node.children && this.node.children.length > 0) {
                this.$set(this.node, 'children', []);
            } else {
                axios.get('/treeMenu/' + this.node.id).then(res => {
                    this.$set(this.node, 'children', res.data);
                });
            }
        }
    }
});

最后,将定义好的Vue组件使用<tree-menu-node>标签在HTML中排列成树形结构即可。

<div id="treeMenu"></div>
<script>
    // ... Vue组件定义过程 ...
</script>

这样就能够实现树形菜单的展示了。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot + Mybatis Plus实现树状菜单的方法 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • AngularJS中的按需加载ocLazyLoad示例

    AngularJS是一个流行的JavaScript框架,但是对于大型应用程序,为了提高性能,我们需要按需加载JavaScript文件。在AngularJS中,我们可以使用ocLazyLoad库来实现按需加载。在这里,我们将提供一个完整的攻略来讲解AngularJS中的按需加载ocLazyLoad示例。 需求分析 在介绍操作步骤之前,我们需要先分析一下我们的需…

    other 2023年6月25日
    00
  • 饥荒联机洞穴设置及常见问题的解决方法

    饥荒联机洞穴设置及常见问题的解决方法 洞穴设置 在饥荒联机游戏中,洞穴探索一直是重要的内容之一。在联机模式下,如果想要完成大型挑战,需要玩家们共同探索洞穴。在这里,给大家介绍如何设置饥荒联机中的洞穴探索: 首先,你需要确保你的游戏设置为联机游戏模式。在游戏主界面选择“联机游戏”,然后输入你要加入的服务器IP。这样你就可以成功进入游戏. 进入联机游戏后,在游戏…

    other 2023年6月27日
    00
  • JS批量获取参数构建JSON参数对象

    下面是“JS批量获取参数构建JSON参数对象的完整攻略”,包括基本原理、实现方法和两个示例说明。 基本原理 在 JavaScript 中,可以使用对象字面量语法来创建 JSON 对象。要构建 JSON 参数对象,需要批量获取参数并将其存储到一个对象中。可以使用循环、条件语句等控制结构来实现这一目标。 实现方法 实现批量获取参数构建 JSON 参数对象可以按照…

    other 2023年5月5日
    00
  • 深入理解java重载和重写

    深入理解Java重载和重写 什么是Java重载? Java中的重载指的是在同一个类中可以定义具有相同名称但参数不同的多个方法。即同一个方法名可以用于多个不同的方法,通过参数的不同或类型的不同来区分它们。Java重载可以提高代码的可读性和重用性,方便用户根据自己的需要选择相应的方法。 public class Calculator { public int a…

    other 2023年6月26日
    00
  • excel表格怎么设置打开进入页面布局视图?

    当你打开Excel表格时,默认情况下会进入“普通视图”模式,但你可以通过以下步骤将其更改为“页面布局视图”模式: 打开Excel表格并选择要设置页面布局视图的工作表。 在Excel菜单栏中,点击“视图”选项卡。 在“视图”选项卡中,找到“视图”组,并点击“页面布局”按钮。这将切换到页面布局视图模式。 示例说明1:假设你有一个包含大量数据的工作表,并且你想在打…

    other 2023年9月5日
    00
  • PHP中全局变量global和$GLOBALS[]的区别分析

    PHP中全局变量global和$GLOBALS[]的区别分析 在PHP中,全局变量是在函数外部定义的变量,可以在整个脚本中访问。而global关键字和$GLOBALS数组都用于在函数内部访问全局变量,但它们有一些区别。 使用global关键字 global关键字用于在函数内部引用全局变量。它的使用方法是在函数内部使用global关键字声明需要引用的全局变量,…

    other 2023年7月28日
    00
  • csssprites介绍

    以下是关于CSS Sprites的完整攻略,包括基本介绍、实现步骤、示例说明等内容。 1. 基本介绍 CSS Sprites是一种优化网页性能的技术,它可以将多个小图片合并成一张大图片,然后使用CSS的background-position属性来显示需要的部分。这样可以减少HTTP请求次数,从而提高网页的加载速度。 2. 实现步骤 以下是使用CSS Spri…

    other 2023年5月10日
    00
  • 在cmd命令行里进入和退出Python程序的方法

    在CMD命令行中,进入和退出Python程序需要使用Python解释器。下面是进入和退出Python程序的完整攻略。 进入Python程序 要进入Python程序,我们首先需要在CMD命令行中打开Python解释器。这可以通过输入python命令来实现。打开Python解释器后,我们就可以在命令行中开始运行Python代码了。 示例代码: C:\> p…

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部