八 菜单维护

该博客介绍了如何在Java中进行菜单维护,包括树形结构基础知识,如节点类型、数据库中的树形结构表示和Java类中的表示。内容涵盖菜单的增删改查操作,使用zTree展示,并详细讲解了前端和后端的实现代码。
摘要由CSDN通过智能技术生成

八 菜单维护

git checkout -b 8.0.0_permission

1. 树形结构基础知识介绍

1.1 节点类型

约定: 整个树形结构节点的层次最多只能有 3 级

img

1.2 在数据库中表示树形结构

1.2.1 创建菜单的数据库表
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)
);
1.2.2 插入数据
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');
1.2.3 关联方式
  • 子节点通过 pid 字段关键到父节点的 id 字段, 建立父子关系

img

  • 根节点的 pid 为 null, 无父节点

1.3 在 java 类中表示树形结构

1.3.1 基本方式
  • 在 Menu 类中使用 List children 属性存储当前节点的子节点
1.3.2 为了配合 zTree 使需要添加的属性
属性说明
pid找到父节点
name作为子节点名称
icon当前节点使用的图标
open控制节点是否默认打开
url点击节点时跳转的
  • pid 属性: 找到父节点
  • name 属性: 作为子节点名称
  • icon 属性: 当前节点使用的图标
  • open 属性: 控制节点是否默认打开
  • url 属性: 点击节点时跳转的

2 菜单维护: 页面显示

2.1 目标

  • 将数据库中查询得到的数据页面上显示出来

2.2 思路

  • 数据库 -> Java 对象组装 -> 页面上使用 zTree 显示

2.3 代码: 后端

2.3.1 生成代码
  • atcrowdfunding06-common-reverse -> generatorConfig.xml

img

<!-- 数据库表名字和我们的 entity 类对应的映射指定 -->
        <!--        <table tableName="t_admin" domainObjectName="Admin"/>-->
        <!--        <table tableName="t_role" domainObjectName="Role"/>-->
        <table tableName="t_menu" domainObjectName="Menu"/>
  • 执行命令

img

  • 修改生成的实体类 - Menu.java
    /**
     * 存储子节点的集合, 初始化是为了避免空指针异常
     */
    private List<Menu> children = new ArrayList<>();

    /**
     * 默认打开树节点
     * 控制节点是否默认打开, 设置为 true 表示默认打开
     */
    private Boolean open = true;
  • 整体代码
package com.atguigu.crowd.entity;

import java.util.ArrayList;
import java.util.List;

public class Menu {
    /**
     * 主键
     */
    private Integer id;

    /**
     * 父节点的 id
     */
    private Integer pid;

    /**
     * 节点名称
     */
    private String name;

    /**
     * 节点附带的 url 地址, 是将来点击菜单项时要跳转的地址
     */
    private String url;

    /**
     * 节点图标的样式
     */
    private String icon;

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

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

    public Menu() {
    }

    public Menu(Integer id, Integer pid, String name, String url, String icon, List<Menu> children, Boolean open) {
        this.id = id;
        this.pid = pid;
        this.name = name;
        this.url = url;
        this.icon = icon;
        this.children = children;
        this.open = open;
    }

    @Override
    public String toString() {
        return "Menu{" +
                "id=" + id +
                ", pid=" + pid +
                ", name='" + name + '\'' +
                ", url='" + url + '\'' +
                ", icon='" + icon + '\'' +
                ", children=" + children +
                ", open=" + open +
                '}';
    }

    public List<Menu> getChildren() {
        return children;
    }

    public void setChildren(List<Menu> children) {
        this.children = children;
    }

    public Boolean getOpen() {
        return open;
    }

    public void setOpen(Boolean open) {
        this.open = open;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url == null ? null : url.trim();
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon == null ? null : icon.trim();
    }
}
  • 将生成的文件移动到对应的位置

img

2.3.2 后端代码
  • MenuHandler
package com.atguigu.crowd.mvc.handler;

