前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >layui三级菜单渲染

layui三级菜单渲染

作者头像
猿码优创
发布2019-07-27 21:07:35
2.3K1
发布2019-07-27 21:07:35
举报
文章被收录于专栏:猿码优创猿码优创

项目做完了,可以有时间写写博客了。 今天给大家讲解一下layui的三级动态加载菜单含后端代码。

代码语言:javascript
复制
我是最近刚学的layui,非常感谢贤心大神。开发出这么牛逼ui的框架。
声明:KingYiFan前段是渣渣,本次讲解用的前端js并非KingYiFan亲自封装只是稍作修改,非常感谢封装三级菜单CSDN的大神:yufengaotian	

下图就是三级菜单效果。为什么要讲这个呢。

file
file

yufenggaotian大神也在博客中写道这个问题我就借用一下:

file
file

(图来着yufenggaotianCSDN博客)

前端页面:

代码语言:javascript
复制
//直接上代码吧
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>三级菜单展示</title>
</head>
<body>

<div class="layui-side layui-bg-black" id="admin-side">
    <div class="layui-side-scroll">
        <ul class="layui-nav layui-nav-tree" id="nav" lay-filter="demo"></ul>
    </div>
</div>
</body>

<link rel="stylesheet" href="/static/css/layui.css">
<script src="/static/js/jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="/static/lib/layui/layui.js"></script>
<script src="/static/js/api.js"></script>
<script>
    //监听选中页签添加样式
    layui.config({
        base: '/static/lib/layui/'   //navbar组件js所在目录
    }).use('navbar', function() {
        var navbar = layui.navbar();
        navbar.set({
            elem: '#nav',
            url: "/menuInfo" //访问后台接口
        });
        navbar.render();
      
        //给选中的页签添加选中样式(解决刷新失效问题)
        var url = window.location.href.replace("//", "");
        var relUrl = url.substring(url.lastIndexOf("/") + 1);
        //去掉参数部分
        if (relUrl.indexOf("?") != -1) {
            relUrl = relUrl.split("?")[0];
        }
        $("#leftNavbar a").each(function () {
            var that = this;
            if ($(that).attr("href") == relUrl) {
                $(that).parent().addClass("layui-this");
                $(that).parents("li:eq(0)").addClass("layui-nav-itemed");
                var nodes = $(that).parents("li:eq(0)").find("a .layui-nav-more");
                if (nodes.length > 0) {
                    nodes.each(function () {
                        if ($(this).parents("dd:eq(0)").find("[href='" + relUrl +
                            "']").length > 0) {
                            $(this).parent().parent().addClass("layui-nav-itemed");
                        }
                    });
                }
            }
        });

    });
</script>
</html>

js部分可以不做修改

