(1)路由表的创建,包含静态路由与动态路由
/src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import {constantRouterMap} from './routes'
Vue.use(VueRouter)
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this,location).catch(err => err)
}
const createRouter = () => new VueRouter({
mode: 'history',
scrollBehavior: () => ({
y: 0
}),
routes:constantRouterMap
})
const router = createRouter()
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
/src/router/routes.js
import commonLayout from '../layout/index.vue'
// 静态路由设置
export const constantRouterMap = [
{
path:'/',
component:commonLayout,
redirect: '/home',
meta:{
title:'',
icon:'el-icon-house',
hidden:false
},
children:[
{
path:'/home',
component:() => import('@/views/home/index.vue'),
meta:{
title:'首页',
icon:'el-icon-house',
hidden:false
}
}
]
},
{
path:'/login',
name:'login',
component:() => import('@/views/login/index.vue'),
hidden:true
},
{
path:'/404',
name:'404',
component:() => import('@/views/404/index.vue'),
hidden:true
}
]
// 登录成功,获取用户身份信息,动态添加路由
export const asyncRoutes = [
{
path:'/user',
name:'user',
component:commonLayout,
redirect:'/user/role',
meta:{
title:'用户管理',
icon:'el-icon-user',
roles: ['admin']
},
children:[
{
path:'/user/role',
name:'role',
component:() => import('@/views/user/roleManagement/index.vue'),
meta:{
title:'角色管理',
icon:'el-icon-user',
roles: ['admin']
}
},
{
path:'/user/permission',
name:'permission',
component:() => import('@/views/user/perManagement/index.vue'),
meta:{
title:'权限管理',
icon:'el-icon-lock',
roles: ['admin']
}
}
]
},
{
path:'/product',
name:'product',
component:commonLayout,
meta:{
title:'商品管理',
icon:'el-icon-tickets',
roles:['admin','operate']
}
},
{
path: '*',
redirect: '/404',
hidden: true
}
]
(2)动态路由思路:登录成功获取token-获取用户信息,根据meta.roles筛选路由表
在src下创建permission.js
import NProgress from "nprogress";
import 'nprogress/nprogress.css';
import { Message } from 'element-ui';
import { GET_TOKEN } from '@/utils/token';
import router from './router'
import store from './store'
NProgress.configure({showSpinner:false});
const whiteList = ['/login', '/404'] // 不重定向白名单
router.beforeEach(async (to, from, next) => {
NProgress.start()
let token = GET_TOKEN()
if (token) {
if (to.path == '/login') {
next({ path: '/' })
} else {
// 确定获取用户角色,筛选路由
let roles = store.getters.roles && store.getters.roles.length > 0
if (roles) {
// 有用户权限时,则完成路由筛选
next()
} else {
try {
// 获取用户权限
let res = await store.dispatch('user/getUserInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', res.roles)
router.options.routes = store.getters.permission_routes
// 动态添加路由
accessRoutes.forEach(route => {
router.addRoute(route)
})
next({
...to,
replace: true
})
} catch (error) {
// 退出登录
await store.dispatch('loginout')
Message.error('出现错误~请重新登录')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
next()
}
} else {
// 未登录重定向
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
let redirect = router.currentRoute.query.redirect
next({path: redirect || '/'})
NProgress.done()
}
}
})
router.afterEach(() => {
NProgress.done()
})
使用状态管理筛选路由
/src/module/permission.js
import { constantRouterMap,asyncRoutes } from '@/router/routes'
function hasPermission(route,roles){
if(route.meta && route.meta.roles){
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
export function filterAsyncRoutes(routes,roles) {
let res = []
routes.forEach(route => {
let temp = {
...route
}
// 有权限,递归筛选路由
if(hasPermission(route,roles)) {
if(temp.children) {
temp.children = filterAsyncRoutes(temp.children,roles)
}
res.push(temp)
}
})
return res
}
const state = {
routes:[],//全部路由
addRoutes:[]//动态路由
}
const mutations = {
SET_ROUTES: (state,routes) => {
state.addRoutes = routes
state.routes = constantRouterMap.concat(routes)
}
}
// 根据角色筛选路由
const actions = {
generateRoutes({commit},roles) {
return new Promise((resolve,reject) => {
let accessRoutes = []
if(roles && roles.length > 0) {
accessRoutes = filterAsyncRoutes(asyncRoutes,roles)
}
commit('SET_ROUTES',accessRoutes)
resolve(accessRoutes)
})
}
}
export default {
namespaced:true,
state,
mutations,
actions
}
侧边栏:获取状态管理下筛选后的路由routes
/layout/index.js页面
<template>
<div class="common_layout">
<div :class="[{isFold:fold},'layout_asider']">
<Logo/>
<el-menu
:default-active="currentPath"
background-color="#545c64"
text-color="#ffffff"
unique-opened
router
:collapse="fold"
>
<Menu :routes="permission_routes"/>
</el-menu>
</div>
<div :class="[{isFold:fold},'layout_nav']">
<Tabbar/>
</div>
<div :class="[{isFold:fold},'layout_container']">
<Main/>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import Menu from './menu'
import Logo from'./logo'
import Tabbar from './tabbar'
import Main from './main'
export default {
name:"layout",
components:{ Menu,Logo,Tabbar,Main },
data() {
return {}
},
computed:{
...mapGetters([
'permission_routes',
'fold'
]),
currentPath() {
return this.$route.path
}
}
}
</script>
<style lang="less" scoped>
.common_layout{
width: 100%;
height: 100vh;
.layout_asider{
width: @base_asider_width;
height: 100vh;
background-color: @base_asider_background;
overflow: auto;
/deep/.el-menu{
border: none;
}
&.isFold{
width:@base_asider_minWidth;
}
/deep/.el-menu--collapse{
.el-submenu__icon-arrow{
display: none;
}
}
}
.layout_nav{
width:calc(100% - @base_asider_width);
height:@base_nav_height;
position: fixed;
top:0;
left:@base_asider_width;
border-bottom:1px solid #e4e7e9;
&.isFold{
width: calc(100% - @base_asider_minWidth);
left: @base_asider_minWidth;
}
}
.layout_container{
width: calc(100% - @base_asider_width);
height: calc(100vh - @base_nav_height);
position: absolute;
top: @base_nav_height;
left: @base_asider_width;
padding: 15px;
&.isFold{
width: calc(100% - @base_asider_minWidth);
left: @base_asider_minWidth;
}
}
}
</style>
/src/menu/index.js页面
<template>
<div>
<div v-for="item in routes" :key="item.path">
<template v-if="!item.children">
<el-menu-item :index="item.path" v-if="!item.hidden">
<i :class="item.meta.icon"></i>
<template #title>
{{ item.meta.title }}
</template>
</el-menu-item>
</template>
<template v-if="item.children && item.children.length === 1">
<el-menu-item :index="item.children[0].path" v-if="!item.children[0].hidden">
<i :class="item.children[0].meta.icon"></i>
<template #title>
{{ item.children[0].meta.title }}
</template>
</el-menu-item>
</template>
<template v-if="item.children && item.children.length > 1">
<el-submenu :index="item.path" v-if="!item.hideen">
<template #title>
<i :class="item.meta.icon"></i>
<span>{{ item.meta.title }}</span>
</template>
<Menu :routes="item.children"/>
</el-submenu>
</template>
</div>
</div>
</template>
<script>
export default {
name:"Menu",
props:{
routes:{
type: Array,
default: () => []
}
},
data() {
return {}
},
}
</script>
<style lang="less" scoped>
</style>