import com.atguigu.crowd.entity.Menu;
import com.atguigu.crowd.service.api.MenuService;
import com.atguigu.crowd.util.ResultEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class MenuHandler {

    @Autowired
    private MenuService menuService;

    @GetMapping("/menu/get/whole/tree.json")
    public ResultEntity<Menu> getWholeTree2() {
        // 1. 查询全部的 Menu 对象
        List<Menu> menuList = menuService.getAll();

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

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

        // 4. 遍历 MenuList 填充 menuMap
        for (Menu menu : menuList) {
            Integer id = menu.getId();

            menuMap.put(id, menu);
        }

        // 5. 再次遍历 menuList 查找根节点、组装父子节点
        for (Menu menu :
                menuList) {
            // 6. 获取当前 menu 对象的 pid 属性值
            Integer pid = menu.getPid();

            // 7. 如果 pid 为null, 判定为根节点
            if(pid == null){
                root = menu;

                // 8. 如果当前节点是跟节点, 那么肯定没有父节点, 不必继续执行
                continue;
            }

            // 9. 如果 pid 不为 null, 说明当前节点有父节点, 那么可以根据 pid 到 menuMap 中查找对应的 Menu 对象
            Menu father = menuMap.get(pid);

            // 10. 将当前节点存入父节点的 children 集合中
            father.getChildren().add(menu);
        }

        // 11. 经过上面的运算, 根节点包含了整个树形结构, 返回根节点角色返回整个数
        return ResultEntity.successWithData(root);
    }

}
  • MenuService
package com.atguigu.crowd.service.api;

import com.atguigu.crowd.entity.Menu;

import java.util.List;

public interface MenuService {
    /**
     * 查询所有
     *
     * @return
     */
    List<Menu> getAll();
}
  • MenuServiceImpl
package com.atguigu.crowd.service.impl;

import com.atguigu.crowd.entity.Menu;
import com.atguigu.crowd.entity.MenuExample;
import com.atguigu.crowd.mapper.MenuMapper;
import com.atguigu.crowd.service.api.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MenuServiceImpl implements MenuService {

    @Autowired
    private MenuMapper menuMapper;

    @Override
    public List<Menu> getAll() {
        return menuMapper.selectByExample(new MenuExample());
    }
}
2.3.3 页面跳转

img

   <mvc:view-controller path="/menu/to/page.html" view-name="menu-page" />
  • menu-page.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zn-CN">
<%@include file="/WEB-INF/include-head.jsp" %>
<link rel="stylesheet" href="css/pagination.css"/>
<script type="text/javascript" src="jquery/jquery.pagination.js"></script>
<script src="crowd/js/my-menu.js?v=6" type="text/javascript"></script>
<body>

<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
    <div class="row">
        <%@include file="/WEB-INF/include-sidebar.jsp" %>

        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <div class="panel panel-default">
                <div class="panel-heading"><i class="glyphicon glyphicon-th-list"></i> 权限菜单列表
                    <div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i
                            class="glyphicon glyphicon-question-sign"></i></div>
                </div>
                <div class="panel-body">
                    <ul id="treeDemo" class="ztree"></ul>
                </div>
            </div>
        </div>
    </div>
</div>

</body>
</html>
  • include-sidebar.jsp
<a href="menu/to/page.html"><i class="glyphicon glyphicon-lock"></i> 许可维护</a>
  • my-menu.js

    • 模拟数据
$(function () {
    // 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);
})
    • 请求获取数据
$(function () {
    $.ajax({
        url: 'menu/get/whole/tree.json',
        type: 'get',
        dataType: 'json',
        success: function (response) {
            var result = response.result;
            if (result === 'SUCCESS') {
                // 1. 创建 JSON 对象用于存储对 zTree 所做的设置
                var setting = {};
                // 2. 准备生成树形结构的 JSON 数据
                var data = response.data;
                // 3. 初始化树形结构
                $.fn.zTree.init($('#treeDemo'), setting, data);
            } else if (result === 'FAILED') {
                layer.msg('操作失败! ' + response.message);
            }
        },
        error: function (response) {
            layer.msg(response.status + ' ' + response.statusText)
        }
    })
})

2.4 树

2.4.1 修改默认图标

img

$(function () {
  $.ajax({
    url: 'menu/get/whole/tree.json',
    type: 'get',
    dataType: 'json',
    success: function (response) {
      var result = response.result;
      if (result === 'SUCCESS') {
        // 1. 创建 JSON 对象用于存储对 zTree 所做的设置
        var setting = {
          view: {
            addDiyDom: myAddDiyDom
          }
        };
        // 2. 准备生成树形结构的 JSON 数据
        var data = response.data;
        // 3. 初始化树形结构
        $.fn.zTree.init($('#treeDemo'), setting, data);
      } else if (result === 'FAILED') {
        layer.msg('操作失败! ' + response.message);
      }
    },
    error: function (response) {
      layer.msg(response.status + ' ' + response.statusText)
    }
  })
})

