layui实现树形菜单(二)

894 阅读3分钟

「这是我参与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\">&#xe723;</i>\n" +
                    "                    <cite>" + data[i]['menu_name'] + "</cite>\n" +
                    "                    <i class=\"iconfont nav_right\">&#xe697;</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\">&#xe6a7;</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('&#xe697;');
                        $(this).children('.sub-menu').stop().slideUp();
                        $(this).siblings().children('.sub-menu').slideUp();
                    }else{
                        $(this).addClass('open');
                        $(this).children('a').find('.nav_right').html('&#xe6a6;');
                        $(this).children('.sub-menu').stop().slideDown();
                        $(this).siblings().children('.sub-menu').stop().slideUp();
                        $(this).siblings().find('.nav_right').html('&#xe697;');
                        $(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,但时间紧迫也只能后面慢慢优化了。