Vue后台开发实例教程(九)实现权限校验,vue动态菜单实现
本实例教程是.net core 系列实例开发教程-权限管理系统前端开发教程,使用PanJiaChen写的Vue后台模板。
本文源码下载地址:http://www.80cxy.com/Blog/ResourceView?arId=202403191532545995NAAqJh
系列教程地址:http://www.80cxy.com/Blog/ArticleView?arId=202403191517574161ay3s5V
本文实现根据用户权限显示相应权限的菜单及功能按钮。整个项目需要修改4个地方。
一、路由模块修改
将路由分为常量路由、异步路由、任意路由三个常量。用户最终显示路由使用异步路由跟接口返回用户角色进行计算,得出应该显示的路由。修改src/router//index.js文件,代码修改如下:
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) /* Layout */ import Layout from '@/layout' /** * Note: sub-menu only appear when route children.length >= 1 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html * * hidden: true if set true, item will not show in the sidebar(default is false) * alwaysShow: true if set true, will always show the root menu * if not set alwaysShow, when item has more than one children route, * it will becomes nested mode, otherwise not show the root menu * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb * name:'router-name' the name is used by <keep-alive> (must set!!!) * meta : { roles: ['admin','editor'] control the page roles (you can set multiple roles) title: 'title' the name show in sidebar and breadcrumb (recommend set) icon: 'svg-name'/'el-icon-x' the icon show in the sidebar breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) activeMenu: '/example/list' if set path, the sidebar will highlight the path you set } */ /** * constantRoutes * a base page that does not have permission requirements * all roles can be accessed */ //需要把项目中的路由进行拆分 //常量路由:不管什么角色都可以看见的路由 登录 404 首页 export const constantRoutes = [ { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/home', children: [ { path: 'home', name: 'Home', component: () => import('@/views/home/index'), meta: { title: '首页', icon: 'dashboard' } } ] }, ] //异步路由:不同的用户(角色),需要过滤筛选出的路由。 export const asyncRoutes = [ { name: 'Recruit', path: '/recruit', component: Layout, meta: { title: '招聘管理', icon: 'el-icon-goods' }, children: [ { name: 'RecruitBase', path: 'recruitbase', component: () => import('@/views/recruit/recruitBase'), meta: { title: '招聘管理', icon: 'dashboard' } }, { name: 'RecruitPosition', path: 'recruitposition', component: () => import('@/views/recruit/recruitPosition'), meta: { title: '职位管理', icon: 'dashboard' } }, { name: 'RecruitUser', path: 'recruituser', component: () => import('@/views/recruit/recruitUser'), meta: { title: '用户管理', icon: 'dashboard' } }, ] }, { name: 'System', path: '/system', component: Layout, meta: { title: '系统管理', icon: 'el-icon-goods' }, children: [ { name: 'User', path: 'user', component: () => import('@/views/system/user'), meta: { title: '用户管理', icon: 'dashboard' } }, { name: 'Menu', path: 'menu', component: () => import('@/views/system/menu'), meta: { title: '菜单管理', icon: 'dashboard' } }, { name: 'Role', path: 'role', component: () => import('@/views/system/role'), meta: { title: '角色管理', icon: 'dashboard' } }, { name: 'IndexAuth', path: 'role/auth/:id', component: () => import('@/views/system/role/indexAuth'), meta: { activeMenu: '/acl/role', title: '角色授权', }, hidden: true, }, ] }, ]; //任意路由:当路径出现错误的时候,重定向到404 export const anyRoutes = { path: '*', redirect: '/404', hidden: true };// 404 page must be placed at the end !!! const createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), routes: constantRoutes }) const router = createRouter() // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router } export default router
二、根据接口返回权限计算显示路由
修改用户登录模块,将接口返回权限数据与异步路由计算得出显示路由,修改src/store/modules/user.js,代码如下:
import { login, logout, getInfo } from '@/api/system' import { getToken, setToken, removeToken } from '@/utils/auth' import { anyRoutes, resetRouter, asyncRoutes, constantRoutes } from '@/router' import router from '@/router' const getDefaultState = () => { return { token: getToken(),//token name: '',//用户名 avatar: '',//用户头像 //服务器返回的菜单信息【根据不同的角色:返回的标记信息,数组里面的元素是字符串】 routes: [], //当前用户角色信息 roles: [], //按钮权限的信息 buttons: [], //对比之后[项目中已有的异步路由与服务器返回的标记的信息进行对比,最终需要展示的路由] resultAsyncRoutes: [], //用户最终需要展示的全部路由 resultAllRoutes: [], } } const state = getDefaultState() const mutations = { RESET_STATE: (state) => { Object.assign(state, getDefaultState()) }, SET_TOKEN: (state, token) => { state.token = token }, //存储用户信息 SET_USERINFO: (state, userInfo) => { //用户名 state.name = userInfo.name; //用户头像 state.avatar = userInfo.avatar; //菜单权限标记(路由的名称) state.routes = userInfo.routes; //按钮权限标记 state.buttons = userInfo.buttons; //角色信息 state.roles = userInfo.roles; }, SET_NAME: (state, name) => { state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }, //最终计算出来的路由 SET_RESULTASYNCROUTES: (state, asyncRoutes) => { state.resultAsyncRoutes = asyncRoutes; //计算出当前用户需要展示的所有路由 state.resultAllRoutes = constantRoutes.concat(state.resultAsyncRoutes, anyRoutes); //给路由器添加新的路由 router.addRoutes(state.resultAllRoutes); } } //定义一个函数:两个数组进行对比,对比出当前用户到底展示哪些异步路由 const computedAsyncRoutes = (asyncRoutes, routes) => { return asyncRoutes.filter(item => { //数组当中没有这个元素返回索引值-1 if (routes.indexOf(item.name) != -1) { //递归 if (item.children && item.children.length) { item.children = computedAsyncRoutes(item.children, routes) } return true; } }) } const actions = { //用户登录 async login({ commit }, userInfo) { const { username, password } = userInfo let result = await login({ username: username.trim(), password: password }) if (result.code == 200) { commit('SET_TOKEN', result.data.token) setToken(result.data.token) return 'ok' } else { return Promise.reject(new Error('faile')) } }, // 获取用户信息 async getInfo({ commit, state }) { let result = await getInfo(state.token) if (result.code == 200) { //返回数据包含:用户名name、用户头像avatar、routes[返回的标志:不同用户应该展示哪些菜单的标记]、roles(用户角色信息)、buttons[按钮的信息:按钮权限用的标记] const { data } = result commit('SET_USERINFO', data) commit('SET_RESULTASYNCROUTES', computedAsyncRoutes(asyncRoutes, data.routes)); //执行成功返回data数据 return 'ok' //return Promise.resolve(result.data) } else { return Promise.reject(new Error('faile')) } }, // 退出登录 async logout({ commit, state }) { let result = await logout(state.token) if (result.code == 200) { removeToken() // must remove token first resetRouter() commit('RESET_STATE') //执行成功返回data数据 return 'ok' //return Promise.resolve(result.data) } else { return Promise.reject(new Error('faile')) } }, // 移除token resetToken({ commit }) { return new Promise(resolve => { removeToken() // must remove token first commit('RESET_STATE') resolve() }) } } export default { namespaced: true, state, mutations, actions }
三、修改菜单组件的路由获取源改为计算的路由
修改src/layout/components/sidebar/index.vue组件
routes() { return this.$store.state.user.resultAllRoutes; },
四、返回页面刷新空白页面
修改src/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 import { getToken } from '@/utils/auth' // get token from cookie import getPageTitle from '@/utils/get-page-title' /* 路由守卫 */ NProgress.configure({ showSpinner: false }) // NProgress Configuration const whiteList = ['/login'] // no redirect whitelist //跳转路由前执行代码 router.beforeEach(async(to, from, next) => { // 开启进度条 NProgress.start() // 设置页面标题 document.title = getPageTitle(to.meta.title) // 判断用户是否已登录 const hasToken = getToken() //用户已经登录 if (hasToken) { //如何用户访问的是登录页面, if (to.path === '/login') { // 如果登录跳转到首页 next({ path: '/' }) NProgress.done() } else { //判断是否获取到用户信息,如果没有执行获取用户信息 const hasGetUserInfo = store.getters.name if (hasGetUserInfo) { next() } else { try { // 获取用户信息 await store.dispatch('user/getInfo') //{...to}解决页面刷新返回空页面问题 next({...to}) } 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 { //如果用户访问的是登录页面则直接跳转 if (whiteList.indexOf(to.path) !== -1) { //in the free login whitelist, go directly //在免费登录白名单中,直接进入 next() } //如果用户访问非登录页面,需要跳转到登录页面 else { // other pages that do not have permission to access are redirected to the login page. //其他没有访问权限的页面被重定向到登录页面。 next(`/login?redirect=${to.path}`) NProgress.done() } } }) router.afterEach(() => { // finish progress bar NProgress.done() })
五、功能按钮权限控制
<el-button type="primary" @click="showAddUser" v-show="$store.state.user.buttons.indexOf('btn.user.add')!=-1">添加</el-button>
学习交流
猜您可能还喜欢
- CERT_HAS_EXPIRED错误如何解决(2245)
- Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2188)
- Vue 2.x + Element后台模板开发教程(三)后台首页模板设计(1213)
- vuex中 this.$store.dispatch() 与 this.$store.commit()方法的区别(1190)
- vuejs中如何使用axios调用后台接口(1025)
- Vue 2.x + Element后台模板开发教程(一)(1020)
- Error: Cannot find module ‘chokidar‘ 解决方法(1009)
- Vue后台开发实例教程(二)Title修改及左侧菜单修改(901)
- vue.js初探(一)vue.js全家桶介绍(825)
- vue.js初探(三)vue页面结构(820)
评论列表
发表评论
文章分类
文章归档
- 2025年3月 (1)
- 2024年6月 (2)
- 2024年5月 (2)
- 2024年4月 (4)
- 2024年3月 (30)
- 2024年1月 (4)
- 2023年12月 (2)
- 2023年11月 (4)
- 2023年10月 (4)
- 2023年9月 (6)
- 2023年3月 (2)
- 2023年2月 (1)
- 2023年1月 (1)
- 2022年12月 (1)
- 2022年9月 (21)
- 2022年8月 (10)
- 2022年7月 (3)
- 2022年4月 (1)
- 2022年3月 (13)
- 2021年8月 (1)
- 2021年3月 (1)
- 2020年12月 (42)
- 2020年11月 (7)
- 2020年10月 (5)
- 2020年8月 (1)
- 2020年6月 (1)
- 2020年3月 (2)
- 2019年12月 (8)
- 2019年11月 (3)
- 2019年9月 (1)
- 2019年4月 (1)
- 2019年3月 (6)
- 2019年2月 (1)
- 2018年7月 (7)
阅读排行
- 1.asp.net mvc内微信pc端、H5、JsApi支付方式总结(5702)
- 2.各大搜索网站网站收录提交入口地址(3201)
- 3.Windows 10休眠文件更改存储位置(3163)
- 4.ECharts仪表盘实例及参数使用详解(3095)
- 5.windows 10安装myeclipse 10破解补丁cracker.jar、run.bat闪退解决办法(2991)
- 6.HTML5 WebSocket与C#建立Socket连接实现代码(2866)
- 7.华为鸿蒙系统清除微信浏览器缓存方法(2779)
- 8.CERT_HAS_EXPIRED错误如何解决(2245)
- 9.Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2188)
- 10.HBuilder编辑器格式化代码(2118)