layui三级菜单渲染

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

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

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

file

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

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(&#34;//&#34;, &#34;&#34;);
    var relUrl = url.substring(url.lastIndexOf(&#34;/&#34;) + 1);
    //去掉参数部分
    if (relUrl.indexOf(&#34;?&#34;) != -1) {
        relUrl = relUrl.split(&#34;?&#34;)[0];
    }
    $(&#34;#leftNavbar a&#34;).each(function () {
        var that = this;
        if ($(that).attr(&#34;href&#34;) == relUrl) {
            $(that).parent().addClass(&#34;layui-this&#34;);
            $(that).parents(&#34;li:eq(0)&#34;).addClass(&#34;layui-nav-itemed&#34;);
            var nodes = $(that).parents(&#34;li:eq(0)&#34;).find(&#34;a .layui-nav-more&#34;);
            if (nodes.length &gt; 0) {
                nodes.each(function () {
                    if ($(this).parents(&#34;dd:eq(0)&#34;).find(&#34;[href=&#39;&#34; + relUrl +
                        &#34;&#39;]&#34;).length &gt; 0) {
                        $(this).parent().parent().addClass(&#34;layui-nav-itemed&#34;);
                    }
                });
            }
        }
    });

});

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

    //只展开一个二级菜单
    if (_config.spreadOne) {
        var $ul = $container.children(&#39;ul&#39;);
        $ul.find(&#39;li.layui-nav-item&#39;).each(function () {
            $(this).on(&#39;click&#39;, function () {
                $(this).siblings().removeClass(&#39;layui-nav-itemed&#39;);
            });
        });
    }
    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) !== &#39;string&#39;) {
        common.throwError(&#39;Navbar error:事件名配置出错,请参考API文档.&#39;);
    }
    var lIndex = events.indexOf(&#39;(&#39;);
    var eventName = events.substr(0, lIndex);
    var filter = events.substring(lIndex + 1, events.indexOf(&#39;)&#39;));
    if (eventName === &#39;click&#39;) {
        if (_con.attr(&#39;lay-filter&#39;) !== undefined) {
            _con.children(&#39;ul&#39;).find(&#39;li&#39;).each(function () {
                var $this = $(this);
                if ($this.find(&#39;dl&#39;).length &gt; 0) {
                    var $dd = $this.find(&#39;dd&#39;).each(function () {
                        $(this).on(&#39;click&#39;, function () {
                            var $a = $(this).children(&#39;a&#39;);
                            var href = $a.data(&#39;url&#39;);
                            var icon = $a.children(&#39;i:first&#39;).data(&#39;icon&#39;);
                            var title = $a.children(&#39;cite&#39;).text();
                            var data = {
                                elem: $a,
                                field: {
                                    href: href,
                                    icon: icon,
                                    title: title
                                }
                            }
                            callback(data);
                        });
                    });
                } else {
                    $this.on(&#39;click&#39;, function () {
                        var $a = $this.children(&#39;a&#39;);
                        var href = $a.data(&#39;url&#39;);
                        var icon = $a.children(&#39;i:first&#39;).data(&#39;icon&#39;);
                        var title = $a.children(&#39;cite&#39;).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 = &#39;&lt;ul class=&#34;layui-nav layui-nav-tree beg-navbar&#34;&gt;&#39;;
    for (var i = 0; i &lt; data.length; i++) {
        if (data[i].spread) {
            ulHtml += &#39;&lt;li class=&#34;layui-nav-item layui-nav-itemed&#34;&gt;&#39;;
        } else {
            ulHtml += &#39;&lt;li class=&#34;layui-nav-item&#34;&gt;&#39;;
        }

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

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

    return ulHtml;
}

var navbar = new Navbar();

exports(&#39;navbar&#39;, 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(&#39;common&#39;, 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&lt;Long&gt; roleIdList = bsTenantRoleMapper.selectRoleIdListByTenantId(userId);
    Set&lt;String&gt; urlSet = new HashSet&lt;String&gt;();
    //遍历全部角色
    for (Long roleId : roleIdList) {
        //根据角色获取当前资源
        List&lt;Map&lt;String, String&gt;&gt; resourceList = bsRoleMapper.selectResourceListByRoleId(roleId);
        for (Map&lt;String, String&gt; map : resourceList) {
            urlSet.add(map.get(&#34;id&#34;));
        }
    }
    //根据id查询菜单
    List&lt;BsResource&gt; bsResources = bsResourceMapper.selectBatchIds(urlSet);
    return bsResources;
}</code></pre></div></div><blockquote><p> 实体类

代码语言: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&lt;BsResource&gt;());
               }
               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/头像: 头像地址