day05【后台】菜单维护

1、数据库中存储树形结构

1.1、节点类型

1.2、创建菜单表

  • 执行SQL语句创建数据库表
USE project_crowd
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.3、插入数据

  • 执行SQL语句插入数据
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 glyphiconglyphicon-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.4、表的结构

  • 菜单表为自关联表

2、Java中表示树形结构

2.1、逆向工程

2.1.1、修改逆向工程配置文件

  • 修改reverse工程下的逆向工程配置文件:generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration><!-- mybatis-generator:generate --><context id="atguiguTables" targetRuntime="MyBatis3"><commentGenerator><!-- 是否去除自动生成的注释 true:是;false:否 --><property name="suppressAllComments" value="true" /></commentGenerator><!--数据库连接的信息: 驱动类、 连接地址、 用户名、 密码 --><jdbcConnection driverClass="com.mysql.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/project_crowd"userId="root" password="101713"></jdbcConnection><!-- 默认 false, 把 JDBC DECIMAL 和 NUMERIC 类型解析为 Integer, 为 true 时把 JDBC DECIMAL 和 NUMERIC 类型解析为 java.math.BigDecimal --><javaTypeResolver><property name="forceBigDecimals" value="false" /></javaTypeResolver><!-- targetProject:生成 Entity 类的路径 --><javaModelGenerator targetProject=".\src\main\java"targetPackage="com.atguigu.crowd.entity"><!-- enableSubPackages:是否让 schema 作为包的后缀 --><property name="enableSubPackages" value="false" /><!-- 从数据库返回的值被清理前后的空格 --><property name="trimStrings" value="true" /></javaModelGenerator><!-- targetProject:XxxMapper.xml 映射文件生成的路径 --><sqlMapGenerator targetProject=".\src\main\java"targetPackage="com.atguigu.crowd.mapper"><!-- enableSubPackages:是否让 schema 作为包的后缀 --><property name="enableSubPackages" value="false" /></sqlMapGenerator><!-- targetPackage: Mapper 接口生成的位置 --><javaClientGenerator type="XMLMAPPER"targetProject=".\src\main\java"targetPackage="com.atguigu.crowd.mapper"><!-- enableSubPackages:是否让 schema 作为包的后缀 --><property name="enableSubPackages" value="false" /></javaClientGenerator><!-- 数据库表名字和我们的 entity 类对应的映射指定 --><table tableName="t_menu" domainObjectName="Menu" /></context>
</generatorConfiguration>

2.1.2、执行Maven命令

  • 执行Maven命令:mybatis-generator:generate

2.1.3、资源归位

  • JavaBean归位至entity工程

  • Mapper接口归位至component工程

  • Mapper.xml映射文件归位至webui工程

2.1.4、修改JavaBean

  • 为了配合 zTree,修改Menu实体类,增加一些数据库中没有的字段
public class Menu {// 主键private Integer id;// 父节点的idprivate 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() {// TODO Auto-generated constructor stub}public Menu(Integer id, Integer pid, String name, String url, String icon, List<Menu> children, Boolean open) {super();this.id = id;this.pid = pid;this.name = name;this.url = url;this.icon = icon;this.children = children;this.open = open;}@Overridepublic String toString() {return "Menu [id=" + id + ", pid=" + pid + ", name=" + name + ", url=" + url + ", icon=" + icon + ", children="+ children + ", 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();}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;}}

2.2、添加组件

2.2.1、Handler组件

  • component工程下添加MenuHandler.java

2.2.2、Service组件

  • component工程下添加Service层的组件

3、页面显示树形结构

3.1、准备工作

3.1.1、添加view-controller

  • SpringMVC配置文件中添加view-controller,通过view-controller跳转至指定页面

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

3.1.2、添加菜单栏页面