// 修改默认的图标
function myAddDiyDom(treeId, treeNode) {
  // treeId 是整个树形结构附着的 ul 标签的 id
  // console.log("treeId:" + treeId);
  
  // 当前树形节点的全部的数据, 包括从后端查询得到的 Menu 对象的全部属性
  // console.log(treeNode)
  
  // zTree 生成 id 的规则
  // 例子: treeDome_7_ico
  // 解析:ul 标签的 id_当前节点的序号_功能
  
  // 提示: ul 标签的id_当前节点的序号" - 这个部分可以通过 treeNode 的 tId 属性得到
  // 根据 id 的生成规则拼接出来 span 标签的 id
  var spanId = treeNode.tId + '_ico';
  
  // 根据控制图标的 span 标签的 id 找到整个 span 标签
  // 删除旧的 class
  // 添加新的 class
  $('#' + spanId)
    .removeClass()
    .addClass(treeNode.icon);
}
2.4.2 设置: 取消菜单的点击跳转页面功能
// 1. 创建 JSON 对象用于存储对 zTree 所做的设置
var setting = {
    data: {
        key: {
            url: 'no'
        }
    }
};
2.4.3 显示按钮组
// 在鼠标移入节点范围时添加按钮组
function myAddHoverDom(treeId, treeNode) {
    // 按钮组的标签结构: <span><a><i></i></a><a><i></i></a></span>
    // 按钮组出现的位置: 节点中 treeDDemo_n_a 超链接的后面

    // 为了在需要移除按钮组的时候能够精确定位按钮组所在 span, 需要给 spna 设置有规律的 id
    var btnGroupId = treeNode.tId + '_btnGrp';

    // 判断一下以前是否已经添加了按钮组
    if ($('#' + btnGroupId).length > 0) {
        return false;
    }

    // 执行在超链接后面附加 span 元素的操作
    // 准备各个按钮的 HTML 标签
    var addBtn = `<a id="${treeNode.id}" class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="添加子节点">&nbsp;&nbsp;<i class="fa fa-fw fa-plus rbg "></i></a>`;
    var removeBtn = `<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="删除节点">&nbsp;&nbsp;<i class="fa fa-fw fa-times rbg "></i></a>`;
    var editBtn = `<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="修改节点">&nbsp;&nbsp;<i class="fa fa-fw fa-edit rbg "></i></a>`;

    // 获取当前就带你的级别数据
    var level = treeNode.level;

    // 声明变量存储拼装号的按钮代码
    var btnHtml = '';

    if (level === 0) {
        // 根节点: 添加子节点
        btnHtml = addBtn;
    } else if (level === 1) {
        // 分支节点: 添加子节点 修改 <没有子节点可以删除 有子节点不能删除>
        btnHtml = addBtn + ' ' + editBtn;

        // 获取当前节点的子节点数量
        var length = treeNode.children.length;

        (length === 0) ? btnHtml += ' ' + removeBtn : '';
    } else if (level === 2) {
        // 叶子节点: 修改 删除
        btnHtml = editBtn + ' ' + removeBtn;
    }

    // 找到附着按钮组的超链接
    var anchorId = treeNode.tId + "_a";
    $('#' + anchorId).after(`<span id="${btnGroupId}">${btnHtml}</span>`);
}
2.4.4 移除按钮组
// 在鼠标离开节点范围时删除按钮组
function myRemoveHoverDom(treeId, treeNode) {
    // 拼接按钮组的 id
    var btnGroupId = treeNode.tId + '_btnGrp';
    // 移除对应的元素
    $('#' + btnGroupId).remove();
}
2.4.5 初始化树形结构函数 - 包装请求
$(function () {
    // 调用专门封装好的函数初始化树形结构
    generateTree();
})

