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错误如何解决(2964)
- Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2600)
- Vue 2.x + Element后台模板开发教程(三)后台首页模板设计(1294)
- vuex中 this.$store.dispatch() 与 this.$store.commit()方法的区别(1269)
- Error: Cannot find module ‘chokidar‘ 解决方法(1258)
- Vue后台开发实例教程(二)Title修改及左侧菜单修改(1147)
- Vue 2.x + Element后台模板开发教程(一)(1120)
- vuejs中如何使用axios调用后台接口(1115)
- vue.js初探(一)vue.js全家桶介绍(914)
- vue.js初探(三)vue页面结构(908)
评论列表
发表评论
文章分类
文章归档
- 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支付方式总结(5876)
- 2.Windows 10休眠文件更改存储位置(3825)
- 3.各大搜索网站网站收录提交入口地址(3478)
- 4.ECharts仪表盘实例及参数使用详解(3431)
- 5.windows 10安装myeclipse 10破解补丁cracker.jar、run.bat闪退解决办法(3422)
- 6.HTML5 WebSocket与C#建立Socket连接实现代码(3179)
- 7.华为鸿蒙系统清除微信浏览器缓存方法(3171)
- 8.CERT_HAS_EXPIRED错误如何解决(2964)
- 9.Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2600)
- 10.HBuilder编辑器格式化代码(2393)
