菜单配置在数据库里,第一次打开index.html,通过ajax请求得到后台返回的html菜单。
通过角色控制菜单展示,读取数据库信息,返回role-MenuList映射关系,后期可根据当前用户登录的角色,来控制菜单。
涉及技术:
adminlte渲染+spring-security请求过滤+springmvc请求+mybatis
效果图:
drop table menu;
create table menu(
menu_id number(10),
menu_name varchar2(256),
paerent_id number(10),
is_leaf char,
target_url varchar2(256)
);
drop table role_menu;
create table role_menu(
menu_id number(10),
role_id number(10));
create table ROLE
(
ID NUMBER(10),
ROLE_NAME VARCHAR2(32)
);
delete from menu 1=2;
insert into menu(menu_id,menu_name,paerent_id,is_leaf,target_url)
values(1000,'菜单1',0,0,'#');
insert into menu(menu_id,menu_name,paerent_id,is_leaf,target_url)
values(1001,'子菜单1',1000,1,'/test.html');
insert into menu(menu_id,menu_name,paerent_id,is_leaf,target_url)
values(1002,'子菜单2',1000,1,'#');
insert into menu(menu_id,menu_name,paerent_id,is_leaf,target_url)
values(1003,'菜单1-3',1000,1,'#');
insert into menu(menu_id,menu_name,paerent_id,is_leaf,target_url)
values(1100,'菜单2',0,0,'#');
insert into menu(menu_id,menu_name,paerent_id,is_leaf,target_url)
values(1101,'菜单2-1',1100,1,'#');
delete from role_menu 1=2;
insert into role_menu(menu_id,role_id)
values(1000,0);
insert into role_menu(menu_id,role_id)
values(1001,0);
insert into role_menu(menu_id,role_id)
values(1002,0);
insert into role_menu(menu_id,role_id)
values(1003,0);
insert into role_menu(menu_id,role_id)
values(1100,0);
insert into role_menu(menu_id,role_id)
values(1101,0);
delete from role 1=2;
insert into role(id,role_name)
values(0,'admin');
insert into role(id,role_name)
values(10001,'ROLE_USER');
菜单树持久化(数据库)
RoleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.dao.IRoleDao">
<resultMap id="user_role_permission_map" type="com.demo.domain.Role">
<id column="id" property="id"/>
<result column="role_name" property="roleName"/>
<result column="roleDesc" property="roleDesc"/>
</resultMap>
<select id="findRolesByUserId" resultMap="user_role_permission_map" parameterType="java.lang.String">
select * from role where id in (select role_id from users_role where user_id = #{uid})
</select>
<!-- 一对多 -->
<select id="selectRoleMenu" resultMap="roleMenuMap" parameterType="int">
select * from role a,role_menu b,menu c where a.id = b.role_id and b.menu_id = c.menu_id order by c.menu_id
</select>
<resultMap type="com.demo.domain.po.RoleMenu" id="roleMenuMap">
<id property="id" column="id"/>
<!-- <association property="menuId" column="menu_id" javaType="com.demo.domain.Menu">
<id property="menuId" column="menu_id"/>
<result property="name" column="t_name"/>
</association>-->
<!-- ofType指定students集合中的对象类型 -->
<collection property="menuList" ofType="com.demo.domain.Menu">
<id property="menuId" column="menu_id"/>
<result property="menuId" column="menu_id"/>
<result property="menuName" column="menu_name"/>
<result property="paerentId" column="paerent_id"/>
<result property="isLeaf" column="is_leaf"/>
<result property="targetUrl" column="target_url"/>
</collection>
</resultMap>
</mapper>
po
public class RoleMenu {
private Integer id;
private List<Menu> menuList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Menu> getMenuList() {
return menuList;
}
public void setMenuList(List<Menu> menuList) {
this.menuList = menuList;
}
}
menu
package com.demo.domain;
public class Menu {
private Long menuId;
private String menuName;
private Long paerentId;
private Integer isLeaf;
private String targetUrl;
public String getTargetUrl() {
return targetUrl;
}
public void setTargetUrl(String targetUrl) {
this.targetUrl = targetUrl;
}
public Long getMenuId() {
return menuId;
}
public void setMenuId(Long menuId) {
this.menuId = menuId;
}
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public Long getPaerentId() {
return paerentId;
}
public void setPaerentId(Long paerentId) {
this.paerentId = paerentId;
}
public Integer getIsLeaf() {
return isLeaf;
}
public void setIsLeaf(Integer isLeaf) {
this.isLeaf = isLeaf;
}
}
生成树:GenTree.java
package com.demo.common;
import com.demo.domain.Menu;
import com.demo.domain.po.RoleMenu;
import java.util.List;
public class GenTree {
public String generateTreeHtml(List<RoleMenu> roleMenus) {
List<Menu> menus = null;
StringBuilder sb = new StringBuilder();
for (RoleMenu roleMenu : roleMenus) {
if (0 == roleMenu.getId()) {
menus = roleMenu.getMenuList();
}
}
return tree(menus);
}
public String tree(List<Menu> menus) {
StringBuilder sb = new StringBuilder();
int leafFlag = 0;
int cnt = 0;
for (Menu menu : menus) {
if (menu.getIsLeaf() == 0) {
if (cnt == 0) {
sb.append("<li class=\"nav-item has-treeview\"> \n");
} else {
sb.append("</ul></li>");
sb.append("<li class=\"nav-item has-treeview\"> \n");
}
sb.append("<a href=\"#\" class=\"nav-link\"> <i class=\"nav-icon fas fa-tachometer-alt\"></i> <p>");
sb.append(menu.getMenuName());
sb.append("<i class=\"right fas fa-angle-left\"></i></p></a> ");
leafFlag = 0;
} else {
if (leafFlag == 0) {
sb.append("<ul class=\"nav nav-treeview\">");
}
sb.append("<li class=\"nav-item\"> <a href=\""+menu.getTargetUrl()+"\" target=\"menuFrame\" class=\"nav-link\"> <i class=\"far fa-circle nav-icon\"></i> <p>");
sb.append(menu.getMenuName());
sb.append("</p> </a></li>\n");
if (cnt == menus.size() - 1) {
sb.append("</ul></li>");
}
leafFlag = 1;
}
cnt++;
}
System.out.println(sb.toString());
return sb.toString();
}
}
service
public String queryAllRoleMenu() {
List<RoleMenu> roleMenus = roleDao.selectRoleMenu();
return new GenTree().generateTreeHtml(roleMenus);
}
controller
@RequestMapping(value = "/getTree", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
public String refleshTree() {
return myService.queryAllRoleMenu();
}
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 3 | Legacy User Menu</title>
<!-- Tell the browser to be responsive to screen width -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Font Awesome -->
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<!-- overlayScrollbars -->
<link rel="stylesheet" href="dist/css/adminlte.min.css">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
</head>
<body class="hold-transition sidebar-mini">
<!-- Site wrapper -->
<div class="wrapper">
<!-- Navbar -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Left navbar links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="/test.html" class="nav-link">Home</a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="#" class="nav-link">菜单1121</a>
</li>
</ul>
<!-- SEARCH FORM -->
<form class="form-inline ml-3">
<div class="input-group input-group-sm">
<input class="form-control form-control-navbar" type="search" placeholder="Search" aria-label="Search">
<div class="input-group-append">
<button class="btn btn-navbar" type="submit">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</form>
<!-- Right navbar links -->
<ul class="navbar-nav ml-auto">
<!-- Messages Dropdown Menu -->
<li class="nav-item dropdown user-menu">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">
<img src="dist/img/user2-160x160.jpg" class="user-image img-circle elevation-2" alt="User Image">
<span class="d-none d-md-inline">张三</span>
</a>
<ul class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
<!-- User image -->
<li class="user-header bg-primary">
<img src="dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image">
<p>
张三 - 工程师
<small>Member since Nov. 2012</small>
</p>
</li>
<!-- Menu Body -->
<li class="user-body">
<div class="row">
<div class="col-4 text-center">
<a href="#">Followers</a>
</div>
<div class="col-4 text-center">
<a href="#">Sales</a>
</div>
<div class="col-4 text-center">
<a href="#">Friends</a>
</div>
</div>
<!-- /.row -->
</li>
<!-- Menu Footer-->
<li class="user-footer">
<a href="#" class="btn btn-default btn-flat">Profile</a>
<a href="/logout" class="btn btn-default btn-flat float-right">退出</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" data-widget="control-sidebar" data-slide="true" href="#" role="button">
<i class="fas fa-th-large"></i>
</a>
</li>
</ul>
</nav>
<!-- /.navbar -->
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a href="/fail.html" target="menuFrame" class="brand-link">
<img src="dist/img/AdminLTELogo.png"
alt="AdminLTE Logo"
class="brand-image img-circle elevation-3"
style="opacity: .8">
<span class="brand-text font-weight-light">后台管理系统</span>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar Menu -->
<nav class="mt-2">
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu"
data-accordion="false" id="myHtml" name="myHtml">
<!-- Add icons to the links using the .nav-icon class
with font-awesome or any other icon font library -->
</ul>
</nav>
<!-- /.sidebar-menu -->
</div>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>Legacy User Menu</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Legacy User Menu</li>
</ol>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<!-- Default box -->
<!-- <div class="card">
<div class="card-header">
<h3 class="card-title">Title</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse" data-toggle="tooltip"
title="Collapse">
<i class="fas fa-minus"></i></button>
<button type="button" class="btn btn-tool" data-card-widget="remove" data-toggle="tooltip"
title="Remove">
<i class="fas fa-times"></i></button>
</div>
</div>
<div class="card-body">
Start creating your amazing application!d
</div>
<!– /.card-body –>
<div class="card-footer">
Footer
</div>
<!– /.card-footer–>
</div>-->
<!-- /.card -->
<iframe id="menuFrame" name="menuFrame" src="fail.html" style="overflow:visible;" scrolling="yes" frameborder="no" height="100%" width="100%"></iframe>
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<footer class="main-footer">
<div class="float-right d-none d-sm-block">
<b>Version</b> 3.0.4
</div>
<strong>Copyright © 2014-2019 <a href="http://adminlte.io">AdminLTE.io</a>.</strong> All rights
reserved.
</footer>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->
</div>
<!-- ./wrapper -->
<!-- jQuery -->
<script src="plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- AdminLTE App -->
<script src="dist/js/adminlte.min.js"></script>
<!-- AdminLTE for demo purposes -->
<script src="dist/js/demo.js"></script>
<script src="plugins/vue.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$.ajax({
type: "post",
url: "/rest/getTree",
async: true,
success: function (data) {
console.log(data);
$("#myHtml").append(data);
}
});
});
</script>
</body>
</html>