// 生成树形结构的函数
function generateTree() {
    $.ajax({
        url: 'menu/get/whole/tree.json',
        type: 'get',
        dataType: 'json',
        success: function (response) {
            var result = response.result;
            if (result === 'SUCCESS') {
                // 1. 创建 JSON 对象用于存储对 zTree 所做的设置
                var setting = {
                    view: {
                        addDiyDom: myAddDiyDom,
                        addHoverDom: myAddHoverDom,
                        removeHoverDom: myRemoveHoverDom
                    },
                    data: {
                        key: {
                            url: 'no'
                        }
                    }
                };
                // 2. 准备生成树形结构的 JSON 数据
                var data = response.data;
                // 3. 初始化树形结构
                $.fn.zTree.init($('#treeDemo'), setting, data);
            } else if (result === 'FAILED') {
                layer.msg('操作失败! ' + response.message);
            }
        },
        error: function (response) {
            layer.msg(response.status + ' ' + response.statusText)
        }
    })
}

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

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

    // zTree 生成 id 的规则
    // 例子: treeDome_7_ico
    // 解析:ul 标签的 id_当前节点的序号_功能

    // 提示: ul 标签的id_当前节点的序号" - 这个部分可以通过 treeNode 的 tId 属性得到
    // 根据 id 的生成规则拼接出来 span 标签的 id
    var spanId = treeNode.tId + '_ico';

    // 根据控制图标的 span 标签的 id 找到整个 span 标签
    // 删除旧的 class
    // 添加新的 class
    $('#' + spanId)
        .removeClass()
        .addClass(treeNode.icon);
}

// 在鼠标移入节点范围时添加按钮组
function myAddHoverDom(treeId, treeNode) {
    // 按钮组的标签结构: <span><a><i></i></a><a><i></i></a></span>
    // 按钮组出现的位置: 节点中 treeDDemo_n_a 超链接的后面

    // 为了在需要移除按钮组的时候能够精确定位按钮组所在 span, 需要给 spna 设置有规律的 id
    var btnGroupId = treeNode.tId + '_btnGrp';

    // 判断一下以前是否已经添加了按钮组
    if ($('#' + btnGroupId).length > 0) {
        return false;
    }

    // 执行在超链接后面附加 span 元素的操作
    // 准备各个按钮的 HTML 标签
    var addBtn = `<a id="${treeNode.id}" class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="添加子节点">&nbsp;&nbsp;<i class="fa fa-fw fa-plus rbg "></i></a>`;
    var removeBtn = `<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="删除节点">&nbsp;&nbsp;<i class="fa fa-fw fa-times rbg "></i></a>`;
    var editBtn = `<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="修改节点">&nbsp;&nbsp;<i class="fa fa-fw fa-edit rbg "></i></a>`;

    // 获取当前就带你的级别数据
    var level = treeNode.level;

    // 声明变量存储拼装号的按钮代码
    var btnHtml = '';

    // 根节点: 添加子节点
    if (level === 0) {
        btnHtml += addBtn;
    }

    // 分支节点: 添加子节点 修改 <没有子节点可以删除 有子节点不能删除>
    if (level === 1) {
        btnHtml += addBtn + ' ' + editBtn;

        // 获取当前节点的子节点数量
        var length = treeNode.children.length;

        (length === 0) ? btnHtml += ' ' + removeBtn : '';
    }

    // 叶子节点: 修改 删除
    if (level === 2) {
        btnHtml = editBtn + ' ' + removeBtn;
    }

    // 找到附着按钮组的超链接
    var anchorId = treeNode.tId + "_a";
    $('#' + anchorId).after(`<span id="${btnGroupId}">${btnHtml}</span>`);
}

// 在鼠标离开节点范围时删除按钮组
function myRemoveHoverDom(treeId, treeNode) {
    // 拼接按钮组的 id
    var btnGroupId = treeNode.tId + '_btnGrp';
    // 移除对应的元素
    $('#' + btnGroupId).remove();
}

2.5 增删改按钮组 - 模态框系列

2.5.1 添加子节点
2.5.1.1 目标
  • 给当前节点添加子节点, 保存到数据库并刷新树形结构的显示
2.5.1.2 思路
2.5.1.3 代码
  • 使用按钮的 class 定位绑定点击响应函数
  • 给模态框中的保存按钮绑定点击响应函数

img

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

    // 给添加子节点按钮绑定单击响应函数
    $('#treeDemo').on('click', '.addBtn', function () {
        // 将当前节点的 id, 作为新就带你的 pid 保存到全局变量
        window.pid = this.id;

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

    // 给添加子节点的新增模态框的保存按钮 绑定单击响应函数
    $('#menuSaveBtn').click(function () {
        // 收集表单项中用户输入的数据
        var name = $.trim($('#menuAddModal [name=name]').val());
        var url = $.trim($('#menuAddModal [name=url]').val());
        // 单选按钮要定位到'被选中的那一个'
        var icon = $.trim($('#menuAddModal [name=icon]:checked').val());

        // 发送 ajax 请求
        $.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.result;
                if (result === 'SUCCESS') {
                    layer.msg('操作成功!');
                    // 刷新树形结构
                    generateTree();
                } else if (result === 'FAILED') {
                    layer.msg('操作失败!' + response.message);
                }
            },
            error: function (response) {
                layer.msg(response.status + '' + response.statusText);
            }
        });

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

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

})

