前言
有时我们在浏览网页或者某些APP时,VIP的权限会多一些,那么它本质上,时对当前用户的账户id做了role区分,根据role在页面上动态展示不同的模块,今天我们说说前端使用SlideBar配合SpringMVC+Mybatis实现这个功能
错误认识
直觉上可能觉得三张表就可以,比如
不过我们想一想,一个菜单项是不是对应多个权限,而一个权限对应多个菜单项,多对多的关系嘛,在中间需要加一个桥表,而user也是一样,user并不一定是非黑及白,比如一个人它既是国家主席同时也是人民群众,所以说一个user对应多个role,而一个role肯定多个user对应,也是多对多的关系,所以也需要添加一个桥表
它们之间的模式👇
所以我们正确的表结构是这样的👇
ok我把建表的sql语句写一下,顺便添几条数据,省大家的功夫
create table users(id int primary key auto_increment,uname varchar(32),pwd varchar(32));
insert into users values(1,'zx',md5('abc')),(2,'ls',md5('abc'));
create table roles(id int primary key auto_increment,name varchar(32));
insert into roles
values (1,'SVip'),(2,'Vip'),(3,'普通用户');
create table user_role (id int primary key auto_increment,userid int,roleid int,foreign key (userid) references users(id),foreign key (roleid) references roles(id));
insert into user_role (id,userid,roleid) values (1,1,1),(2,1,2),(3,1,3);
create table menus(
id int primary key auto_increment,
text varchar(50) comment '菜单名字',
href varchar(100) comment '路由',
pid int comment '父id',
sort int comment '排序字段'
);
insert into menus values(1,'书记管理系统','javascript:void(0)',-1,1),
(2,'其它系统','javascript:void(1)',-1,2),
(3,'书籍模块','javascript:void(0)',1,3),
(4,'统计模块','javascript:void(0)',1,4),
(5,'统计模块','javascript:void(0)',1,5),
(6,'书籍列表','/books',3,6),
(7,'出版社信息','/pubs',4,7),
(8,'报表','/report',5,8),
(9,'权限配置','/pre',2,9);
create table role_menu(id int primary key ,roleid int,menuid int,
foreign key (roleid) references roles(id),
foreign key (menuid) references menus(id)
);
insert into role_menu values(1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,3,1),(8,3,2),(9,2,1),(10,2,2),(11,2,3),(12,2,4);
ok我们最重要的表和数据都有了,这里我的意思是roleid为1为超级管理员(VIP) 能看到我们所有的菜单项,而其它roleid只开放部分菜单项
后台
Controller
Spring的知识点我就不多说了
顺着这个流程走,👉controller👇
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private SetResponse sr;
@Autowired
private UserService service;
@RequestMapping("/queryMenuByUserId")
@ResponseBody
public ETResponse queryMenuByUserID(int userID){
List<Menus> menus =service.queryMenuByUserId(userID);//调用service
//这个依赖注入是用来组装ETResponse的
return sr.setResponse(tops);
}
}
其中sr这个依赖注入是用来组装ETResponse的,我贴一下
@Component
@Data@AllArgsConstructor@NoArgsConstructor
public class SetResponse {
@Autowired
private ETResponse etResponse;
public ETResponse setResponse(Object obj){
etResponse.setCode(ETEnums.SUCCESS.getCode());//这里用的枚举
etResponse.setMsg(ETEnums.SUCCESS.getMsg());
etResponse.setData(obj);
return etResponse;
}
}
接着牵引出ETResonse,也是一个依赖注入,我贴一下
@Component
@Data@NoArgsConstructor@AllArgsConstructor
public class ETResponse {
private int code;
private String msg;
private Object data;
}
这个ETnum的枚举在这里👇
public enum ETEnums {
SUCCESS(200,"success"),
ERROR(444,"error"),
LOGIN_SUCCESS(666,"login_success"),
LOGIN_ERROR(445,"login_error");
private int code;
private String msg;
private ETEnums(int code,String msg){
this.code =code;
this.msg =msg;
}
public String getMsg(){
return msg;
}
public int getCode(){
return code;
}
}
ok Controller这里都清楚了往下走,去Service
Service+Mapper+.xml
往下就是各种甩锅,Service–>Mapper–>.xml<–>MySql,首先是Service,刚才Controller调用的Service嘛 我们接上
@Service
public class UserService {
@Autowired
private UserMapper dao;
public List<Menus> queryMenuByUserId(int userID) {
return dao.queryMenuByUserID(userID);//看到了吧 Service调用UserMapper
}
}
看到了吧 Service调用UserMapper,那么UserMapper👇
public interface UserMapper {
List<Menus> queryMenuByUserID(int userID);
}
它映射的是一个.xml文件,这是我的UserMapper.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接口的路径-->
<mapper namespace="com.etoak.book.user.mapper.UserMapper">
<select id="queryMenuByUserID" resultType="menus">
select * from menus m where
m.id in (select menuid from role_menu rm where rm.roleid in (select ur.roleid from user_role ur where ur.userid=#{userID}))
</select>
</mapper>
测试
ok 后台基本就完事了,我们用postMan发个请求查一下
哎,是可以回来数据,但是我们想要的是一个由包含关系的菜单项,不然会给前端造成很大的麻烦,在这里我们得想办法协调一下工作,把菜单的子父关系表现出来,所以我们得改进一下上面的代码
后台(改)
如果在一个菜单项里有子菜单项,那么我们给menus实体类一个属性:List 存放它的子菜单
@Data@AllArgsConstructor@NoArgsConstructor
public class Menus {
private int id;
private String text;
private String href;
private int pid;
private int sort;
private List<Menus> nodes;
}
那么我们需要改进一下Controller的代码
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private SetResponse sr;
@Autowired
private UserService service;
@RequestMapping("/queryMenuByUserId")
@ResponseBody
public ETResponse queryMenuByUserID(int userID){
List<Menus> menus =service.queryMenuByUserId(userID);
List<Menus> tops =new ArrayList<>();//找一下顶部菜单
for (Menus menu : menus) {
if(menu.getPid()==-1){
tops.add(menu);
}
}
for (Menus top : tops) {
SetNodes.setNodes(top,menus);//装载子菜单的方法
}
return sr.setResponse(tops);
}
}
封装了一个小工具类 SetNode
public class SetNodes {
public static void setNodes(Menus m,List<Menus> menus){
List<Menus> children =new ArrayList<>();//存子菜单的集合
for (Menus menu : menus) {
if(menu.getPid()==m.getId()){
children.add(menu);//装载
}
}
m.setNodes(children);//设置当前菜单的子菜单属性
if(children.size()>0){//直到没有子菜单
for (Menus child : children) {
SetNodes.setNodes(child,menus);//递归
}
}
}
}
这样我们的后台就没毛病了,再测试一下
测试(改)
哎!完美,是我们要的嵌套效果,这样我们就根据pid和id把菜单串起来了
前台
使用Vue,这里我们用一个基于elementUI的小东西,SlideBar
一个小资源,下载解压拖进views就好
这里我下载解压好了,直接拖到views中,它其实就是一个组件而已
注册组件并发送请求:
<template>
<el-container>
<el-header>
<div id="logo">
<img src="../assets/logo.png" /> <span>学生管理系统</span>
</div>
<el-dropdown>
<el-button type="warning">
更多菜单<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-container>
<el-aside width="200px">
<!--SlideBar在这里使用 绑定menuList-->
<side-bar :menuList="menuList">
</side-bar>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import SideBar from './book资料/SideBar/SideBar.vue';
export default {
data(){
return{
menuList:[]
}
},
components:{SideBar},
methods: {
queryMenu(){
this.$http
.post("/user/queryMenuByUserId", this.$qs.stringify({userID:1}))
.then((response) => {
this.menuList =response.data.data
});
}
},
created(){
this.queryMenu()
}
};
</script>
<style lang="less" scoped>
.el-container {
margin: 0;
padding: 0;
height: 1000px;
.el-header {
background-color: rgb(84, 92, 100);
color: #333;
text-align: center;
line-height: 60px;
height: 200px;
.el-dropdown {
float: right;
}
#logo {
float: left;
height: 75px;
width: 250px;
display: flex;
align-items: center;
img {
width: 30%;
height: 100%;
}
span {
width: 70%;
height: 100%;
color: #d3dce6;
font-weight: 900;
}
}
}
.el-aside {
background-color: #d3dce6;
color: #333;
text-align: center;
line-height: 200px;
height: 800px;
background-color: rgb(84, 92, 100);
}
.el-main {
background-color: #e9eef3;
color: #333;
text-align: center;
line-height: 160px;
height: 800px;
}
}
</style>
最终效果
效果👇userID为1是Svip,为2是普通vip,为3为普通用户,所以可以看出动态显示模块的效果
总结
那么根据用户的权限,展示不同的模块,我们就完成了,有需求可以加上登录和定时任务这些需求,好了,今天就说这么多吧,原创不易,喜欢记得点赞+关注哦~