目录
一、用户表增加角色
user增加role字段
role表增加唯一标示flag
二、前端增加角色信息
user.vue dialog 增加字段 列表增加role
三、服务端增加角色代码
1、UserDto增加角色和菜单属性
2、定义角色枚举类型
public enum RoleEnum {
ROLE_ADMIN,ROLE_USER;
}
3、查询所有菜单
public List<Menu> findMenus(String name) {
QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name",name);
List<Menu> list = list(queryWrapper);
//找pid=null的一级菜单
List<Menu> parentNodes = list.stream().filter(menu -> menu.getPid()==null).collect(Collectors.toList());
//找出一级菜单的子菜单
for(Menu menu:parentNodes){
menu.setChildren(list.stream().filter(m ->menu.getId().equals(m.getPid())).collect(Collectors.toList()));
}
return parentNodes;
}
4、UserServiceImpl 新增获取当前角色的菜单列表
/**
* 获取当前角色的菜单列表
* @param flag 角色标签
* @return
*/
private List<Menu> getRoleMenus(String flag){
Integer roleId = roleMapper.selectByFlag(flag);
//当前角色的所有菜单
List<Integer> menuIds = roleMenuMapper.selectByRoleId(roleId);
//筛选完成之后的list
List<Menu> roleMenus = new ArrayList<>();
// 查询所有菜单
List<Menu> menus = iMenuService.findMenus("");
//筛选当前用户菜单
for (Menu menu: menus){
if (menuIds.contains(menu.getId())){
roleMenus.add(menu);
}
List<Menu> children = menu.getChildren();
//移除children里面不在menuIds集合中的元素
children.removeIf(child->!menuIds.contains(child.getId()));
};
return roleMenus;
}
5、UserController.java
public UserDto login(UserDto userDto) {
User one = getUserInfo(userDto);
if (one != null) {
BeanUtil.copyProperties(one, userDto, true);
String token = TokenUtils.genToken(one.getId().toString(),one.getPassword());
userDto.setToken(token);
String flag = one.getRole();
//获取当前角色的菜单
List<Menu> roleMenus = getRoleMenus(flag);
userDto.setMenus(roleMenus);
return userDto;
} else {
throw new ServiceException(Constants.CODE_600, "用户名或密码错误"); //自定义异常
}
}
四、动态菜单
1、Login.vue 登录存储menus
login() {
this.$refs['userform'].validate((valid) => {
if (valid) {
this.request.post("user/login", this.user).then(res => {
if (res.code === '200') {
localStorage.setItem("user",JSON.stringify(res.data))
localStorage.setItem("menus",JSON.stringify(res.data.menus))
this.$router.push("/")
this.$message.success("登录成功")
} else {
this.$message.error(res.msg);
}
})
}
});
},
2、Aside.vue 动态菜单
<div style="height: 60px;line-height: 60px;text-align: center;">
<img src="../assets/logo.png" alt="" style="width: 20px;position: relative; top: 5px;margin-right: 5px;">
<b style="color: white;" v-show="logoTextShow">后台管理系统</b>
</div>
<div v-for="item in menus" :key="item.id">
<div v-if="item.path">
<el-menu-item :index="item.path">
<i :class="item.icon"></i>
<span slot="title">{{item.name}}</span>
</el-menu-item>
</div>
<div v-else>
<el-submenu :index="item.id+ ''">
<template slot="title">
<i :class="item.icon"></i>
<span slot="title">{{item.name}}</span>
</template>
<div v-for="subItem in item.children" :key="subItem.id">
<el-menu-item :index="subItem.path">
<i :class="subItem.icon"></i>
<span slot="title">{{subItem.name}}</span>
</el-menu-item>
</div>
</el-submenu>
</div>
</div>
data(){
return{
menus: localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")): []
}
}
3、菜单默认展开
data(){
return{
menus: localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")): [],
opens:localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")).map(v=>v.id+''): []
}
}
五、动态路由
如何将后台返回的menus转化为路由,
menu表添加页面路径字段
Menu.java 添加PagePath
Menu.vue
表格列表新增页面路径字段
<el-table-column prop="pagePath" label="页面路径" align="center">
</el-table-column>
添加对话框添加页面路径字段
<el-form-item label="页面路径">
<el-input v-model="form.pagePath" autocomplete="off"></el-input>
</el-form-item>
router.vue将固定路由改造成动态路由
export const setRoutes = ()=>{
const storeMenus = localStorage.getItem("menus")
if(storeMenus){
const manageRoute = {path: '/',name: 'Manage',component: () => import('../views/Manage.vue'),redirect: '/home',children:[]}
const menus = JSON.parse(storeMenus)
menus.forEach(item => {
let itemMenu = {path:item.path.replace("/",""), name: item.name ,component: ()=>import ('../views/'+item.pagePath+'.vue')}
manageRoute.children.push(itemMenu)
});
//拼装动态路由
router.addRoute(manageRoute)
}
}
Login.vue 使用设置当前用户路由
export const setRoutes = ()=>{
const storeMenus = localStorage.getItem("menus")
if(storeMenus){
const manageRoute = {path: '/',name: 'Manage',component: () => import('../views/Manage.vue'),redirect: '/home',children:[]}
const menus = JSON.parse(storeMenus)
menus.forEach(item => {
if (item.path) {//当且仅当当前path不为空时候才去设置路由
let itemMenu = {path:item.path.replace("/",""), name: item.name ,component: ()=>import ('../views/'+item.pagePath+'.vue')}
manageRoute.children.push(itemMenu)
}else if(item.children.length){
item.children.forEach(item=>{
if (item.path) {
let itemMenu = {path:item.path.replace("/",""), name: item.name ,component: ()=>import ('../views/'+item.pagePath+'.vue')}
manageRoute.children.push(itemMenu)
}
})
}
});
//拼装动态路由
router.addRoute(manageRoute)
}
}
错误路径刷新,后在正确路径刷新页面报空白
export const setRoutes = ()=>{
const storeMenus = localStorage.getItem("menus")
if(storeMenus){
const manageRoute = {path: '/',name: 'Manage',component: () => import('../views/Manage.vue'),redirect: '/home',children:[]}
const menus = JSON.parse(storeMenus)
menus.forEach(item => {
if (item.path) {//当且仅当当前path不为空时候才去设置路由
let itemMenu = {path:item.path.replace("/",""), name: item.name ,component: ()=>import ('../views/'+item.pagePath+'.vue')}
manageRoute.children.push(itemMenu)
}else if(item.children.length){
item.children.forEach(item=>{
if (item.path) {
let itemMenu = {path:item.path.replace("/",""), name: item.name ,component: ()=>import ('../views/'+item.pagePath+'.vue')}
manageRoute.children.push(itemMenu)
}
})
}
})
//获取当前路由数组名称
const currentRoutesNames = router.getRoutes().map(v=>v.name)
if(!currentRoutesNames.includes('Manage')){
//拼装动态路由
router.addRoute(manageRoute)
}
// router.addRoute(manageRoute)
}
}
setRoutes()
六、404页面
<template>
<div style="overflow: hidden; height: 100vh;">
<img src="../assets/404.png" alt="" style="width: 100%;height:100%;">
</div>
</template>
<script>
export default {
name: "Not Found",
}
</script>
<style scoped>
</style>
{
path: '*',
name: '404',
component: () => import( '../views/404.vue')
},
七、菜单部分选中
需要将父级菜单插入数据库
RoleServiceImpl.java
public void setRoleMenu(Integer roleId, List<Integer> menuIds){
//删除当前所有绑定关系
roleMenuMapper.deleteByRoleId(roleId);
//再绑定
for (Integer menuId : menuIds){
Menu menu= iMenuService.getById(menuId);
if (menu.getPid()!=null &&!menuIds.contains(menu.getPid())){//二级菜单,并且传过来的menuID数组里面没有它的父级ID
//那么得补上这个父级ID
RoleMenu roleMenu = new RoleMenu();
roleMenu.setRoleId(roleId);
roleMenu.setMenuId(menu.getPid());
roleMenuMapper.insert(roleMenu);
}
RoleMenu roleMenu = new RoleMenu();
roleMenu.setRoleId(roleId);
roleMenu.setMenuId(menuId);
roleMenuMapper.insert(roleMenu);
}
store.js :登录
logOut(){
localStorage.removeItem("user")
localStorage.removeItem("menus")
router.push("/login")
}
Role.vue :管理员操作需要重新登录
saveRoleMenu(){
this.request.post("/role/roleMenu/"+this.roleId,this.$refs.tree.getCheckedKeys()).then(res=>{
console.log(res)
if (res.code === '200') {
this.$message.success("绑定成功")
this.menuDialogVis = false
//操作管理员要重新登录
if(this.roleFlag ==='ROLE_ADMIN'){
this.$store.commit("logOut")
}
}else{
this.$message.error(res.msg)
}
})
}
父菜单被勾选,子菜单部分勾选时,分配菜单窗口显示全部勾选问题处理
selectMenu(role) {
this.menuDialogVis = true
this.roleId = role.id
this.roleFlag = role.roleFlag
//请求菜单数据
this.request.get("/menu/", {}).then(res => {
this.menuData = res.data
//把后台返回菜单数据处理成id数组
this.expends = this.menuData.map(v=>v.id)})
//请求角色菜单
this.request.get("/role/roleMenu/"+ this.roleId).then(res => {
this.checks = res.data
this.request.get("/menu/ids").then(r=>{
const ids = r.data
ids.forEach(id => {
if (!this.checks.includes(id)) {
this.$refs.tree.setChecked(id,false)
}
})
})
this.menuDialogVis = true
})
},
MenuController.java
@GetMapping("/ids")
public Result findAllIds() {
return Result.success(menuService.list().stream().map(Menu::getId));
}