实现admin用户可以看到所有的菜单
实现test用户看到只属于自己的菜单
整体逻辑:
1.src\router\index.js constantRoutes 列表放公共路由,asyncRoutes 放所有的路由
2.用户的登录的时候获取用户的role,然后跟asyncRoutes列表过滤属于自己的路由。
3.router.addRoutes加载自己的路由
4.vuex管理路由表,侧边栏渲染vuex里面的路由表
src\router\index.js 新增异步路由asyncRoutes,404页面必须放异步路由最后面。
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' 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
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/403',
component: () => import('@/views/403'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: 'dashboard', icon: 'dashboard' }
}]
}
]
export const asyncRoutes = [
{
path: '/example',
component: Layout,
redirect: '/example/table',
name: 'Example',
meta: { title: 'Example', icon: 'example', roles: ['admin', 'test'] },
children: [
{
path: 'table',
name: 'Table',
component: () => import('@/views/table/index'),
meta: { title: 'Table', icon: 'table', roles: ['admin'] }
},
{
path: 'tree',
name: 'Tree',
component: () => import('@/views/tree/index'),
meta: { title: 'Tree', icon: 'tree' }
}
]
},
{
path: '/article',
component: Layout,
children: [
{
path: 'index',
name: 'Article',
component: () => import('@/views/article/index'),
meta: { title: '文章管理', icon: 'form', roles: ['admin'] }
}
]
},
{
path: '/svn',
component: Layout,
children: [
{
path: 'index',
name: 'Svn',
component: () => import('@/views/svn/index'),
meta: { title: 'SVN用户管理', icon: 'form', roles: ['admin'] }
}
]
},
// {
// path: 'external-link',
// component: Layout,
// children: [
// {
// path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
// meta: { title: '外部链接', icon: 'link', roles: ['test'] }
// }
// ]
// },
// {
// path: '/form',
// component: Layout,
// children: [
// {
// path: 'index',
// name: 'Form',
// component: () => import('@/views/form/index'),
// meta: { title: 'Form', icon: 'form' }
// }
// ]
// },
// {
// path: '/nested',
// component: Layout,
// redirect: '/nested/menu1',
// name: 'Nested',
// meta: {
// title: 'Nested',
// icon: 'nested'
// },
// children: [
// {
// path: 'menu1',
// component: () => import('@/views/nested/menu1/index'), // Parent router-view
// name: 'Menu1',
// meta: { title: 'Menu1' },
// children: [
// {
// path: 'menu1-1',
// component: () => import('@/views/nested/menu1/menu1-1'),
// name: 'Menu1-1',
// meta: { title: 'Menu1-1' }
// },
// {
// path: 'menu1-2',
// component: () => import('@/views/nested/menu1/menu1-2'),
// name: 'Menu1-2',
// meta: { title: 'Menu1-2' },
// children: [
// {
// path: 'menu1-2-1',
// component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
// name: 'Menu1-2-1',
// meta: { title: 'Menu1-2-1' }
// },
// {
// path: 'menu1-2-2',
// component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
// name: 'Menu1-2-2',
// meta: { title: 'Menu1-2-2' }
// }
// ]
// },
// {
// path: 'menu1-3',
// component: () => import('@/views/nested/menu1/menu1-3'),
// name: 'Menu1-3',
// meta: { title: 'Menu1-3' }
// }
// ]
// },
// {
// path: 'menu2',
// component: () => import('@/views/nested/menu2/index'),
// meta: { title: 'menu2' }
// }
// ]
// },
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
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
state新增roles: []
mutations新增SET_ROLES
// 当前用户的权限<br /> constroles = data.Roles<br /> commit('SET_ROLES', roles)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1414647/1636615318819-55a2bdc4-1aa1-4288-ab6b-49d10e582b97.png#clientId=u82fd9659-c7cd-4&from=paste&height=488&id=u2ba680f9&margin=%5Bobject%20Object%5D&name=image.png&originHeight=488&originWidth=1510&originalType=binary&ratio=1&size=68811&status=done&style=none&taskId=u734ee731-3415-4b7c-9124-7abb3ecbb72&width=1510)<br /> commit('SET_ROLES', '')<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1414647/1636615365529-d38bd707-3ec1-4380-bfdd-c01445f43f6a.png#clientId=u82fd9659-c7cd-4&from=paste&height=296&id=u2b149cf2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=296&originWidth=1341&originalType=binary&ratio=1&size=36253&status=done&style=none&taskId=ucd92147e-313b-403e-bfd8-417f98dcb53&width=1341)
import { login, logout, getInfo, refreshToken } from '@/api/user'
import { getToken, setToken, removeToken, getTokenExpire, setTokenExpire, removeTokenExpire } from '@/utils/auth'
import { resetRouter } from '@/router'
const state = {
token: getToken(),
name: '',
avatar: '',
tokenExpire: getTokenExpire(),
roles: []
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_TOKENEXPIRE: (state, token) => {
state.tokenExpire = token
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const data = response
commit('SET_TOKEN', data.token)
commit('SET_TOKENEXPIRE', data.expire)
setToken(data.token)
setTokenExpire(data.expire)
resolve()
}).catch(error => {
reject(error)
})
})
},
refreshToken({ commit }) {
return new Promise((resolve, reject) => {
refreshToken().then(response => {
const data = response
commit('SET_TOKEN', data.token)
commit('SET_TOKENEXPIRE', data.expire)
setToken(data.token)
setTokenExpire(data.expire)
resolve()
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(response => {
const data = response.data
if (!data) {
reject('Verification failed, please Login again.')
}
const { Name, Avatar } = data
// 当前用户的权限
const roles = data.Roles
commit('SET_ROLES', roles)
commit('SET_NAME', Name)
commit('SET_AVATAR', Avatar)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', '')
commit('SET_TOKENEXPIRE', '')
removeToken()
removeTokenExpire()
resetRouter()
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_ROLES', '')
commit('SET_TOKEN', '')
commit('SET_TOKENEXPIRE', '')
removeToken()
removeTokenExpire()
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
src\store\getters.js
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
visitedViews: state => state.tagsView.visitedViews, // 这个是新增的
cachedViews: state => state.tagsView.cachedViews, // 这个是新增的
roles: state => state.user.roles,
permission_routes: state => state.permission.routes
}
export default getters
src\store\index.js
modules新增permission
复制vue-admin完整文件到src\store\modules\permission.js
import { asyncRoutes, constantRoutes } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* 使用 meta.role 判断当前用户是否有权限
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
// admin 获得全部路由 这里可以写您的逻辑
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
src\permission.js
路由守卫文件在获取用户信息之后,异步挂在路由。
awaitstore.dispatch(‘user/getInfo’)
// 对应store/modules/user.js、根据登录的账号设置权限
constroles = store.getters.roles
// 可以访问的路由
constaccessRoutes = awaitstore.dispatch(‘permission/generateRoutes’, roles)
console.log(‘当前账号的权限页面(非公共页面)’, accessRoutes)
router.addRoutes(accessRoutes)
next({ …to, replace:true })
src\layout\components\Sidebar\index.vue
最后修改侧边栏,循环路由
改为
computed新增’permission_routes’
src\layout\components\Sidebar\index.vue
<template>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<!-- <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" /> -->
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
export default {
components: { SidebarItem, Logo },
computed: {
...mapGetters([
'sidebar',
'permission_routes'
]),
routes() {
return this.$router.options.routes
},
activeMenu() {
const route = this.$route
const { meta, path } = route
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
至此实现了。。。。