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;
// 父节点的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() {
// 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;
}
@Override
public 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 {
@Autowired
private MenuService menuService;
// 未改进的方法,存在嵌套循环,性能不好
public ResultEntity<Menu> getWholeTreeOld() {
// 1.查询全部的Menu对象
List<Menu> menuList = menuService.getAll();
// 2.声明一个变量用来存储找到的根节点
Menu root = null;
// 3.遍历menuList
for (Menu menu : menuList) {
// 4.获取当前menu对象的pid属性值
Integer pid = menu.getPid();
// 5.检查pid是否为null
if(pid == null) {
// 6.把当前正在遍历的这个menu对象赋值给root
root = 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填充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);
}
}
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
文档部署到Tomcat
的webapp
目录下
- 启动
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标签的id
console.log("treeId="+treeId);
// 当前树形节点的全部的数据,包括从后端查询得到的Menu对象的全部属性
console.log(treeNode);
// zTree生成id的规则
// 例子:treeDemo_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);
}
- 在
menu-page.jsp
页面中设置zTree
的addDiyDom
属性,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
页面中设置zTree
的url
属性,设置成一个不存在的值;当点击单个节点,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设置有规律的id var 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='添加子节点'> <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='删除节点'> <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='修改节点'> <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) { // 拼接按钮组的id var 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='添加子节点'> <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">×</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" />
<i class="glyphicon glyphicon-dashboard"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-dashboard" />
<i class="glyphicon glyphicon glyphicon-tasks"></i>
<input type="radio" name="icon" value="glyphicon glyphicon glyphicon-tasks" />
<i class="glyphicon glyphicon-user"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-user" />
<i class="glyphicon glyphicon-king"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-king" />
<i class="glyphicon glyphicon-lock"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-lock" />
<i class="glyphicon glyphicon-ok"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-ok" />
<i class="glyphicon glyphicon-check"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-check" />
<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" />
<i class="glyphicon glyphicon-equalizer"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-equalizer" />
<i class="glyphicon glyphicon-random"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-random" />
<i class="glyphicon glyphicon-hdd"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-hdd" />
<i class="glyphicon glyphicon-comment"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-comment" />
<i class="glyphicon glyphicon-list"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-list" />
<i class="glyphicon glyphicon-tags"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-tags" />
<i class="glyphicon glyphicon-list-alt"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-list-alt" />
<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='修改节点'> <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">×</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" />
<i class="glyphicon glyphicon-dashboard"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-dashboard" />
<i class="glyphicon glyphicon glyphicon-tasks"></i>
<input type="radio" name="icon" value="glyphicon glyphicon glyphicon-tasks" />
<i class="glyphicon glyphicon-user"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-user" />
<i class="glyphicon glyphicon-king"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-king" />
<i class="glyphicon glyphicon-lock"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-lock" />
<i class="glyphicon glyphicon-ok"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-ok" />
<i class="glyphicon glyphicon-check"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-check" />
<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" />
<i class="glyphicon glyphicon-equalizer"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-equalizer" />
<i class="glyphicon glyphicon-random"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-random" />
<i class="glyphicon glyphicon-hdd"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-hdd" />
<i class="glyphicon glyphicon-comment"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-comment" />
<i class="glyphicon glyphicon-list"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-list" />
<i class="glyphicon glyphicon-tags"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-tags" />
<i class="glyphicon glyphicon-list-alt"></i>
<input type="radio" name="icon" value="glyphicon glyphicon-list-alt" />
<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='删除节点'> <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">×</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 {