  • WEB-INF文件夹下添加menu-page.jsp页面,用于显示菜单栏

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp"%>
<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标签是zTree动态生成的节点所依附的静态节点 --><ul id="treeDemo" class="ztree"></ul></div></div></div></div></div>
</body>
</html>

3.1.3、跳转地址

  • 修改侧边栏中【菜单维护】按钮的跳转地址

<li style="height: 30px;"><a href="menu/to/page.html"><i class="glyphicon glyphicon-lock"></i> 菜单维护</a></li>

3.2、后端代码

  • Handler组装树形结构,并返回给前端
@Controller
public class MenuHandler {@Autowiredprivate MenuService menuService;// 未改进的方法,存在嵌套循环,性能不好public ResultEntity<Menu> getWholeTreeOld() {// 1.查询全部的Menu对象List<Menu> menuList = menuService.getAll();// 2.声明一个变量用来存储找到的根节点Menu root = null;// 3.遍历menuListfor (Menu menu : menuList) {// 4.获取当前menu对象的pid属性值Integer pid = menu.getPid();// 5.检查pid是否为nullif(pid == null) {// 6.把当前正在遍历的这个menu对象赋值给rootroot = menu;// 7.停止本次循环,继续执行下一次循环continue ;}// 8.如果pid不为null,说明当前节点有父节点,找到父节点就可以进行组装,建立父子关系for (Menu maybeFather : menuList) {// 9.获取maybeFather的id属性Integer id = maybeFather.getId();// 10.将子节点的pid和疑似父节点的id进行比较if(Objects.equals(pid, id)) {// 11.将子节点存入父节点的children集合maybeFather.getChildren().add(menu);// 12.找到即可停止运行循环break ;}}}// 13.将组装的树形结构(也就是根节点对象)返回给浏览器return ResultEntity.successWithData(root);}@ResponseBody@RequestMapping("/menu/get/whole/tree.json")public ResultEntity<Menu> getWholeTreeNew() {// 1.查询全部的Menu对象List<Menu> menuList = menuService.getAll();// 2.声明一个变量用来存储找到的根节点Menu root = null;// 3.创建Map对象用来存储id和Menu对象的对应关系便于查找父节点Map<Integer, Menu> menuMap = new HashMap<>();// 4.遍历menuList填充menuMapfor (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);}}

3.3、后端代码测试

  • 后端返回的json数据

3.4、前端代码

3.4.1、引入zTree环境

  • menu-page.jsp页面中引入zTree所需的资源

3.4.2、显示树形结构(假数据)

  • menu-page.jsp页面中,使用zTree显示数据结构(假数据)

<script type="text/javascript">$(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);});</script>
  • 显示效果

3.4.3、显示树形结构(真实数据)

  • menu-page.jsp页面中,发送Ajax请求得到菜单栏数据,并通过zTree显示树形结构