代码语言:javascript
复制
layui.define(['element', 'common'], function (exports) {
    "use strict";
    var $ = layui.jquery,
        layer = parent.layer === undefined ? layui.layer : parent.layer,
        element = layui.element,
        common = layui.common,
        cacheName = 'tb_navbar';

    var Navbar = function () {
        /**
         *  默认配置
         */
        this.config = {
            elem: undefined, //容器
            data: undefined, //数据源
            url: undefined, //数据源地址
            type: 'GET', //读取方式
            cached: false, //是否使用缓存
            spreadOne: false //设置是否只展开一个二级菜单
        };
        this.v = '1.0.0';
    };
    //渲染
    Navbar.prototype.render = function () {
        var _that = this;
        var _config = _that.config;
        if (typeof (_config.elem) !== 'string' && typeof (_config.elem) !== 'object') {
            common.throwError('Navbar error: elem参数未定义或设置出错,具体设置格式请参考文档API.');
        }
        var $container;
        if (typeof (_config.elem) === 'string') {
            $container = $('' + _config.elem + '');
        }
        if (typeof (_config.elem) === 'object') {
            $container = _config.elem;
        }
        if ($container.length === 0) {
            common.throwError('Navbar error:找不到elem参数配置的容器,请检查.');
        }
        if (_config.data === undefined && _config.url === undefined) {
            common.throwError('Navbar error:请为Navbar配置数据源.')
        }
        if (_config.data !== undefined && typeof (_config.data) === 'object') {
            var html = getHtml(_config.data);
            $container.html(html);
            element.init();
            _that.config.elem = $container;
        } else {
            if (_config.cached) {
                var cacheNavbar = layui.data(cacheName);
                if (cacheNavbar.navbar === undefined) {
                    $.ajax({
                        type: _config.type,
                        url: _config.url,
                        async: false, //_config.async,
                        dataType: 'json',
                        success: function (result, status, xhr) {
                            //添加缓存
                            layui.data(cacheName, {
                                key: 'navbar',
                                value: result
                            });
                            var html = getHtml(result);
                            $container.html(html);
                            element.init();
                        },
                        error: function (xhr, status, error) {
                            common.msgError('Navbar error:' + error);
                        },
                        complete: function (xhr, status) {
                            _that.config.elem = $container;
                        }
                    });
                } else {
                    var html = getHtml(cacheNavbar.navbar);
                    $container.html(html);
                    element.init();
                    _that.config.elem = $container;
                }
            } else {
                //清空缓存
                layui.data(cacheName, null);
                $.ajax({
                    type: _config.type,
                    url: _config.url,
                    async: false, //_config.async,
                    dataType: 'json',
                    success: function (result, status, xhr) {
                        var html = getHtml(result);
                        $container.html(html);
                        element.init();
                    },
                    error: function (xhr, status, error) {
                        common.msgError('Navbar error:' + error);
                    },
                    complete: function (xhr, status) {
                        _that.config.elem = $container;
                    }
                });
            }
        }

        //只展开一个二级菜单
        if (_config.spreadOne) {
            var $ul = $container.children('ul');
            $ul.find('li.layui-nav-item').each(function () {
                $(this).on('click', function () {
                    $(this).siblings().removeClass('layui-nav-itemed');
                });
            });
        }
        return _that;
    };
    /**
     * 配置Navbar
     * @param {Object} options
     */
    Navbar.prototype.set = function (options) {
        var that = this;
        that.config.data = undefined;
        $.extend(true, that.config, options);
        return that;
    };
    /**
     * 绑定事件
     * @param {String} events
     * @param {Function} callback
     */
    Navbar.prototype.on = function (events, callback) {
        var that = this;
        var _con = that.config.elem;
        if (typeof (events) !== 'string') {
            common.throwError('Navbar error:事件名配置出错,请参考API文档.');
        }
        var lIndex = events.indexOf('(');
        var eventName = events.substr(0, lIndex);
        var filter = events.substring(lIndex + 1, events.indexOf(')'));
        if (eventName === 'click') {
            if (_con.attr('lay-filter') !== undefined) {
                _con.children('ul').find('li').each(function () {
                    var $this = $(this);
                    if ($this.find('dl').length > 0) {
                        var $dd = $this.find('dd').each(function () {
                            $(this).on('click', function () {
                                var $a = $(this).children('a');
                                var href = $a.data('url');
                                var icon = $a.children('i:first').data('icon');
                                var title = $a.children('cite').text();
                                var data = {
                                    elem: $a,
                                    field: {
                                        href: href,
                                        icon: icon,
                                        title: title
                                    }
                                }
                                callback(data);
                            });
                        });
                    } else {
                        $this.on('click', function () {
                            var $a = $this.children('a');
                            var href = $a.data('url');
                            var icon = $a.children('i:first').data('icon');
                            var title = $a.children('cite').text();
                            var data = {
                                elem: $a,
                                field: {
                                    href: href,
                                    icon: icon,
                                    title: title
                                }
                            }
                            callback(data);
                        });
                    }
                });
            }
        }
    };
    /**
     * 清除缓存
     */
    Navbar.prototype.cleanCached = function () {
        layui.data(cacheName, null);
    };
    /**
     * 获取html字符串
     * @param {Object} data
     */
    function getHtml(data) {
        var ulHtml = '<ul class="layui-nav layui-nav-tree beg-navbar">';
        for (var i = 0; i < data.length; i++) {
            if (data[i].spread) {
                ulHtml += '<li class="layui-nav-item layui-nav-itemed">';
            } else {
                ulHtml += '<li class="layui-nav-item">';
            }

            if (data[i].children !== undefined && data[i].children !== null && data[i].children.length > 0) {

                ulHtml += '<a href="javascript:;">' + data[i].title;
                ulHtml += '<span class="layui-nav-more"></span>';
                ulHtml += '</a>';
                ulHtml += '<dl class="layui-nav-child">';
                //二级菜单
                for (var j = 0; j < data[i].children.length; j++) {
                    //是否有孙子节点
                    if (data[i].children[j].children !== undefined && data[i].children[j].children !== null && data[i].children[j].children.length > 0) {
                        ulHtml += '<dd>';
                        ulHtml += '<a href="javascript:;">' + data[i].children[j].title;
                        ulHtml += '<span class="layui-nav-more"></span>';
                        ulHtml += '</a>';
                        //三级菜单
                        ulHtml += '<dl class="layui-nav-child">';
                        var grandsonNodes = data[i].children[j].children;
                        for (var k = 0; k < grandsonNodes.length; k++) {
                            ulHtml += '<dd>';
                            ulHtml += '<a target="xq" href="'+ grandsonNodes[k].href +'">' + grandsonNodes[k].title + '</a>';
                            ulHtml += '</dd>';
                        }
                        ulHtml += '</dl>';
                        ulHtml += '</dd>';
                    }else{
                        ulHtml += '<dd>';
                        ulHtml += '<a target="xq" href="'+data[i].children[j].href+'">' + data[i].children[j].title;
                        ulHtml += '</a>';
                        ulHtml += '</dd>';
                    }
                    //ulHtml += '<dd title="' + data[i].children[j].title + '">';
                }
                ulHtml += '</dl>';
            } else {
                var dataUrl = (data[i].href !== undefined && data[i].href !== '') ? 'data-url="' + data[i].href + '"' : '';
                //ulHtml += '<a href="javascript:;" ' + dataUrl + '>';
                ulHtml += '<a href="' + data[i].href + '"' + dataUrl + 'target="xq">';
                if (data[i].icon !== undefined && data[i].icon !== '') {
                        ulHtml +=  '<i  class="layui-icon" >' +'<img src=' +data[i].icon +'>'+'</i>';
                }
                ulHtml += '<cite>' + data[i].title + '</cite>';
                ulHtml += '</a>';
            }
            ulHtml += '</li>';
        }
        ulHtml += '</ul>';

        return ulHtml;
    }

    var navbar = new Navbar();

    exports('navbar', function (options) {
        return navbar.set(options);
    });
});
common.js
layui.define(['layer'], function(exports) {
    "use strict";

    var $ = layui.jquery,
        layer = layui.layer;

    var common = {
        /**
         * 抛出一个异常错误信息
         * @param {String} msg
         */
        throwError: function(msg) {
            throw new Error(msg);
            return;
        },
        /**
         * 弹出一个错误提示
         * @param {String} msg
         */
        msgError: function(msg) {
            layer.msg(msg, {
                icon: 5
            });
            return;
        }
    };

    exports('common', common);
});
代码语言:javascript
复制
json格式:
[
	{
		"title": "我是一级",
		"icon": "",
		"spread": true,
		"href": "",
		"children": [
			{
				"title": "我是二级",
				"icon": "",
				"href": "",
				"spread": true,
				"children": [
					{
						"title": "我是三级",
						"icon": " ",
						"href": "https://cnbuilder.cn/"
					},
					{
						"title": "我也是三级",
						"icon": " ",
						"href": "https://cnbuilder.cn/"
					}
				]
			}
		]
	}
]

