项目背景:物业管理后台,不同角色拥有不同权限
采用技术:Vue.js + Vuex + Element UI
实现 RBAC 权限管理需要后端接口支持,这里仅提供前端解决方案。
因代码篇幅较大,对代码进行了删减,文中 “...” 即为省略的一部分代码。
大致思路:
首先登录成功后,从后台拉取用户当前可显示的菜单和可用权限列表,分别将其存入 store 的 nav(菜单导航) 和 auth(用户可用权限) 中,在用户切换路由时,判断是否存在 auth ,如果不存在,则重新获取,判断当前访问地址 to.meta.alias 是否在用户可用权限列表中,如果不存在,则提示无权限,否则进入路由。
1. 路由与侧边菜单分离
侧边菜单相关代码 Main.vue
<template> <!-- ... --> <aside :class="collapsed"> <!--导航菜单--> <el-menu :default-active="$route.path" class="el-menu-vertical-aliyun" @open="handleopen" @close="handleclose" @select="handleselect" :collapse="collapsed" unique-opened router> <template v-for="(item,index) in nav"> <!-- 二级菜单 --> <el-submenu :index="index+''" v-if="item.children && item.children.length > 0"> <!-- 二级菜单顶级 --> <template slot="title"> <i :class="['icon',item.iconCls]"></i> <span slot="title">{{item.name}}</span> </template> <!-- 二级菜单下级 --> <el-menu-item-group> <!--<span slot="title">{{item.name}}</span>--> <!-- && child.url--> <template v-for="child in item.children"> <!--无三级菜单--> <el-menu-item :index="child.url" :key="child.url" v-if="!child.children"> {{child.name}} </el-menu-item> <!--有三级菜单--> <el-submenu :index="child.url" :key="child.url" v-if="child.children"> <span slot="title">{{child.name}}</span> <el-menu-item v-for="subChild in child.children" :index="subChild.url" :key="subChild.url"> {{subChild.name}} </el-menu-item> </el-submenu> </template> </el-menu-item-group> </el-submenu> <!-- 一级菜单 --> <el-menu-item v-if="!item.children" :index="item.url"> <i :class="['icon',item.iconCls]"></i> <span slot="title">{{item.name}}</span> </el-menu-item> </template> </el-menu> </aside> <!-- ... --> </template> <script> export default { // ... computed: { // 从 Vuex 中获取导航菜单 nav() { return this.$store.state.nav; } } // ... } </script>
2. 路由切换前进行鉴权
路由定义的部分代码,对每个路由添加了 meta 属性,用于鉴权。
这里 component 采用了异步引入的方式。
定义路由
// ... // 系统管理 { path: '/system', component: Main, name: '系统管理', redirect: '/system/organization', children: [{ path: '/system/organization', component: () => import ('@/views/System/Organization.vue'), name: '组织结构', // requiresAuth 用于确认此地址是否需要验证 // alias 用于获取后端返回rbac权限对应的前端路由地址和导航菜单图标 meta: {requiresAuth: true, alias: 'Pmsadmin/Oragnize/list'} }, { path: '/system/user', component: () => import ('@/views/System/User.vue'), name: '人员管理', redirect: '/system/user/index', children: [ { path: '/system/user/index', component: () => import ('@/views/System/UserList.vue'), name: '职员列表', meta: {requiresAuth: true, alias: 'Pmsadmin/Admin/list'} } ] }, { path: '/system/auth', component: () => import ('@/views/System/Auth.vue'), name: '角色管理', meta: {requiresAuth: true, alias: 'Pmsadmin/Role/list'} } ] } // ...
路由钩子 beforeEach
router.beforeEach((to, from, next) => { document.title = `${configs.title} - ${to.name}`; const {hasAuth, auth} = store.state.user; // 未拿到权限,则获取 if (!hasAuth) { store.dispatch('getUserAuth'); console.log('重新获取用户权限'); // next(); } // 如果未登录,跳转 if (window.localStorage.getItem('IS_LOGIN') === null && to.path !== '/login') { console.log('未登录状态'); next({ path: '/login', query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由 }) } else { // 需要鉴权的路由地址 console.log(to, auth.indexOf(to.meta.alias), auth); if (to.meta.requiresAuth) { if (auth.indexOf(to.meta.alias) > -1) { console.log('有权限进入'); next(); } else { if(auth.length > 0) { Message.error({ message: '当前用户权限不足,无法访问', showClose: true, }); } else { next(); } } } else { next(); } } });
在 Vuex 的 state 中,定义好 nav 对象
// 登录用户信息 const user = { name: '', // 用户名 avatar: '', // 用户头像 auth: [], // 用户权限 hasAuth: false // 是否已经加载用户权限 }; // 导航菜单 const nav = [];
通过 action 异步获取数据
// 获取用户权限 const getUserAuth = async ({commit}) => { const res = await http.post('YOUR_URL', {}); if (res === null) return; console.log('getUserAuth', res.param); commit('SET_USER_AUTH', res.param.auth); commit('SET_SIDE_NAV', res.param.nav); };
Vuex 中的 mutation 的相关代码
// 设置用户权限 const SET_USER_AUTH = (state, auth) => { state.user.auth = auth.concat('欢迎使用'); state.user.hasAuth = true; }; // 设置导航菜单 const SET_SIDE_NAV = (state, nav) => { // 导航菜单 let _nav = [{ name: '欢迎使用', url: "/main", iconCls: 'fa fa-bookmark' }]; // 权限菜单对应的路由地址 const route = { "系统管理": {iconCls: 'fa fa-archive', url: ''}, "Pmsadmin/Oragnize/list": {iconCls: '', url: '/system/organization'}, "Pmsadmin/Admin/list": {iconCls: '', url: '/system/user/index'}, "Pmsadmin/Role/list": {iconCls: '', url: '/system/auth'}, "Pmsadmin/Log/record": {iconCls: '', url: '/system/logs'}, "项目管理": {iconCls: 'fa fa-unlock-alt', url: ''}, "Pmsadmin/Project/list": {iconCls: '', url: '/project/list/index'}, "Pmsadmin/House/list": {iconCls: '', url: '/project/house'}, "Pmsadmin/Pack/list": {iconCls: '', url: '/project/pack'}, "广告位": {iconCls: 'fa fa-edit', url: ''}, "Pmsadmin/Place/list": {iconCls: '', url: '/adsplace/list'}, "投诉建议": {iconCls: 'fa fa-tasks', url: ''}, "Pmsadmin/Scategory/list": {iconCls: '', url: '/complain/type'}, "Pmsadmin/Complain/list": {iconCls: '', url: '/complain/list'}, "Pmsadmin/Suggest/list": {iconCls: '', url: '/complain/suggestion'}, "报事报修": {iconCls: 'fa fa-user', url: ''}, "Pmsadmin/Rcategory/list": {iconCls: '', url: '/rcategory/type'}, "Pmsadmin/Rcategory/info": {iconCls: '', url: '/rcategory/public'}, "Pmsadmin/Repair/list": {iconCls: '', url: '/rcategory/personal'}, "便民服务": {iconCls: 'fa fa-external-link', url: ''}, "Pmsadmin/Bcategory/list": {iconCls: '', url: '/bcategory/type'}, "Pmsadmin/Service/list": {iconCls: '', url: '/bcategory/list'}, "首座推荐": {iconCls: 'fa fa-file-text', url: ''}, "Pmsadmin/stcategory/list": {iconCls: '', url: '/stcategory/type'}, "Pmsadmin/Store/list": {iconCls: '', url: '/stcategory/list'}, "招商租赁": {iconCls: 'fa fa-leaf', url: ''}, "Pmsadmin/Bussiness/list": {iconCls: '', url: '/bussiness/list'}, "Pmsadmin/Company/list": {iconCls: '', url: '/bussiness/company'}, "Pmsadmin/Question/list": {iconCls: '', url: '/bussiness/question'}, "停车找车": {iconCls: 'fa fa-ra', url: ''}, "Pmsadmin/Cplace/list": {iconCls: '', url: '/cplace/cmanage'}, "Pmsadmin/Clist/list": {iconCls: '', url: '/cplace/clist'}, "Pmsadmin/Cquestion/list": {iconCls: '', url: '/cplace/cquestion'}, }; for (let key in nav) { let item = nav[key]; let _temp = {}; let subItems = []; // 二级菜单临时数组 if (item.children && item.children.length > 0) { // 二级菜单 item.children.forEach(subItem => { subItems.push(Object.assign({}, { name: subItem.name || '', url: route[subItem.url].url || '', iconCls: route[subItem.url].iconCls || '', })) }); // 一级菜单 _temp = Object.assign({}, { name: item.name || '', url: item.url || '', iconCls: route[item.name].iconCls || '', children: subItems.slice(0) }); _nav.push(_temp); } } state.nav = _nav; };
3. 后端接口返回内容
{ "status": 200, "info": "数据查询成功!", "param": { "nav": { "1": { "name": "系统管理", "url": "", "children": [ { "name": "组织结构", "url": "Pmsadmin/Oragnize/list" }, { "name": "人员管理", "url": "Pmsadmin/Admin/list" }, { "name": "角色管理", "url": "Pmsadmin/Role/list" }, { "name": "日志管理", "url": "Pmsadmin/Log/record" } ] }, "61": { "name": "广告位", "url": "", "children": [ { "name": "广告位列表", "url": "Pmsadmin/Place/list" } ] } }, "auth": [ "系统管理", "Pmsadmin/Oragnize/list", "Pmsadmin/Admin/list", "Pmsadmin/Role/list", "Pmsadmin/Log/record", "广告位", "Pmsadmin/Place/list" ] } }
存在的问题
- 新增 修改 删除 按钮还无法实现根据用户权限控制其显示
- 代码上还存在着不足,期待大神能够有更优的解决方案。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]