「这是我参与11月更文挑战的第 6 天,活动详情查看:2021最后一次更文挑战」
1、前言
在上一篇文章中,我们分享了如何在后端通过双层for循环构建树形菜单结构,本次我们来详细分享下,如何通过递归以及hutool的树形结构工具类来构建树形菜单菜单,以及前端layui相关的代码。
2、递归构建树形结构
递归构建树形菜单就是从根节点的彩带一级一级的一直找下去,直到找不到子菜单为止。
/**
* 构建树形菜单结构
*/
public List<Node> getMenuTree(List<MenuTreeNode> menuList){
List<MenuTreeNode> resMenuTree = new ArrayList<>();
for (MenuTreeNode menuNode : menuList) {
if(Stringutils.isBlank(node.getUpperId())){
getChildMenu(menuNode, menuList);
resMenuTree.add(menuNode);
}
}
return resMenuTree;
}
/**
* 递归查找一个菜单下的所有子菜单
*/
public void getChildMenu(MenuTreeNode menuNode, List<MenuTreeNode> menuList){
List<MenuTreeNode> children = menuList.stream().filter(item->item.getUpperId().equals(menuNode.getId())).collect(Collectors.toList());
if(!CollectionUtils.isEmpty(children)){
for (MenuTreeNode node : children) {
getMenuTree(n,list);
}
menuNode.setChildren(children);
}
}
后来,无意间在hutool的参考文档中看到了树形工具类,才发现自己之前写的都是重复工作了。。
然后正好有一个树形标签的功能还没做完,正好就用了下hutool的TreeUtil,发现真的挺好用的,很方便。下面就结合代码分享下具体使用:
可以看到,代码还是很简单的,其中ROOT是需要我们自己定义的根菜单的parentID,然后设定好上下级关系的id与parentId字段,还有就是排序、节点名称,扩展属性这些。
private static final String ROOT = "00000000-0000-0000";
public List<Tree<String>> getTreeComTags() {
//1、获取到所有的标签信息
List<ComTag> allTags = readComTagMapper.getAllTags();
//2、构建树形列表
List<Tree<String>> treeNodes = TreeUtil.build(allTags, ROOT, new TreeNodeConfig(), (comTag, tree) -> {
tree.setId(comTag.getCom_tag_id());
tree.setParentId(comTag.getCom_tag_parent_id());
tree.setWeight(comTag.getSort());
tree.setName(comTag.getTag_name());
// 扩展属性
tree.putExtra("tag_type", comTag.getTag_type());
tree.putExtra("add_time", comTag.getAdd_time());
});
return treeNodes;
}
3、前端代码
由于之前前端代码写的很少,并且layui的文档稍微有些简单,实际开发过程中还是遇到了很多问题,在此记录下,避免后续有类似需求的开发踩坑。
<!-- 左侧菜单开始 -->
<div class="left-nav">
<div id="side-nav">
<ul id="nav"></ul>
</div>
</div>
<!-- <div class="x-slide_left"></div> -->
<!-- 左侧菜单结束 -->
由于我现在开发的这个项目里还用到了二次封装的layui框架xadmin,而且layui的版本也比较低(v2.2),刚入职1个多月,我也不敢贸然改版本,这里的树形菜单就在JS里通过拼接的方式实现了,并且需求说了只有二级菜单,不会有更多级。为了快速上线,只能先这样简单暴力了,后续再仔细研究下layui的menu模块,实现一个无限极的菜单加载。
$(function () {
$.ajax({
type: "GET",
url: "/loadMenu",
dataType: "json",
success: function (data) {
let menuList = "";
for (let i = 0, len = data.length; i < len; i++) {
let menuLi = "<li>\n" +
" <a href=\"javascript:;\">\n" +
" <i class=\"iconfont\"></i>\n" +
" <cite>" + data[i]['menu_name'] + "</cite>\n" +
" <i class=\"iconfont nav_right\"></i>\n" +
" </a>";
let childrenUl = "";
let childrenMenu = data[i]['children'];
for (let j = 0, length = childrenMenu.length; j < length; j++) {
childrenUl = childrenUl + "<ul class=\"sub-menu\">\n" +
" <li>\n" +
" <a _href=\"" + childrenMenu[j]['menu_url'] + "\">\n" +
" <i class=\"iconfont\"></i>\n" +
" <cite>" + childrenMenu[j]['menu_name'] + "</cite>\n" +
" </a>\n" +
" </li>\n" +
" </ul>"
}
menuLi = menuLi + childrenUl + "</li>";
menuList += menuLi;
}
$("#nav").html(menuList);
//左侧菜单效果
$('.left-nav #nav li').click(function (event) {
if($(this).children('.sub-menu').length){
if($(this).hasClass('open')){
$(this).removeClass('open');
$(this).find('.nav_right').html('');
$(this).children('.sub-menu').stop().slideUp();
$(this).siblings().children('.sub-menu').slideUp();
}else{
$(this).addClass('open');
$(this).children('a').find('.nav_right').html('');
$(this).children('.sub-menu').stop().slideDown();
$(this).siblings().children('.sub-menu').stop().slideUp();
$(this).siblings().find('.nav_right').html('');
$(this).siblings().removeClass('open');
}
}else{
var url = $(this).children('a').attr('_href');
var title = $(this).find('cite').html();
var index = $('.left-nav #nav li').index($(this));
for (var i = 0; i <$('.x-iframe').length; i++) {
if($('.x-iframe').eq(i).attr('tab-id')==index+1){
tab.tabChange(index+1);
event.stopPropagation();
return;
}
};
tab.tabAdd(title,url,index+1);
tab.tabChange(index+1);
}
event.stopPropagation();
})
}
});
//触发事件
var tab = {
tabAdd: function(title,url,id){
//新增一个Tab项
element.tabAdd('xbs_tab', {
title: title
,content: '<iframe tab-id="'+id+'" frameborder="0" src="'+url+'" scrolling="yes" class="x-iframe"></iframe>'
,id: id
})
}
,tabDelete: function(othis){
//删除指定Tab项
element.tabDelete('xbs_tab', '44');
othis.addClass('layui-btn-disabled');
}
,tabChange: function(id){
//切换到指定Tab项
element.tabChange('xbs_tab', id);
}
};
})
整体来讲上述代码还是很简单的,就是刚开始由于项目里使用的layui版本太低,在网上找不到对应文档和示例,只能看源码然后自己一遍遍的去试,浪费了很多时间,最后还是用了最简单暴力的拼接方法,虽然写的很low,但时间紧迫也只能后面慢慢优化了。