java代码部分 1、用到了MyBatisPlus技术 2、用到了Lombok 所以代码量很少

代码语言:javascript
复制
controller:
	/**
     * 1、根据用户id查询角色
     * 2、遍历角色查询资源
     * KingYiFan create By 2019/03/14
     * @return
     */
    @GetMapping("/menuInfo")
    @ResponseBody
    public Object menuInfo() {
        if (getShiroUser().getId() == null) {
            return "redirect:/login";
        }
		//查询用户的菜单
        List<BsResource> bsResources = bsRoleService.selectMenuByUserId(getShiroUser().getId());
        //根据菜单封装需要的格式
		List<BsResource> trees = TreeBuilder.bulid(bsResources);
        return trees;
    }
代码语言:javascript
复制
service接口
 @Override
    public List<BsResource> selectMenuByUserId(Long userId) {

        //根据用户id查询所有角色
        List<Long> roleIdList = bsTenantRoleMapper.selectRoleIdListByTenantId(userId);
        Set<String> urlSet = new HashSet<String>();
        //遍历全部角色
        for (Long roleId : roleIdList) {
            //根据角色获取当前资源
            List<Map<String, String>> resourceList = bsRoleMapper.selectResourceListByRoleId(roleId);
            for (Map<String, String> map : resourceList) {
                urlSet.add(map.get("id"));
            }
        }
        //根据id查询菜单
        List<BsResource> bsResources = bsResourceMapper.selectBatchIds(urlSet);
        return bsResources;
    }

