Springboot + vue前后端分离后台管理系统(十二) -- 动态角色菜单
前言后台管理系统是基于RBAC设计的,也就是说不同的角色应该拥有不同的资源访问权限,这篇就来实现这个功能实现方式一vue-admin-template 只提供了基础的vue后台管理框架,权限管理模块没有引入。vue-element-admin 相对完整的组件demo和权限模块。它的动态路由配置如下:前端配置完整的路由菜单请求后端api返回 用户拥有的菜单在前端过滤渲染呈现用户的菜单方式二由后端统一
前言
后台管理系统是基于RBAC设计的,也就是说不同的角色应该拥有不同的资源访问权限,这篇就来实现这个功能
实现
方式一
vue-admin-template 只提供了基础的vue后台管理框架,权限管理模块没有引入。
vue-element-admin 相对完整的组件demo和权限模块。它的动态路由配置如下:
- 前端配置完整的路由菜单
- 请求后端api返回 用户拥有的菜单
- 在前端过滤渲染呈现用户的菜单
方式二
由后端统一配置菜单,直接返回给前端渲染
优缺点
方式一:对于前后端分离多人协作开发比较友好,前端只需要提供路由菜单的编码,由后端配置给对应角色用户。前端人员不受后端开发的约束。
方式二:动态路由菜单直接由后端配置并返回,相对安全。但是前端人员的路由文件的存放和命名就和后端人员耦合度较高。
这个demo尝试使用方式二的方式来实现动态路由菜单,喜欢方式一的可以直接研究vue-element-admin项目
系统管理功能
用户界面
用户管理:新增/编辑用户信息,并可以给用户设置一个或多个角色。
角色 界面
角色管理:新增/编辑角色信息,并可以给角色设置菜单权限。
菜单界面
菜单管理:新增/编辑、管理所有的基础菜单,不同角色可以指定不同的菜单。
核心代码
-
数据库菜单数据
-
接口返回给前端的树形结构数据
"menus": [{
"id": "5e9d2ef576424b61445388acc285da0f",
"pId": "0",
"weight": "10",
"name": "系统管理",
"path": "/sys-manage",
"component": "",
"code": "sys-manage",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "el-icon-s-operation",
"permission": "",
"updateTime": null,
"type": "0",
"children": [{
"id": "e308c7f841d56b791af44fd6a0b6745f",
"pId": "5e9d2ef576424b61445388acc285da0f",
"weight": "10",
"name": "用户管理",
"path": "/users",
"component": "sys/user/users",
"code": "users",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "el-icon-user-solid",
"permission": "",
"updateTime": null,
"type": "0"
}, {
"id": "72633ded69127f0974141bb5687e541d",
"pId": "5e9d2ef576424b61445388acc285da0f",
"weight": "20",
"name": "角色管理",
"path": "/roles",
"component": "sys/role/roles",
"code": "roles",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "el-icon-s-custom",
"permission": "",
"updateTime": null,
"type": "0"
}, {
"id": "5aadb65fed8eca1761bcabe8bb6b9cf9",
"pId": "5e9d2ef576424b61445388acc285da0f",
"weight": "30",
"name": "菜单管理",
"path": "/menus",
"component": "sys/menu/menus",
"code": "menus",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "el-icon-menu",
"permission": "",
"updateTime": null,
"type": "0"
}]
}, {
"id": "afc533ce3f5e8611efaa3888e11a9265",
"pId": "0",
"weight": "20",
"name": "开发者工具",
"path": "/develop",
"component": "",
"code": "develop",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "fa fa-desktop",
"permission": "",
"updateTime": null,
"type": "0",
"children": [{
"id": "00aa7dc71a0c0d0538300919f8f308e9",
"pId": "afc533ce3f5e8611efaa3888e11a9265",
"weight": "10",
"name": "测试1",
"path": "/test1",
"component": "",
"code": "111",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "fa fa-cutlery",
"permission": "",
"updateTime": null,
"type": "0"
}, {
"id": "512369a39292357cf9624fddc8e83ec8",
"pId": "afc533ce3f5e8611efaa3888e11a9265",
"weight": "10",
"name": "代码生成",
"path": "/gen",
"component": "",
"code": "gen",
"hidden": "0",
"createTime": null,
"forbidden": null,
"icon": "fa fa-code",
"permission": "",
"updateTime": null,
"type": "0"
}]
}]
- 新增一个store路由模块来清洗数据,把后端数据库表里的数据清洗成route支持的结构
import { asyncRoutes, constantRoutes } from '@/router'
/* Layout */
import Layout from '@/layout'
/**
* 处理拼装侧边栏菜单
* @param routes asyncRoutes
* @param menus
*/
export function filterAsyncRoutes(menus) {
const res = []
// 遍历一级菜单 需要component: Layout 布局
menus.forEach(menu => {
const tmp = {
path: menu.path,
name: menu.code,
component: Layout,
meta: {
title: menu.name,
icon: menu.icon
},
hidden: menu.hidden === '0' ? false : true
}
if (menu.children) {
tmp.children = initChildren(menu.children)
}
res.push(tmp)
})
return res
}
// 递归遍历子菜单
function initChildren(children) {
const rs = []
children.forEach(child => {
const temp = {
path: child.path,
name: child.code,
meta: {
title: child.name,
icon: child.icon
},
hidden: child.hidden === '0' ? false : true,
component: loadView(child.component)
}
if (child.children) {
temp.children = initChildren(child.children)
}
rs.push(temp)
})
return rs
}
/**
* 解决动态加载路由 报错:Cannot find module 'XXXX', 例如:Cannot find module '@/views/sys/role/roles'
* @param {*} view
*/
export const loadView = (view) => {
return (resolve) => require([`@/views/${view}`], resolve)
};
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, menus) {
return new Promise(resolve => {
let accessedRoutes
accessedRoutes = filterAsyncRoutes(menus)
//404 页面,放在最后 否则会报错。这是框架里说得 还真只能放最后。。。
accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
- 修改permission.js逻辑
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async (to, from, next) => {
// start progress bar
NProgress.start()
// 白名单直接跳转 如果是登陆路由,清空用户token
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
//验证是否有token, 有token跳转, 无token或token失效跳转登录页
const token = store.getters.token
if (token) {
const name = store.getters.name
if (name) {
next();
} else {
// 获取动态路由菜单
try {
const { menus } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('accessRoutes/generateRoutes', menus)
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
} else {
// await store.dispatch('user/resetToken')
Message.error('登陆失效,请重新登陆')
next(`/login?redirect=${to.path}`)
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
效果
-
配置角色菜单
-
配置用户角色
-
用不同角色用户进行登陆
user
admin
结尾
基础的代码太多,就没有贴出来,展示出来的都是功能的效果。
唠叨一下
拖延症犯了就不想写文章。。。
更多推荐
所有评论(0)