// 生成树形结构的函数
function generateTree() {
    $.ajax({
        url: 'menu/get/whole/tree.json',
        type: 'get',
        dataType: 'json',
        success: function (response) {
            var result = response.result;
            if (result === 'SUCCESS') {
                // 1. 创建 JSON 对象用于存储对 zTree 所做的设置
                var setting = {
                    view: {
                        addDiyDom: myAddDiyDom,
                        addHoverDom: myAddHoverDom,
                        removeHoverDom: myRemoveHoverDom
                    },
                    data: {
                        key: {
                            url: 'no'
                        }
                    }
                };
                // 2. 准备生成树形结构的 JSON 数据
                var data = response.data;
                // 3. 初始化树形结构
                $.fn.zTree.init($('#treeDemo'), setting, data);
            } else if (result === 'FAILED') {
                layer.msg('操作失败! ' + response.message);
            }
        },
        error: function (response) {
            layer.msg(response.status + ' ' + response.statusText)
        }
    })
}

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

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

    // zTree 生成 id 的规则
    // 例子: treeDome_7_ico
    // 解析:ul 标签的 id_当前节点的序号_功能

    // 提示: ul 标签的id_当前节点的序号" - 这个部分可以通过 treeNode 的 tId 属性得到
    // 根据 id 的生成规则拼接出来 span 标签的 id
    var spanId = treeNode.tId + '_ico';

    // 根据控制图标的 span 标签的 id 找到整个 span 标签
    // 删除旧的 class
    // 添加新的 class
    $('#' + spanId)
        .removeClass()
        .addClass(treeNode.icon);
}

// 在鼠标移入节点范围时添加按钮组
function myAddHoverDom(treeId, treeNode) {
    // 按钮组的标签结构: <span><a><i></i></a><a><i></i></a></span>
    // 按钮组出现的位置: 节点中 treeDDemo_n_a 超链接的后面

    // 为了在需要移除按钮组的时候能够精确定位按钮组所在 span, 需要给 spna 设置有规律的 id
    var btnGroupId = treeNode.tId + '_btnGrp';

    // 判断一下以前是否已经添加了按钮组
    if ($('#' + btnGroupId).length > 0) {
        return false;
    }

    // 执行在超链接后面附加 span 元素的操作
    // 准备各个按钮的 HTML 标签
    var addBtn = `<a id="${treeNode.id}" class="addBtn btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="添加子节点">&nbsp;&nbsp;<i class="fa fa-fw fa-plus rbg "></i></a>`;
    var removeBtn = `<a class="removeBtn btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="删除节点">&nbsp;&nbsp;<i class="fa fa-fw fa-times rbg "></i></a>`;
    var editBtn = `<a class="editBtn btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="修改节点">&nbsp;&nbsp;<i class="fa fa-fw fa-edit rbg "></i></a>`;

    // 获取当前就带你的级别数据
    var level = treeNode.level;

    // 声明变量存储拼装号的按钮代码
    var btnHtml = '';

    if (level === 0) {
        // 根节点: 添加子节点
        btnHtml = addBtn;
    } else if (level === 1) {
        // 分支节点: 添加子节点 修改 <没有子节点可以删除 有子节点不能删除>
        btnHtml = addBtn + ' ' + editBtn;

        // 获取当前节点的子节点数量
        var length = treeNode.children.length;

        (length === 0) ? btnHtml += ' ' + removeBtn : '';
    } else if (level === 2) {
        // 叶子节点: 修改 删除
        btnHtml = editBtn + ' ' + removeBtn;
    }

    // 找到附着按钮组的超链接
    var anchorId = treeNode.tId + "_a";
    $('#' + anchorId).after(`<span id="${btnGroupId}">${btnHtml}</span>`);
}

