day05【后台】菜单维护

day05【后台】菜单维护

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

1.1、节点类型

image-20200919191131400

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、表的结构

  • 菜单表为自关联表

image-20200613224550062

image-20200919191308304

2、Java中表示树形结构

2.1、逆向工程

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

image-20200614095110688

<?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

image-20200614095205111

2.1.3、资源归位
  • JavaBean归位至entity工程

image-20200614095316378

  • Mapper接口归位至component工程

image-20200614095605086

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

image-20200614095533500

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

image-20200614095956973

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

image-20200614100049600

3、页面显示树形结构

3.1、准备工作

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

image-20200614102554393

<mvc:view-controller 
	path="/menu/to/page.html" view-name="menu-page"/>
3.1.2、添加菜单栏页面
  • WEB-INF文件夹下添加menu-page.jsp页面,用于显示菜单栏

image-20200614103005925

image-20200614103133775

<%@ 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、跳转地址
  • 修改侧边栏中【菜单维护】按钮的跳转地址

image-20200614113043043

<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数据

image-20200614102149598

3.4、前端代码

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

image-20200614112357820

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

image-20200614103005925

<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>
  • 显示效果

image-20200614113715676

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

image-20200614103005925

<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>
  • 显示效果

image-20200614155205854

3.4.4、准备zTree文档
  • 需要将zTree文档部署到Tomcatwebapp目录下

image-20200614161004658

image-20200614160729411

  • 启动Tomcat,访问API文档

image-20200614160905684

  • 我擦嘞:Edge可以直接访问本地文件,无需部署
3.4.5、zTree显示图标原理
  • 修改<span/>标签的class属性即可

image-20200614160107437

<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

image-20200614161054104

3.4.7、引入JavaScript库
  • menu-page.jsp页面中引入my-menu.js

image-20200614161210990

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

image-20200614161054104

// 修改默认的图标
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页面中设置zTreeaddDiyDom属性,zTree加载菜单栏时,将回调我们自定义的myAddDiyDom函数,这样就能修改默认图标啦~

image-20200614103005925

<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开始)

image-20200614212604463

  • 测试

image-20200614161825424

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

image-20200919191548488

<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>
  • 鼠标单击可选中节点

image-20200614183848872

3.4.10、3.4.10、显示按钮组
  • zTree中按钮组的结构如下

image-20200614185851000

  • 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='添加子节点'>&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) {
    	
    	// 拼接按钮组的id
    	var btnGroupId = treeNode.tId + "_btnGrp";
    	
    	// 移除对应的元素
    	$("#"+btnGroupId).remove();
    	
    }
    
  • 显示效果

image-20200614211653219

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

image-20200614161054104

//生成树形结构的函数
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函数

image-20200614103005925

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

</script>

4、添加子节点

4.1、思路

image-20200919191606818

4.2、准备工作

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

image-20200614213245905

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

image-20200614214237756

<%@ 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中保存节点至数据库

image-20200614095956973

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

4.7、Service代码

  • Service层实现上述方法

image-20200614100049600

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

4.8、测试

  • 添加子节点

image-20200615104046269

  • 添加成功~

image-20200615104139565

5、更新节点信息

5.1、思路

image-20200919191619620

5.2、准备工作

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

image-20200615112016964

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

image-20200615104339752

<%@ 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页面中引入模态框

image-20200615111722640

<%@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、测试

image-20200615111822141

image-20200615111845080

6、删除节点

6.1、思路

image-20200919191634040

6.2、准备工作

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

image-20200615104634351

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

image-20200615112100873

<%@ 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页面中引入模态框

image-20200615112223151

<%@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、测试

image-20200615141213630

image-20200615141227671

7、@RestController

7.1、@ResponseBody

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

7.2、@RestController

  • @Controller + @ResponseBody=@RestController
@RestController
public class MenuHandler {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值