实体类

代码语言:javascript
复制
package cn.cnbuilder.entity;

import com.baomidou.mybatisplus.annotations.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.hcr.model.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.util.List;

/**
 * <p>
 * 资源表 这个是全局的资源表
 * </p>
 *
 * @author KingYiFan
 * @since 2019-03-14
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class BsResource {

    private static final long serialVersionUID = 1L;

    private Long id;

    @TableField(exist = false)
    private List<BsResource> children;
    /**
     * 资源名称
     */
    private String title;

    /**
     * 资源路径
     */
    private String href;

    /**
     * 资源介绍
     */
    private String description;

    /**
     * 资源图标
     */
    private String icon;

    /**
     * 父级资源id
     */
    @TableField(value = "p_id")
    private Integer pId;

    /**
     * 排序
     */
    private String seq;

    /**
     * 状态
     */
    private Integer status;

    /**
     * 打开状态
     */
    private Integer spread;

    /**
     * 资源类别
     */
    private Integer resourceType;
}

工具类

代码语言:javascript
复制
package cn.cnbuilder.utils;

import cn.cnbuilder.entity.BsResource;

import java.util.List;


import java.util.ArrayList;
import java.util.List;

/**
 * Created by KingYiFan on  2019/03/14.
 */
public class TreeBuilder {

    /**
     * 两层循环实现建树
     *
     * @param BsResources 传入的树节点列表
     * @return
     */
    public static List<BsResource> bulid(List<BsResource> BsResources) {

        List<BsResource> trees = new ArrayList<BsResource>();

        for (BsResource BsResource : BsResources) {

            if (BsResource.getPId()==0) {
                trees.add(BsResource);
            }

            for (BsResource it : BsResources) {
                if (it.getPId() == BsResource.getId().intValue()) {
                    if (BsResource.getChildren() == null) {
                        BsResource.setChildren(new ArrayList<BsResource>());
                    }
                    BsResource.getChildren().add(it);
                }
            }
        }
        return trees;
    }

    /**
     * 使用递归方法建树
     *
     * @param BsResources
     * @return
     */
    public static List<BsResource> buildByRecursive(List<BsResource> BsResources) {
        List<BsResource> trees = new ArrayList<BsResource>();
        for (BsResource BsResource : BsResources) {
            if ("0".equals(BsResource.getPId())) {
                trees.add(findChildren(BsResource, BsResources));
            }
        }
        return trees;
    }

    /**
     * 递归查找子节点
     *
     * @param BsResources
     * @return
     */
    public static BsResource findChildren(BsResource BsResource, List<BsResource> BsResources) {
        for (BsResource it : BsResources) {
            if (BsResource.getId().equals(it.getPId())) {
                if (BsResource.getChildren() == null) {
                    BsResource.setChildren(new ArrayList<BsResource>());
                }
                BsResource.getChildren().add(findChildren(it, BsResources));
            }
        }
        return BsResource;
    }


    public static void main(String[] args) {
        List<BsResource> list = new ArrayList<BsResource>();
        List<BsResource> trees = TreeBuilder.bulid(list);
        List<BsResource> trees_ = TreeBuilder.buildByRecursive(list);

    }
}

这就是layui动态三级菜单渲染。有什么不懂的可以跟我联系。


代码语言:javascript
复制
感谢一路支持我的人。。。。。

Love me and hold me
QQ:69673804(16年老号)
EMAIL:69673804@qq.com
友链交换
如果有兴趣和本博客交换友链的话,请按照下面的格式在评论区进行评论,我会尽快添加上你的链接。

代码语言:javascript
复制
网站名称:KingYiFan’S Blog
网站地址:http://blog.cnbuilder.cn
网站描述:年少是你未醒的梦话,风华是燃烬的彼岸花。
网站Logo/头像: [头像地址](https://blog.cnbuilder.cn/upload/2018/7/avatar20180720144536200.jpg)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档