// 在鼠标离开节点范围时删除按钮组
function myRemoveHoverDom(treeId, treeNode) {
    // 拼接按钮组的 id
    var btnGroupId = treeNode.tId + '_btnGrp';
    // 移除对应的元素
    $('#' + btnGroupId).remove();
}
  • menu-page.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html>
    <html lang="zn-CN">
      <%@include file="/WEB-INF/include-head.jsp" %>
        <link rel="stylesheet" href="ztree/zTreeStyle.css"/>
        <script type="text/javascript" src="jquery/jquery.pagination.js"></script>
        <script type="text/javascript" src="ztree/jquery.ztree.all-3.5.min.js"></script>
        <script src="crowd/js/my-menu.js?v=10" type="text/javascript"></script>
        <body>
          
          <%@include file="/WEB-INF/include-nav.jsp" %>
            <div class="container-fluid">
              <div class="row">
                <%@include file="/WEB-INF/include-sidebar.jsp" %>
                  
                  <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
                    <div class="panel panel-default">
                      <div class="panel-heading"><i class="glyphicon glyphicon-th-list"></i> 权限菜单列表
                        <div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i
                                                                                                               class="glyphicon glyphicon-question-sign"></i></div>
                      </div>
                      <div class="panel-body">
                        <ul id="treeDemo" class="ztree"></ul>
                      </div>
                    </div>
                  </div>
                  </div>
              </div>
              
              <%@include file="/WEB-INF/modal-menu-add.jsp" %>
                <%@include file="/WEB-INF/modal-menu-confirm.jsp" %>
                  <%@include file="/WEB-INF/modal-menu-edit.jsp" %>
                    </body>
                  </html>
  • modal-menu-add.jsp 添加模态框
<%--
    菜单维护的添加模态框
--%>
<%@ page language="java" contentType="text/html;charset=UTF-8"
         pageEncoding="UTF-8"%>
<div id="menuAddModal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal"
                        aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">尚筹网-添加节点</h4>
            </div>
            <form>
                <div class="modal-body">
                    请输入节点名称:<input type="text" name="name" /><br />
                    请输入URL地址:<input type="text" name="url" /><br />
                    <i class="glyphicon glyphicon-th-list"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-th-list" />&nbsp;

                    <i class="glyphicon glyphicon-dashboard"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-dashboard" /> &nbsp;

                    <i class="glyphicon glyphicon glyphicon-tasks"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon glyphicon-tasks" /> &nbsp;

                    <i class="glyphicon glyphicon-user"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-user" /> &nbsp;

                    <i class="glyphicon glyphicon-king"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-king" /> &nbsp;

                    <i class="glyphicon glyphicon-lock"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-lock" /> &nbsp;

                    <i class="glyphicon glyphicon-ok"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-ok" /> &nbsp;

                    <i class="glyphicon glyphicon-check"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-check" /> &nbsp;

                    <i class="glyphicon glyphicon-th-large"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-th-large" /> <br />

                    <i class="glyphicon glyphicon-picture"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-picture" /> &nbsp;

                    <i class="glyphicon glyphicon-equalizer"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-equalizer" /> &nbsp;

                    <i class="glyphicon glyphicon-random"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-random" /> &nbsp;

                    <i class="glyphicon glyphicon-hdd"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-hdd" /> &nbsp;

                    <i class="glyphicon glyphicon-comment"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-comment" /> &nbsp;

                    <i class="glyphicon glyphicon-list"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-list" /> &nbsp;

                    <i class="glyphicon glyphicon-tags"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-tags" /> &nbsp;

                    <i class="glyphicon glyphicon-list-alt"></i>
                    <input type="radio" name="icon" value="glyphicon glyphicon-list-alt" /> &nbsp;
                    <br />

                </div>
                <div class="modal-footer">
                    <button id="menuSaveBtn" type="button" class="btn btn-default"><i class="glyphicon glyphicon-plus"></i> 保存</button>
                    <button id="menuResetBtn" type="reset" class="btn btn-primary"><i class="glyphicon glyphicon-refresh"></i> 重置</button>
                </div>
            </form>
        </div>
    </div>
</div>
2.5.2 修改节点
2.5.2.1 目标
  • 修改当前节点的基本属性, 保存到数据库并刷新树形结构的显示
2.5.2.2 思路

img

2.5.2.3 前端代码
  • 将按钮组的 id 设置好
  • 给按钮绑定单击响应函数
  • 给模态框中的更新按钮绑定单击响应函数
  • my-menu.js
