项目做完了,可以有时间写写博客了。 今天给大家讲解一下layui的三级动态加载菜单含后端代码。
我是最近刚学的layui,非常感谢贤心大神。开发出这么牛逼ui的框架。
声明:KingYiFan前段是渣渣,本次讲解用的前端js并非KingYiFan亲自封装只是稍作修改,非常感谢封装三级菜单CSDN的大神:yufengaotian
下图就是三级菜单效果。为什么要讲这个呢。
yufenggaotian大神也在博客中写道这个问题我就借用一下:
(图来着yufenggaotianCSDN博客)
前端页面:
//直接上代码吧 <!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部分可以不做修改
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);
});
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 所以代码量很少
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;
}
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;
}</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<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/头像: 头像地址