【尚筹网项目】 五、【后台】 菜单维护

本文详细介绍了如何使用ZTree在Java环境下实现菜单管理,包括树形结构的基础知识,如节点类型、数据库表示、Java类表示及按钮操作规则。接着展示了页面显示树形结构的完整流程,从数据库查询、数据组装到页面展示,以及ZTree的配置和图标修改。此外,还涵盖了添加、修改和删除菜单节点的前后端实现,提供了详细的代码示例。
摘要由CSDN通过智能技术生成

菜单维护


一、树形结构基础知识

(1) 节点类型

在这里插入图片描述

(2) 在数据库表中表示树形结构

① 创建菜单的数据库表
create table t_menu
(
	id int(11) not null auto_increment, 
	pid int(11), 
	name varchar(200), 
	url varchar(200),
	icon varchar(200), 
	primary key (id)
);
② 插入数据
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('1',NULL,'系统权限菜单','glyphicon
glyphicon-th-list',NULL);
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('2','1',' 控 制 面 板 ','glyphicon
glyphicon-dashboard','main.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('3','1','权限管理','glyphicon glyphicon
glyphicon-tasks',NULL);
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('4','3',' 用 户 维 护 ','glyphicon
glyphicon-user','user/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('5','3',' 角 色 维 护 ','glyphicon
glyphicon-king','role/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('6','3',' 菜 单 维 护 ','glyphicon
glyphicon-lock','permission/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('7','1',' 业 务 审 核 ','glyphicon
glyphicon-ok',NULL);
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('8','7',' 实 名 认 证 审 核 ','glyphicon
glyphicon-check','auth_cert/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('9','7',' 广 告 审 核 ','glyphicon
glyphicon-check','auth_adv/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('10','7',' 项 目 审 核 ','glyphicon
glyphicon-check','auth_project/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('11','1',' 业 务 管 理 ','glyphicon
glyphicon-th-large',NULL);
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('12','11',' 资 质 维 护 ','glyphicon
glyphicon-picture','cert/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('13','11',' 分 类 管 理 ','glyphicon
glyphicon-equalizer','certtype/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('14','11',' 流 程 管 理 ','glyphicon
glyphicon-random','process/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('15','11',' 广 告 管 理 ','glyphicon
glyphicon-hdd','advert/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('16','11',' 消 息 模 板 ','glyphicon
glyphicon-comment','message/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('17','11',' 项 目 分 类 ','glyphicon
glyphicon-list','projectType/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('18','11',' 项 目 标 签 ','glyphicon
glyphicon-tags','tag/index.htm');
insert into `t_menu` (`id`, `pid`, `name`, `icon`, `url`) values('19','1',' 参 数 管 理 ','glyphicon
glyphicon-list-alt','param/index.htm');
③ 关联方式

子节点通过 pid 字段关联到父节点的 id 字段,建立父子关系。

在这里插入图片描述

(3) 在 Java 类中表示树形结构

① 基本方式

在 Menu 类中使用 List<Menu> children 属性存储 当前节点的子节点

② 为了配合 zTree 所需要添加的属性
pid 属性:找到父节点

name 属性:作为节点名称

icon 属性:当前节点使用的图标

open 属性:控制节点是否默认打开

url 属性:点击节点时跳转的位置

(4) 按钮增删改查的规则

evel 0:根节点
	添加子节点
	
level 1:分支节点
	修改
	添加子节点
	没有子节点:可以删除
	有子节点:不能删除
	
level 2:叶子节点
	修改
	删除

二、 菜单维护:页面显示树形结构

(1) 思路

数据库查询全部Java 对象组装页面上使用 zTree 显示


(2) 代码:逆向工程

生成后和之前一样,放到该放的位置
在这里插入图片描述

在这里插入图片描述

<table tableName="t_menu" domainObjectName="Menu" />

(3) 逆向生成的 Menu 实体类需要做一些调整:

添加两个属性,并生成get set方法,以及构造器,tostring()等方法

// 存储子节点的集合,初始化是为了避免空指针异常
private List<Menu> children = new ArrayList<>();

// 控制节点是否默认为打开装,设置为 true 表示默认打开
private Boolean open = true;

(4) 将数据在 Java 代码中组装成树形结构

① MenuHandler

controller 为直接写为 restcontroller , 所以不用加 responsebody
在这里插入图片描述

@RequestMapping("/menu/get/whole/tree.json")
 public ResultEntity<Menu> getAll() {

     // 查询全部的menu对象
     List<Menu> menuList = menuService.getAll();

     // 声明一个变量用来存储找到的根节点
     Menu root = null;

     // 创建 Map 对象用来存储 id 和 Menu 对象的对应关系便于查找父节点
     Map<Integer,Menu> menuMap = new HashMap<>();

     // 遍历 menuList 找出所有 id 所对应的 menu
     for (Menu menu : menuList) {

         // 获取当前菜单的id
         Integer id = menu.getId();

         // 填充menuMap: id所对应的menu对象
         menuMap.put(id,menu);
     }


     for (Menu menu : menuList) {

         // 获取父节点id
         Integer pid = menu.getPid();

         // 若父节点为空,则该节点为根节点
         if (pid == null) {
             // 赋值根节点
             root = menu;

             continue;
         }

         // 若父节点不为空,则找出对应关系
         Menu father = menuMap.get(pid);

         // 将当前节点放入父节点的子节点列表中
         father.getChildren().add(menu);
     }

     // 根节点包含了整个树形结构,返回根节点就是返回整个树
     return ResultEntity.successWithData(root);
 }
② MenuService

在这里插入图片描述

 public List<Menu> getAll();
③ MenuServiceImpl

在这里插入图片描述

@Override
public List<Menu> getAll() {
    return menuMapper.selectByExample(new MenuExample());
}

(5) 代码:跳转页面

在这里插入图片描述

<mvc:view-controller path="/menu/to/page.html" view-name="menu-page" />

(6) 引入 zTree 环境

在这里插入图片描述

在这里插入图片描述

<link rel="stylesheet" href="ztree/zTreeStyle.css"/>
<script type="text/javascript" src="ztree/jquery.ztree.all-3.5.min.js"></script>

(7) 页面上使用 zTree 初步显示树形结构(假数据)

// 1.创建 JSON 对象用于存储对 zTree 所做的设置
var setting = {	};

// 2.准备生成树形结构的 JSON 数据
var zNodes =[
   { name:"父节点1 - 展开", open:true,
       children: [
           { name:"父节点11 - 折叠",
               children: [
                   { name:"叶子节点111"},
                   { name:"叶子节点112"},
                   { name:"叶子节点113"},
                   { name:"叶子节点114"}
               ]},
           { name:"父节点12 - 折叠",
               children: [
                   { name:"叶子节点121"},
                   { name:"叶子节点122"},
                   { name:"叶子节点123"},
                   { name:"叶子节点124"}
               ]},
           { name:"父节点13 - 没有子节点", isParent:true}
       ]},
   { name:"父节点2 - 折叠",
       children: [
           { name:"父节点21 - 展开", open:true,
               children: [
                   { name:"叶子节点211"},
                   { name:"叶子节点212"},
                   { name:"叶子节点213"},
                   { name:"叶子节点214"}
               ]},
           { name:"父节点22 - 折叠",
               children: [
                   { name:"叶子节点221"},
                   { name:"叶子节点222"},
                   { name:"叶子节点223"},
                   { name:"叶子节点224"}
               ]},
           { name:"父节点23 - 折叠",
               children: [
                   { name:"叶子节点231"},
                   { name:"叶子节点232"},
                   { name:"叶子节点233"},
                   { name:"叶子节点234"}
               ]}
       ]},
   { name:"父节点3 - 没有子节点", isParent:true}

];

// 3.初始化树形结构
$.fn.zTree.init($("#treeDemo"), setting, zNodes);

(8) 代码:在页面上使用真实数据显示树形结构

// 准备生成树形结构的 JSON 数据
$.ajax({
    url: "menu/get/whole/tree.json",
    type: "post",
    dataType: "json",
    success: function (response) {

        // 获取响应的操作结果
         var result = response.operationResult;

         if (result == "SUCCESS") {

             // 创建 JSON 对象用于存储对 zTree 所做的设置
             var setting = {	};

             // 从响应体中获取用来生成树形结构的 JSON 数据
             var zNodes = response.queryData;

             // 初始化树形结构
             $.fn.zTree.init($("#treeDemo"), setting, zNodes);
         }

         if (result == "FAILED") {
             layer.msg(response.message);
         }
    }
});

在这里插入图片描述

(9) 代码:修改默认图标为真实图标

在这里插入图片描述

function myAddDiyDom(treeId, treeNode) {
    // treeId是整个树形结构附着的ul标签的id
    console.log("treeId=" + treeId);

    // 当前树形节点的全部的数据,包括从后端查询得到的Menu对象的全部属性
    console.log(treeNode);

    // zTree生成图标的规则
    // 例子:treeDemo_7_ico
    // 解析:ul标签的id_当前节点的序号_功能
    // 提示:"ul标签的id_当前节点的序号"可以通过访问treeNode的tId属性得到
    // 根据id的生成规则拼接出来span标签的id
    var spanId = treeNode.tId + "_ico";

    // 根据id的生成规则拼接出来span的class
    // 根据控制图标的span标签的id找到这个span标签
    // 删除旧的class
    // 添加新的class
    $("#" + spanId).removeClass()
                   .addClass(treeNode.icon);
}

在这里插入图片描述

(10) 实现“点了不跑”

在这里插入图片描述

// 创建 JSON 对象用于存储对 zTree 所做的设置
var setting = {
    "view": {
        "addDiyDom": myAddDiyDom
    },
    "data": {
        "key": {
            "url": "maomi" //使页面不跳转
        }
    }
};

(11) 代码:显示按钮组

① 思路和步骤
第一步:控制<span>A</span>是否显示

第二步:明确具体按钮的添加规则

第三步:准备好按钮的 HTML 标签

第四步:根据按钮规则把按钮填充到 span 中
② myRemoveHoverDom(treeId, treeNode)函数

在这里插入图片描述

// 在鼠标离开节点范围时删除按钮组
function myRemoveHoverDom(treeId, treeNode) {
    // 拼接按钮组的 id
    var btnGroupId = treeNode.tId + "_btnGrp";
    // 移除对应的元素
    $("#"+btnGroupId).remove();
}
③ myAddHoverDom(treeId, treeNode)函数
// 在鼠标移入节点范围时添加按钮组
function myAddHoverDom(treeId, treeNode) {
    // 按钮组的标签结构:<span><a><i></i></a><a><i></i></a></span>
    // 按钮组出现的位置:节点中 treeDemo_n_a 超链接的后面
    // 为了在需要移除按钮组的时候能够精确定位到按钮组所在 span,需要给 span 设置有规律的id
    var btnGroupId = treeNode.tId + "_btnGrp";
    
    // 判断一下以前是否已经添加了按钮组
    if ($("#" + btnGroupId).length > 0) {
        return;
    }
    
    // 准备各个按钮的 HTML 标签
    var addBtn = "<a id='" + treeNode.id + "' class='btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' title='添加子节点'>&nbsp;&nbsp;<i class='fafa - fwfa - plusrbg'></i></a>";
    var removeBtn = "<a id='" + treeNode.id + "' class='btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' title=' 删 除 节 点 '>&nbsp;&nbsp;<i class='fafa - fwfa - timesrbg'></i></a>";
    var editBtn = "<a id='" + treeNode.id + "' class='btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' title=' 修 改 节 点 '>&nbsp;&nbsp;<i class='fafa - fwfa - editrbg'></i></a>";
    
    // 获取当前节点的级别数据
    var level = treeNode.level;
    
    // 声明变量存储拼装好的按钮代码
    var btnHTML = "";
    
    // 判断当前节点的级别
    if (level == 0) {
    // 级别为 0 时是根节点,只能添加子节点
        btnHTML = addBtn;
    }
    
    if (level == 1) {
    // 级别为 1 时是分支节点,可以添加子节点、修改
        btnHTML = addBtn + " " + editBtn;
        // 获取当前节点的子节点数量
        var length = treeNode.children.length;
        // 如果没有子节点,可以删除
        if (length == 0) {
            btnHTML = btnHTML + " " + removeBtn;
        }
    }
    
    if (level == 2) {
        // 级别为 2 时是叶子节点,可以修改、删除
        btnHTML = editBtn + " " + removeBtn;
    }
    // 找到附着按钮组的超链接
    var anchorId = treeNode.tId + "_a";
    // 执行在超链接后面附加 span 元素的操作
    $("#" + anchorId).after("<span id='" + btnGroupId + "'>" + btnHTML + "</span>");
}
④ 代码:把生成树形结构的代码封装到函数

在这里插入图片描述

menu-page.jsp 页面上调用函数即可

$(function(){
	// 调用专门封装好的函数初始化树形结构
	generateTree();
});

三、 菜单维护:添加子节点

① 思路

在这里插入图片描述


② 给“+”按钮绑定单击响应函数

在这里插入图片描述

// 为 '+' 号创建点击事件
$("#treeDemo").on("click",".addBtn",function () {

    // 添加子节点,当前节点就是添加子节点后的父节点,保存到全局变量中
    window.pid = this.id;

    // 打开模态框
    $("#menuAddModal").modal("show");

    // 禁止页面跳转
    return false;
});

③ 给添加子节点的模态框中的保存按钮绑定单击响应函数
// 添加子节点:点击保存后发送ajax请求
$("#menuSaveBtn").click(function () {

    // 收集表单项中用户输入的数据
    var name = $("#menuAddModal [name=name]").val();
    var url = $("#menuAddModal [name=url]").val();
    // radio要收集的是被选中的那一个
    var icon = $("#menuAddModal [name=icon]:checked").val();

    $.ajax({
        url: "menu/save.json",
        type: "post",
        data: {
            "pid": window.pid,
            "name": name,
            "url": url,
            "icon": icon
        },
        dataType: "json",
        success: function (response) {

            var result = response.operationResult;

            if (result == "SUCCESS") {

                layer.msg("添加成功!");

                // 刷新菜单页面
                generateTree();
            }

            if(result == "FAILED") {

                layer("添加失败: " + response.message);
            }

        },
        error: function (response) {
            layer.msg((response.status+" "+response.statusText));
        }
    });

    // 关闭模态框
    $("#menuAddModal").modal("hide");

    // 清空表单
    // jQuery 对象调用 click()函数,里面不传任何参数,相当于用户点击了一下
    $("#menuResetBtn").click();
});

④ 后端代码 MenuHandler

在这里插入图片描述

// 添加子节点
@RequestMapping("/menu/save.json")
public ResultEntity<String> addMenu(Menu menu) {
    // 执行添加子节点
    menuService.addMenu(menu);

    return ResultEntity.successWithoutData();
}

⑤ 后端代码 MenuService

在这里插入图片描述

void addMenu(Menu menu);

⑥ 后端代码 MenuServiceImpl

在这里插入图片描述

@Override
public void addMenu(Menu menu) {
    menuMapper.insert(menu);
}

四、菜单维护:修改菜单节点

① 思路

在这里插入图片描述


② 给 修改 按钮绑定单击响应函数

在这里插入图片描述

// 为 修改节点 按钮创建点击事件
$("#treeDemo").on("click",".editBtn",function () {

    // 将当前节点的 id 保存到全局变量
    window.id = this.id;

    // 显示模态框
    $("#menuEditModal").modal("show");

    // 获取 zTreeObj 对象
    var zTreeObj = $.fn.zTree.getZTreeObj("treeDemo");

    // 根据 id 属性查询节点对象
    // 用来搜索节点的属性名
    var key = "id";

    // 用来搜索节点的属性值
    var value = window.id;

    var currentNode = zTreeObj.getNodeByParam(key, value);

    // 回显内容
    $("#menuEditModal [name=name]").val(currentNode.name);
    $("#menuEditModal [name=url]").val(currentNode.url);
    // 回显 radio 可以这样理解:被选中的 radio 的 value 属性可以组成一个数组,
    // 然后再用这个数组设置回 radio,就能够把对应的值选中
    $("#menuEditModal [name=icon]").val([currentNode.icon]);

    return false;
});

③ 给更新节点的模态框中的更新按钮绑定单击响应函数
// 为修改模态框中 更新 按钮绑定单击事件
$("#menuEditBtn").click(function () {

    // 收集信息
    var name = $("#menuEditModal [name=name]").val();
    var url = $("#menuEditModal [name=url]").val();
    var icon = $("#menuEditModal [name=icon]:checked").val();

    $.ajax({
        url: "menu/update.json",
        type: "post",
        data: {
            id: window.id,
            name: name,
            url: url,
            icon: icon
        },
        dataType: "json",
        success: function (response) {

            var result = response.operationResult;

            if (result == "SUCCESS") {

                layer.msg("修改成功!");

                // 刷新菜单页面
                generateTree();
            }

            if(result == "FAILED") {

                layer("修改失败: " + response.message);
            }
        },
        error: function (response) {
            layer.msg((response.status+" "+response.statusText));
        }
    });

    // 隐藏模态框
    $("#menuEditModal").modal("hide");
});

④ 后端代码 MenuHandler

在这里插入图片描述

// 修改菜单
@RequestMapping("/menu/update.json")
public ResultEntity<String> editMenu(Menu menu) {
    // 执行修改菜单
    menuService.editMenu(menu);

    return ResultEntity.successWithoutData();
}

⑤ 后端代码 MenuService

在这里插入图片描述

 void editMenu(Menu menu);

⑥ 后端代码 MenuServiceImpl

在这里插入图片描述

@Override
public void editMenu(Menu menu) {
    // 由于 pid 没有传入,一定要使用有选择的更新,保证“pid”字段不会被置空
    menuMapper.updateByPrimaryKeySelective(menu);
}

五、菜单维护:修改菜单节点

① 思路

在这里插入图片描述


② 给“×”按钮绑定单击响应函数

在这里插入图片描述

// 为 x 按钮绑定单击事件
$("#treeDemo").on("click",".removeBtn ",function () {

    // 显示模态框
    $("#menuConfirmModal").modal("show");

    // 将当前节点的 id 保存到全局变量
    window.id = this.id;

    // 获取 zTreeObj 对象
    var zTreeObj = $.fn.zTree.getZTreeObj("treeDemo");

    // 根据 id 属性查询节点对象
    // 用来搜索节点的属性名
    var key = "id";

    // 用来搜索节点的属性值
    var value = window.id;

    var currentNode = zTreeObj.getNodeByParam(key, value);

    // 将当前要删除的icon和name展示在模态框中 (不知道代码哪里有问题,回显和这里的icon都不显示)
    $("#removeNodeSpan").html(" 【 <iclass='"+currentNode.icon+"'></i>"+currentNode.name+"】");

    // 禁止页面跳转
    return false;
});

③ 给确认模态框中的 OK 按钮绑定单击响应函数
// 为模态框中的 ok 绑定单击事件
$("#confirmBtn").click(function () {

    $.ajax({
        url: "menu/remove.json",
        type: "post",
        data: {
            id: window.id
        },
        dataType: "json",
        success: function (response) {

            var result = response.operationResult;

            if (result == "SUCCESS") {

                layer.msg("删除成功!");

                // 刷新菜单页面
                generateTree();
            }

            if(result == "FAILED") {

                layer("删除失败: " + response.message);
            }
        },
        error: function (response) {
            layer.msg((response.status+" "+response.statusText));
        }
    });

    // 隐藏模态框
    $("#menuConfirmModal").modal("hide");
});

④ 后端代码 MenuHandler

在这里插入图片描述

// 删除菜单节点
@RequestMapping("/menu/remove.json")
public ResultEntity<String> removeMenu(int id) {

    // 执行删除菜单节点
    menuService.removeMenu(id);

    return ResultEntity.successWithoutData();
}

⑤ 后端代码 MenuService

在这里插入图片描述

void removeMenu(int id);

⑥ 后端代码 MenuServiceImpl

在这里插入图片描述

@Override
public void removeMenu(int id) {
    // 执行删除菜单节点
    menuMapper.deleteByPrimaryKey(id);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值