$(function () {
    // 调用专门封装好的函数初始化树形结构
    generateTree();

    // 给修改子节点按钮绑定单击响应函数
    $('#treeDemo').on('click', '.editBtn', function () {
        // 将当前节点的 id 保存到全局变量
        window.id = this.id;
        console.log(window.id);

        // 获取 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 回显的本质是把 value 属性 currentNode.icon 一致的 radio 选中
        // 回显 radio 可以这样理解: 被选中的 radio 的 value 属性可以组成一个数组, 然后再用这个数组设置回 radio, 就能够把对应的值选中
        $('#menuEditModal [name=icon]').val([currentNode.icon]);

        // 打开模态框
        $('#menuEditModal').modal('show');
    });

    // 给更新模态框中的更新按钮绑定点击响应函数
    $('#menuEditBtn').click(function () {
        // 收集表单数据
        var name = $('#menuEditModal [name=name]').val();
        var url = $('#menuEditModal [name=url]').val();
        var icon = $('#menuEditModal [name=icon]:checked').val();

        // 发送 ajax 请求
        $.ajax({
            url: 'menu/update.json',
            type: 'put',
            contentType: "application/json;charset=UTF-8",
            data: JSON.stringify({
                id: window.id,
                name: name,
                url: url,
                icon: icon
            }),
            dataType: 'json',
            success: function (response) {
                var result = response.result;
                if (result === 'SUCCESS') {
                    layer.msg('操作成功!');
                    // 刷新树形结构
                    generateTree();
                } else if (result === 'FAILED') {
                    layer.msg('操作失败!' + response.message);
                }
            },
            error: function (response) {
                layer.msg(' 请求失败! ' + response.status + ' ' + response.statusText);
            }
        });

        // 关闭模态框
        $('#menuEditModal').modal('hide');
    });

    // 给添加子节点按钮绑定单击响应函数
    $('#treeDemo').on('click', '.addBtn', function () {
        // 将当前节点的 id, 作为新节点的 pid 保存到全局变量
        window.pid = this.id;

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

    // 给添加子节点的新增模态框的保存按钮 绑定单击响应函数
    $('#menuSaveBtn').click(function () {
        // 收集表单项中用户输入的数据
        var name = $.trim($('#menuAddModal [name=name]').val());
        var url = $.trim($('#menuAddModal [name=url]').val());
        // 单选按钮要定位到'被选中的那一个'
        var icon = $.trim($('#menuAddModal [name=icon]:checked').val());

        // 发送 ajax 请求
        $.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.result;
                if (result === 'SUCCESS') {
                    layer.msg('操作成功!');
                    // 刷新树形结构
                    generateTree();
                } else if (result === 'FAILED') {
                    layer.msg('操作失败!' + response.message);
                }
            },
            error: function (response) {
                layer.msg(response.status + ' ' + response.statusText);
            }
        });

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

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

})

// 生成树形结构的函数
function generateTree() {
    $.ajax({
        url: 'menu/get/whole/tree.json',
        type: 'get',
        dataType: 'json',
        success: function (response) {
            var result = response.result;
            if (result === 'SUCCESS') {
                // 1. 创建 JSON 对象用于存储对 zTree 所做的设置
                var setting = {
                    view: {
                        addDiyDom: myAddDiyDom,
                        addHoverDom: myAddHoverDom,
                        removeHoverDom: myRemoveHoverDom
                    },
                    data: {
                        key: {
                            url: 'no'
                        }
                    }
                };
                // 2. 准备生成树形结构的 JSON 数据
                var data = response.data;
                // 3. 初始化树形结构
                $.fn.zTree.init($('#treeDemo'), setting, data);
            } else if (result === 'FAILED') {
                layer.msg('操作失败! ' + response.message);
            }
        },
        error: function (response) {
            layer.msg(response.status + ' ' + response.statusText)
        }
    })
}

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

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

    // zTree 生成 id 的规则
    // 例子: treeDome_7_ico
    // 解析:ul 标签的 id_当前节点的序号_功能

    // 提示: ul 标签的id_当前节点的序号" - 这个部分可以通过 treeNode 的 tId 属性得到
    // 根据 id 的生成规则拼接出来 span 标签的 id
    var spanId = treeNode.tId + '_ico';

    // 根据控制图标的 span 标签的 id 找到整个 span 标签
    // 删除旧的 class
    // 添加新的 class
    $('#' + spanId)
        .removeClass()
        .addClass(treeNode.icon);
}