<script type="text/javascript">$(function(){// 1.准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到$.ajax({"url": "menu/get/whole/tree.json","type":"post","dataType":"json","success":function(response){var result = response.result;if(result == "SUCCESS") {// 2.创建JSON对象用于存储对zTree所做的设置var setting = {};// 3.从响应体中获取用来生成树形结构的JSON数据var zNodes = response.data;// 4.初始化树形结构$.fn.zTree.init($("#treeDemo"), setting, zNodes);}if(result == "FAILED") {layer.msg(response.message);}}});});
</script>
  • 显示效果

3.4.4、准备zTree文档

  • 需要将zTree文档部署到Tomcatwebapp目录下

  • 启动Tomcat,访问API文档

  • 我擦嘞:Edge可以直接访问本地文件,无需部署

3.4.5、zTree显示图标原理

  • 修改<span/>标签的class属性即可

<span id="treeDemo_4_ico" title="" treenode_ico="" class="glyphicon glyphicon-th-list" style="background:url(glyphicon glyphicon-phone) 0 0 no-repeat;"></span>

3.4.6、添加自定义JavaScript库

  • webapp下创建自定义的JavaScript

3.4.7、引入JavaScript库

  • menu-page.jsp页面中引入my-menu.js

3.4.8、修改默认图标

  • my-menu.js中添加myAddDiyDom函数,将默认的图标修改为真实图标

// 修改默认的图标
function myAddDiyDom(treeId, treeNode) {// treeId是整个树形结构附着的ul标签的idconsole.log("treeId="+treeId);// 当前树形节点的全部的数据,包括从后端查询得到的Menu对象的全部属性console.log(treeNode);// zTree生成id的规则// 例子:treeDemo_7_ico// 解析:ul标签的id_当前节点的序号_功能// 提示:“ul标签的id_当前节点的序号”部分可以通过访问treeNode的tId属性得到// 根据id的生成规则拼接出来span标签的idvar spanId = treeNode.tId + "_ico";// 根据控制图标的span标签的id找到这个span标签// 删除旧的class// 添加新的class$("#"+spanId).removeClass().addClass(treeNode.icon);}
  • menu-page.jsp页面中设置zTreeaddDiyDom属性,zTree加载菜单栏时,将回调我们自定义的myAddDiyDom函数,这样就能修改默认图标啦~

<script type="text/javascript">$(function(){// 1.准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到$.ajax({"url": "menu/get/whole/tree.json","type":"post","dataType":"json","success":function(response){var result = response.result;if(result == "SUCCESS") {// 2.创建JSON对象用于存储对zTree所做的设置var setting = {"view": {"addDiyDom": myAddDiyDom,}};// 3.从响应体中获取用来生成树形结构的JSON数据var zNodes = response.data;// 4.初始化树形结构$.fn.zTree.init($("#treeDemo"), setting, zNodes);}if(result == "FAILED") {layer.msg(response.message);}}});});
</script>
  • treeNode类型的结构如下

    • 可通过level字段获取节点级别,级别0为根节点
    • open=true表示节点是展开状态
    • tId=treeId+节点编号,节点编号从上往下次递增(从1开始)

  • 测试

3.4.9、点击节点不跳转

  • menu-page.jsp页面中设置zTreeurl属性,设置成一个不存在的值;当点击单个节点,zTree找不到对应url,就不会发请求,也就不会跳转

<script type="text/javascript">$(function(){// 1.准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到$.ajax({"url": "menu/get/whole/tree.json","type":"post","dataType":"json","success":function(response){var result = response.result;if(result == "SUCCESS") {// 2.创建JSON对象用于存储对zTree所做的设置var setting = {"view": {"addDiyDom": myAddDiyDom,}"data": {"key": {"url": "maomi"}}};// 3.从响应体中获取用来生成树形结构的JSON数据var zNodes = response.data;// 4.初始化树形结构$.fn.zTree.init($("#treeDemo"), setting, zNodes);}if(result == "FAILED") {layer.msg(response.message);}}});});
</script>
  • 鼠标单击可选中节点

3.4.10、3.4.10、显示按钮组

  • zTree中按钮组的结构如下

  • my-menu.js库中:

    • 编写myAddHoverDom函数:鼠标移入节点时,添加按钮组;不同级别的节点需要不同的按钮组,根据节点级别添加按钮组(前提:菜单栏最多只能有三级节点,即只能到叶子节点)

      • 根节点:只能添加子节点
      • 子节点:添加子节点、 修改子节点、删除子节点(该节点没有子节点才能删除)
      • 叶子节点:修改子节点、删除子节点
    // 在鼠标移入节点范围时添加按钮组
    function myAddHoverDom(treeId, treeNode) {// 按钮组的标签结构:<span><a><i></i></a><a><i></i></a></span>// 按钮组出现的位置:节点中treeDemo_n_a超链接的后面// 为了在需要移除按钮组的时候能够精确定位到按钮组所在span,需要给span设置有规律的idvar btnGroupId = treeNode.tId + "_btnGrp";// 判断一下以前是否已经添加了按钮组if($("#"+btnGroupId).length > 0) {return ;}// 准备各个按钮的HTML标签var addBtn = "<a id='"+treeNode.id+"' class='addBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' 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;' href='#' 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;' href='#' title='修改节点'>&nbsp;&nbsp;<i class='fa fa-fw fa-edit rbg '></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>");}
    
    • 编写myRemoveHoverDom函数:鼠标移除节点时,删除按钮组
    //在鼠标离开节点范围时删除按钮组
    function myRemoveHoverDom(treeId, treeNode) {// 拼接按钮组的idvar btnGroupId = treeNode.tId + "_btnGrp";// 移除对应的元素$("#"+btnGroupId).remove();}
    
  • 显示效果

3.4.11、3.4.11、封装Ajax请求

  • 将之前发送Ajax请求,生成树形结构的代码,封装成一个函数,放到my-menu.js库中

//生成树形结构的函数
function generateTree() {// 1.准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到$.ajax({"url": "menu/get/whole/tree.json","type":"post","dataType":"json","success":function(response){var result = response.result;if(result == "SUCCESS") {// 2.创建JSON对象用于存储对zTree所做的设置var setting = {"view": {"addDiyDom": myAddDiyDom,"addHoverDom": myAddHoverDom,"removeHoverDom": myRemoveHoverDom},"data": {"key": {"url": "maomi"}}};// 3.从响应体中获取用来生成树形结构的JSON数据var zNodes = response.data;// 4.初始化树形结构$.fn.zTree.init($("#treeDemo"), setting, zNodes);}if(result == "FAILED") {layer.msg(response.message);}}});
}
  • menu-page.jsp页面中,调用封装好的generateTree函数

<script type="text/javascript">$(function(){// 调用专门封装好的函数初始化树形结构generateTree(); });</script>

4、添加子节点

4.1、思路

4.2、准备工作

  • 生成添加按钮时,添加上class属性,这样我们才能使用jQuery找到添加按钮

var addBtn = "<a id='"+treeNode.id+"' class='addBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' title='添加子节点'>&nbsp;&nbsp;<i class='fa fa-fw fa-plus rbg '></i></a>";

4.3、新增子节点模态框

  • WEB-INF文件夹下添加用于新增子节点的模态框: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>
  • menu-page.jsp页面中引入模态框
<%@include file="/WEB-INF/modal-menu-add.jsp" %>

4.4、打开模态框

  • menu-page.jsp页面中,为添加按钮绑定单击响应函数:记录节点Id,并打开模态框
// 给添加子节点按钮绑定单击响应函数
$("#treeDemo").on("click",".addBtn",function(){// 将当前节点的id,作为新节点的pid保存到全局变量window.pid = this.id;// 打开模态框$("#menuAddModal").modal("show");return false;
});

4.5、发送保存请求

  • 给新增子节点模态框中的保存按钮绑定单击响应函数:

    • 采集用户输入的数据
    • 通过Ajax发送保存请求
    • 通过layer组件显示保存是否成功
    • 刷新菜单栏树形结构
    • 关闭并重置模态框
// 给添加子节点的模态框中的保存按钮绑定单击响应函数
$("#menuSaveBtn").click(function(){// 收集表单项中用户输入的数据var name = $.trim($("#menuAddModal [name=name]").val());var url = $.trim($("#menuAddModal [name=url]").val());// 单选按钮要定位到“被选中”的那一个var icon = $("#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();}if(result == "FAILED") {layer.msg("操作失败!"+response.message);}},"error":function(response){layer.msg(response.status+" "+response.statusText);}});// 关闭模态框$("#menuAddModal").modal("hide");// 清空表单// jQuery对象调用click()函数,里面不传任何参数,相当于用户点击了一下$("#menuResetBtn").click();
});

4.6、Handler代码

  • MenuHandler.java中保存节点至数据库

@ResponseBody
@RequestMapping("/menu/save.json")
public ResultEntity<String> saveMenu(Menu menu) {menuService.saveMenu(menu);  return ResultEntity.successWithoutData();
}

4.7、Service代码

  • Service层实现上述方法

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

4.8、测试

  • 添加子节点

  • 添加成功~

5、更新节点信息

5.1、思路

5.2、准备工作

  • 生成更新按钮时,添加上class属性,这样我们才能使用jQuery找到添加按钮

var editBtn = "<a id='"+treeNode.id+"' class='editBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' title='修改节点'>&nbsp;&nbsp;<i class='fa fa-fw fa-edit rbg '></i></a>";

5.3、更新节点信息模态框

  • WEB-INF文件夹下添加用于更新节点信息的模态框:modal-menu-edit.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<div id="menuEditModal" 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="menuEditBtn" type="button" class="btn btn-default"><i class="glyphicon glyphicon-edit"></i> 更新</button></div></form></div></div>
</div>
  • menu-page.jsp页面中引入模态框

<%@include file="/WEB-INF/modal-menu-edit.jsp" %>

5.4、打开模态框

  • menu-page.jsp页面中,为更新按钮绑定单击响应函数:

    • 记录当前节点的id至全局变量中
    • 打开更新节点信息的模态框
    • 根据id属性的值获取节点信息,并回显
// 给编辑按钮绑定单击响应函数
$("#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;
});

5.5、发送更新请求

  • 给更新模态框中的更新按钮绑定单击响应函数:

    • 收集表单数据(用户输入数据)
    • 通过Ajax发送更新请求
    • 通过layer组件显示更新是否成功
    • 更新成功则刷新菜单栏
    • 关闭模态框
// 给更新模态框中的更新按钮绑定单击响应函数
$("#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":"post","data":{"id": window.id,"name":name,"url":url,"icon":icon},"dataType":"json","success":function(response){var result = response.result;if(result == "SUCCESS") {layer.msg("操作成功!");// 重新加载树形结构,注意:要在确认服务器端完成保存操作后再刷新// 否则有可能刷新不到最新的数据,因为这里是异步的generateTree();}if(result == "FAILED") {layer.msg("操作失败!"+response.message);}},"error":function(response){layer.msg(response.status+" "+response.statusText);}});// 关闭模态框$("#menuEditModal").modal("hide");});

5.6、Handler代码

  • MenuHandler.java中更新节点信息
@ResponseBody
@RequestMapping("/menu/update.json")
public ResultEntity<String> updateMenu(Menu menu) {   menuService.updateMenu(menu);   return ResultEntity.successWithoutData();
}

5.7、Service代码

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

5.8、测试

6、删除节点

6.1、思路

6.2、准备工作

  • 生成更新按钮时,添加上class属性,这样我们才能使用jQuery找到添加按钮

var removeBtn = "<a id='"+treeNode.id+"' class='removeBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' href='#' title='删除节点'>&nbsp;&nbsp;<i class='fa fa-fw fa-times rbg '></i></a>";

6.3、删除节点模态框

  • WEB-INF文件夹下添加用于更新节点信息的模态框:modal-menu-confirm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<div id="menuConfirmModal" 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">您真的要删除<span id="removeNodeSpan"></span>这个节点吗?</div><div class="modal-footer"><button id="confirmBtn" type="button" class="btn btn-danger"><i class="glyphicon glyphicon-ok"></i> OK</button></div></form></div></div>
</div>
  • menu-page.jsp页面中引入模态框

<%@include file="/WEB-INF/modal-menu-confirm.jsp" %>

6.4、打开模态框

  • menu-page.jsp页面中,为删除按钮绑定单击响应函数:

    • 记录当前节点的id至全局变量中
    • 打开删除节点信息的模态框
    • 根据节点的图标和文本信息,并回显
// 给“×”按钮绑定单击响应函数
$("#treeDemo").on("click",".removeBtn",function(){// 将当前节点的id保存到全局变量window.id = this.id;// 打开模态框$("#menuConfirmModal").modal("show");// 获取zTreeObj对象var zTreeObj = $.fn.zTree.getZTreeObj("treeDemo");// 根据id属性查询节点对象// 用来搜索节点的属性名var key = "id";// 用来搜索节点的属性值var value = window.id;var currentNode = zTreeObj.getNodeByParam(key, value);$("#removeNodeSpan").html("【<i class='"+currentNode.icon+"'></i>"+currentNode.name+"】");return false;
});

6.5、发送删除请求

  • 给更新模态框中的更新按钮绑定单击响应函数:

    • 通过Ajax发送删除请求
    • 通过layer组件显示删除是否成功
    • 更新成功则刷新菜单栏
    • 关闭模态框
// 给确认模态框中的OK按钮绑定单击响应函数
$("#confirmBtn").click(function(){$.ajax({"url":"menu/remove.json","type":"post","data":{"id":window.id},"dataType":"json","success":function(response){var result = response.result;if(result == "SUCCESS") {layer.msg("操作成功!");// 重新加载树形结构,注意:要在确认服务器端完成保存操作后再刷新// 否则有可能刷新不到最新的数据,因为这里是异步的generateTree();}if(result == "FAILED") {layer.msg("操作失败!"+response.message);}},"error":function(response){layer.msg(response.status+" "+response.statusText);}});// 关闭模态框$("#menuConfirmModal").modal("hide");
});

6.6、Handler代码

  • MenuHandler.java中删除选中的节点
@ResponseBody
@RequestMapping("/menu/remove.json")
public ResultEntity<String> removeMenu(@RequestParam("id") Integer id) {   menuService.removeMenu(id);return ResultEntity.successWithoutData();
}

6.7、Service代码

  • Service层实现上述方法
@Override
public void removeMenu(Integer id) {menuMapper.deleteByPrimaryKey(id);
}

6.8、测试

7、@RestController

7.1、@ResponseBody

  • 每个方法都标注了@ResponseBody注解,我们可以将其标注在类上
@Controller
@ResponseBody
public class MenuHandler {

7.2、@RestController

  • @Controller + @ResponseBody=@RestController
@RestController
public class MenuHandler {

day05【后台】菜单维护相关推荐

  1. fastAdmin后台一键生成后台菜单

    框架文档:https://doc.fastadmin.net/doc/crud.html 如果嫌麻烦不想看文档,可以看已下内容(我比较笨,虽然很简单,但需要记录下) 一:生成菜单 1.第一步首先在数据 ...

  2. 魔众文库系统 v2.3.0 后台菜单快捷搜索,组件显示优化

    魔众文库系统基于文档系统知识,建立平台与领域,打造流量.用户.付费和变现的闭环,帮助您更好的搭建文库系统. 魔众文库系统发布v2.3.0版本,新功能和Bug修复累计5项,后台菜单快捷搜索,组件显示优化 ...

  3. vue+iview-admin 利用适配器模式改造eova(伊娃管理后台)菜单及路由

    vue+iview-admin 利用适配器模式改造eova(伊娃管理后台)菜单及路由(1) 简单效果展示 改造完后效果 eova 及 iview的部署 略(日后再补充) 菜单功能核心改造 优化 ivi ...

  4. [django项目] 后台菜单管理功能

    后台菜单管理功能 菜单的管理功能其实就是, 对菜单的增删改查 I. 业务功能分析 1>业务需求分析 后台首页菜单根据用户权限动态生成,不同菜单对应不同的功能视图. 菜单的增删改查. 2>功 ...

  5. 魔众积分商城系统 v1.3.0 后台菜单快捷搜索,框架功能升级

    魔众积分商城系统提供积分商城,会员积分系统,积分兑换商城,积分系统,积分运营,营销活动工具,积分游戏.API接口对接会员积分系统,简单配置页面即可上线,助力企业会员运营管理. 魔众积分商城系统发布v1 ...

  6. 魔众抽奖系统 v3.4.0 后台菜单快捷搜索 优化已知字段问题

    魔众抽奖系统是一个在线抽奖系统,支持奖品池.积分.红包,抽奖方式支持大转盘.刮刮卡等,帮您快速的搭建一套私有化的抽奖系统 魔众抽奖系统发布v3.4.0版本,新功能和Bug修复累计7项,后台菜单快捷搜索 ...

  7. 魔众网盘系统 v1.3.0 后台菜单快捷搜索 修复已知问题

    魔众网盘系统提供了一套在线网盘管理系统,支持多用户.分享等功能,支持多种云存储,帮您快速搭建一套公私兼备的网盘系统 魔众网盘系统发布v1.3.0版本,新功能和Bug修复累计8项,后台菜单快捷搜索 修复 ...

  8. 魔众相册系统 v1.3.0 后台菜单快捷搜索 修复已知问题

    魔众相册系统是一个基于MySQL5与PHP5开发的相册系统,支持多分类.图片.视频.加密等特性,帮助您更好的搭建个人相册系统. 魔众相册系统发布v1.3.0版本,新功能和Bug修复累计7项,后台菜单快 ...

  9. 魔众题库系统 v6.4.0 题目导入优化 自动阅卷优化 后台菜单快捷搜索

    魔众题库系统基于PHP开发,可以用于题库管理和试卷生成软件,拥有极简界面和强大的功能,用户遍及全国各行各业. 魔众题库系统发布v6.4.0版本,新功能和Bug修复累计14项,题目导入优化 自动阅卷优化 ...

最新文章

  1. ubuntu1804系统设置在哪里_新风净化系统的风口到底该放在哪里?
  2. 不能装载通讯模块。驱动程序安装不正确。
  3. c++11多线程之packaged_task<>介绍与实例
  4. php 状态码302,HTTP状态码302、303和307的故事
  5. 深入浅出分布式文件系统MogileFS集群
  6. 单元素枚举类实现单例模式
  7. SAP License:HANA在线日志被误删后如何恢复?
  8. 土豆首页图片延迟加载的效果
  9. 量子计算机 脉冲,快十万倍!飞秒激光脉冲将带来超高速量子计算机
  10. 【iOS】崩溃收集和解析
  11. 什么是SMART原则?SMART原则是什么意思?
  12. CT图像之Hu值变换与窗宽窗位调整
  13. 2023湖南大学计算机考研信息汇总
  14. 什么是Bugzilla
  15. JavaScript 高级
  16. vue中用ref实现父子组件、孙组件、兄弟组件、非亲子孙组件互相调用的方法
  17. 中国科学院计算机院士,中国科学院院士夏培肃:我国计算机领域的先驱者
  18. Android-adb命令大全
  19. 选一种姿态,让自己活得无可替代
  20. 配置内网用户通过NAT地址池方式访问Internet外网

热门文章

  1. 曾经“杀手级”的桌面语言 Java 将要退隐江湖?
  2. 微软若“无故”解雇暴雪 CEO,将付 1500 万美元“分手费”
  3. Java 8备受宠爱,HarmonyOS冲刺全球第三大操作系统,全民热议元宇宙|2021十大技术热词
  4. 云原生是什么?它从哪里来?又到哪里去?
  5. 超越Tiny-YOLO V4,全新设计轻量化YOLO模型实现边缘实时检测!!!
  6. 马斯克亲口承认:自动驾驶的开发难度超乎想象;小米再发1.2亿股权激励;Firefox Lite 已死|极客头条...
  7. 都在夸官方文档 Vue.js 2021 年度报告出炉!
  8. 当推荐系统遇见知识图谱会发生什么?
  9. 满满的一整篇,全是 JVM 核心知识点!
  10. 弃用 Cookie!