1 建模
1.1 创建数据库表
CREATE TABLE t_menu (
id INT (11) NOT NULL auto_increment,
pid INT (11),
name
VARCHAR (200),
url VARCHAR (200),
icon VARCHAR (200),
PRIMARY KEY (id)
);
1.2创建实体类
成的Menu实体类需要做一些修改
// 对应数据库表主键
private Integer id;
// 对应父节点id(如果pid为null,则说明当前节点是根节点)
private Integer pid;
// 节点名称
private String name;
// 节点对应的URL地址
private String url;
// 节点的图标
private String icon;
// 当前节点的子节点集合,设置默认值是为了避免组装节点时空指针异常
private List<Menu> children = new ArrayList<>();
// 控制节点展开还是折叠,设置为true是让整个树形菜单默认展开
private Boolean open = true;
2 在页面显示树形结构
2.1 后端操作
把树形结构组装好,具体来说是:给前端返回根节点对象。在根节点中包含子节点,子节点中再包含下一级的子节点。
@RequestMapping("/menu/get/whole/tree")
public ResultEntity<Menu> getWholeTree() {
// 1.查询所有的树形节点用于组装
List<Menu> menuList = menuService.getAll();
// 2.将List<Menu>转换为Map<Menu的id,Menu>
Map<Integer,Menu> menuMap = new HashMap<>();
for (Menu menu : menuList) {
Integer id = menu.getId();
menuMap.put(id, menu);
}
// 3.声明变量用于存储根节点对象
Menu rootNode = null;
// 4.遍历List<Menu>
for (Menu menu : menuList) {
// 5.获取当前Menu对象的pid属性
Integer pid = menu.getPid();
// 6.判断pid是否为null
if(pid == null) {
// 7.如果pid为null,说明当前节点是根节点,所以赋值
rootNode = menu;
// 8.根节点没有父节点,所以不必找父节点组装,本次for循环停止执行,继续执行下一次循环
continue ;
}
// 9.既然pid不为null,那么我们根据这个pid查找当前节点的父节点。
Menu father = menuMap.get(pid);
// 10.组装:将menu添加到maybeFather的子节点集合中
father.getChildren().add(menu);
}
return ResultEntity.successWithData(rootNode);
}
2.2 前端操作
2.2.1 创建menu-page.jsp
主体内容如下:
ztree
2.2.3 显示树形结构
function initWholeTree() {
// setting对象中包含zTree的设置属性
var setting = {
"view": {
"addDiyDom": showMyIcon,
"addHoverDom": addHoverDom,
"removeHoverDom": removeHoverDom
},
"data": {
"key": {
"url": "notExistsProperty" // 阻止点击节点后跳转
}
}
};
// 发送Ajax请求获取zNodes数据
$.ajax({
"url":"menu/get/whole/tree.json",
"type":"post",
"dataType":"json",
"success":function(response){
var result = response.result;
if(result == "SUCCESS") {
// 用于显示树形结构的节点数据
var zNodes = response.data;
// 执行树形结构的初始操作
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}
if(result == "FAILED") {
layer.msg("加载菜单数据失败!原因是:"+response.message);
}
},
"error":function(response){
layer.msg("加载菜单数据失败!原因是:"+response.message);
}
});
}
2.2.4 声明函数将图标修改为自定义图标
// 由setting.view.addDiyDom属性引用,负责显示自定义图标
// treeId是<ul id="treeDemo" class="ztree"></ul>的id属性值
// treeNode对应每一个树形节点
function showMyIcon(treeId, treeNode) {
// 获取当前节点的id
var currentNodeId = treeNode.tId;
// 获取新的class值用于替换
var newClass = treeNode.icon;
// 在当前节点id之后附加“_ico”得到目标span的id
var targetSpanId = currentNodeId + "_ico";
// 将目标span的旧class移除,添加新class
$("#"+targetSpanId)
.removeClass()
.addClass(newClass)
.css("background","");
}
附:节点图标修改前后HTML代码变化
2.2.5 节点结构
id结构:treeDemo_节点序号_节点具体部件
2.2.6 阻止点击节点后跳转
使用了setting.data.key.url属性
3 生成按钮组
3.1 按钮组生成规则
3.1.1 根节点(level 0)
增加子节点
3.1.2 level为1的有子节点的节点
增加子节点
修改
3.1.3 level为1的无子节点的节点
增加子节点
删除
修改
3.1.4 level为2的节点
删除
修改
3.2 设计按钮组所在span的id
规则:treeDemo_序号_btnGrp
举例:treeDemo_20_btnGrp
3.3 各个按钮HTML标签
3.4 声明函数生成按钮组
// 专门生成按钮组的函数
function generateBtnGrp(treeNode) {
// 获取当前节点的id(HTML中li标签的id)
var treeNodeId = treeNode.tId;
// 获取当前节点在数据库中的id值
// Menu实体类中的属性,都可以通过treeNode以“.属性名”的方式直接访问
var menuId = treeNode.id;
// 组装按钮组所在的span的id
var btnGrpSpanId = treeNodeId + "_btnGrp";
// 生成span标签对应的jQuery对象
var $span = $("<span id='"+btnGrpSpanId+"'></span>");
// 获取当前节点的level值
var level = treeNode.level;
// 声明三种按钮
var addBtn = "<a οnclick='showAddModal(this)' id='"+menuId+"' class='btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title='添加子节点'> <i class='fa fa-fw fa-plus rbg'></i></a>";
var editBtn = "<a οnclick='showEditModal(this)' id='"+menuId+"' class='btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title='编辑节点'> <i class='fa fa-fw fa-edit rbg'></i></a>";
var removeBtn = "<a οnclick='showConfirmModal(this)' id='"+menuId+"' class='btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title='删除节点'> <i class='fa fa-fw fa-times rbg'></i></a>";
// 根据level进行判断
if(level == 0) {
$span.append(addBtn);
}
if(level == 1) {
if(treeNode.children.length > 0) {
$span.append(addBtn+" "+editBtn);
} else {
$span.append(addBtn+" "+editBtn+" "+removeBtn);
}
}
if(level == 2) {
$span.append(editBtn+" "+removeBtn);
}
return $span;
}
3.5 addHoverDom(treeId, treeNode)函数
// 在鼠标移入节点范围时添加自定义控件
function addHoverDom(treeId, treeNode) {
// 在执行添加前,先进行判断,如果已经添加过,就停止执行
// 组装按钮组所在的span标签的id
var btnGrpSpanId = treeNode.tId + "_btnGrp";
var btnGrpSpanLength = $("#"+btnGrpSpanId).length;
if(btnGrpSpanLength > 0) {
return ;
}
// 由于按钮组是放在当前节点中的超链接(a标签)后面,所以先定位到a标签
// 按id生成规则组装a标签的id
var anchorId = treeNode.tId + "_a";
// 调用已封装函数生成按钮组
var $btnGrpSpan = generateBtnGrp(treeNode);
// 在a标签后面追加按钮组
$("#"+anchorId).after($btnGrpSpan);
}
3.6 removeHoverDom(treeId, treeNode)函数
// 在鼠标移出节点范围时删除自定义控件
function removeHoverDom(treeId, treeNode) {
// 组装按钮组所在的span标签的id
var btnGrpSpanId = treeNode.tId + "_btnGrp";
// 删除对应的元素
$("#"+btnGrpSpanId).remove();
}