// 在鼠标移入节点范围时添加按钮组
function myAddHoverDom(treeId, treeNode) {
    // 按钮组的标签结构: <span><a><i></i></a><a><i></i></a></span>
    // 按钮组出现的位置: 节点中 treeDDemo_n_a 超链接的后面

    // 为了在需要移除按钮组的时候能够精确定位按钮组所在 span, 需要给 spna 设置有规律的 id
    var btnGroupId = treeNode.tId + '_btnGrp';

    // 判断一下以前是否已经添加了按钮组
    if ($('#' + btnGroupId).length > 0) {
        return false;
    }

    // 执行在超链接后面附加 span 元素的操作
    // 准备各个按钮的 HTML 标签
    var addBtn = `<a id="${treeNode.id}" class="addBtn btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="添加子节点">&nbsp;&nbsp;<i class="fa fa-fw fa-plus rbg "></i></a>`;
    var removeBtn = `<a id="${treeNode.id}" class="removeBtn btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="删除节点">&nbsp;&nbsp;<i class="fa fa-fw fa-times rbg "></i></a>`;
    var editBtn = `<a id="${treeNode.id}" class="editBtn btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" title="修改节点">&nbsp;&nbsp;<i class="fa fa-fw fa-edit rbg "></i></a>`;

    // 获取当前就带你的级别数据
    var level = treeNode.level;

    // 声明变量存储拼装号的按钮代码
    var btnHtml = '';

    if (level === 0) {
        // 根节点: 添加子节点
        btnHtml = addBtn;
    } else if (level === 1) {
        // 分支节点: 添加子节点 修改 <没有子节点可以删除 有子节点不能删除>
        btnHtml = addBtn + ' ' + editBtn;

        // 获取当前节点的子节点数量
        var length = treeNode.children.length;

        (length === 0) ? btnHtml += ' ' + removeBtn : '';
    } else if (level === 2) {
        // 叶子节点: 修改 删除
        btnHtml = editBtn + ' ' + removeBtn;
    }

    // 找到附着按钮组的超链接
    var anchorId = treeNode.tId + "_a";
    $('#' + anchorId).after(`<span id="${btnGroupId}">${btnHtml}</span>`);
}

// 在鼠标离开节点范围时删除按钮组
function myRemoveHoverDom(treeId, treeNode) {
    // 拼接按钮组的 id
    var btnGroupId = treeNode.tId + '_btnGrp';
    // 移除对应的元素
    $('#' + btnGroupId).remove();
}
2.5.2.4 后端代码
  • MenuHandler
    @PutMapping("/menu/update.json")
    public ResultEntity<String> updateMenu(@RequestBody Menu menu) {
        menuService.updateMenu(menu);
        return ResultEntity.successWithoutData();
    }
  • MenuServiceImpl
    @Override
    public void updateMenu(Menu menu) {
        // 由于 pid 没有传入, 一定要使用有选择的更新, 保证 'pid' 字段不会被置空
        menuMapper.updateByPrimaryKeySelective(menu);
    }
2.5.3 删除节点
2.5.3.1 目标
  • 删除当前节点
2.5.3.2 思路

img

2.5.3.3 前端代码
    // 给 "X" 按钮绑定单击响应函数
    $('#treeDemo').on('click', '.removeBtn', function () {
        window.id = this.id;
        // 获取 zTreeObj 对象
        var zTreeObj = $.fn.zTree.getZTreeObj('treeDemo');
        var currentNode = zTreeObj.getNodeByParam('id', this.id);
        $('#removeNodeSpan').html(`【<i class="${currentNode.icon}"></i> ${currentNode.name}`);
        $('#menuConfirmModal').modal('show');
    });

    // 给确认模态框中的 OK 按钮绑定单击响应函数
    $('#confirmBtn').click(function () {
        // 发送 ajax 请求
        $.ajax({
            url: 'menu/delete.json',
            type: 'delete',
            contentType: "application/json;charset=UTF-8",
            data: JSON.stringify(window.id),
            dataType: 'json',
            success: function (response) {
                var result = response.result;
                if (result === 'SUCCESS') {
                    layer.msg('操作成功!');
                    // 刷新树形结构
                    generateTree();
                } else if (result === 'FAILED') {
                    layer.msg('操作失败!' + response.message);
                }
            },
            error: function (response) {
                layer.msg(' 请求失败! ' + response.status + ' ' + response.statusText);
            }
        });

        // 关闭模态框
        $('#menuConfirmModal').modal('hide');
    });
2.5.3.4 后端代码
  • MenuHandler
    @DeleteMapping("/menu/delete.json")
    public ResultEntity<String> deleteMenu(@RequestBody Integer id) {
        menuService.deleteMenuById(id);
        return ResultEntity.successWithoutData();
    }
  • MenuServiceImpl
    @Override
    public void deleteMenuById(Integer id) {
        menuMapper.deleteByPrimaryKey(id);
    }
  • 错误到了主分支
  • 记得检查当前分支: git branch

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值