一般后台管理系统都会有管理员和普通用户的区分,所以要做权限控制
思路
- 创建公用页面login,可以让用户进行登录操作,根据用户信息对应的身份,匹配不同的权限模块
- 在路由前置钩子进行身份验证拦截,将拦截条件分为 白名单(不做拦截)、登录态未登录、登录态已登陆
- 路由注册分为两步,公用页面直接注册,权限路由根据登录用户的身份来注册
- 解决路由层级过深会导致keep-alive不缓存问题
实现
- 安装依赖
"dependencies": {
"nprogress": "^0.2.0",
"vue-router": "^4.0.6"
}
- 在src目录下创建配置文件config,统一管理一些设置信息 新建setting.config.js
export const title: string = '风控管理平台'
export const titleSeparator: string = ' - '
export const titleReverse: boolean = true
export const keepAliveMaxNum: number = 20
export const routerMode: 'hash' | 'history' = 'hash'
export const routesWhiteList: string[] = [
'/login',
'/login/vip',
'/404',
'/403'
]
export const loginInterception: boolean = true
- 在src目录下创建router文件夹,新建index.ts router.ts
import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from 'vue-router'
import { loginInterception, routerMode, routesWhiteList, title, titleReverse } from '@/config/setting.config'
import Layout from '@/views/layout/index.vue'
import VabProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { store } from '@/store'
let routerLoad = false
VabProgress.configure({
easing: 'ease',
speed: 500,
trickleSpeed: 200,
showSpinner: false
})
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: Layout,
redirect: '/home'
},
{
path: '/home',
name: 'Home',
component: () => import('@/views/index/index.vue'),
meta: {
noKeepAlive: true
}
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/login/index.vue')
},
{
path: '/403',
name: '403',
component: () => import('@/views/403.vue')
},
{
path: '/:pathMatch(.*)*',
name: '404',
component: () => import('@/views/404.vue')
}
]
const router = createRouter({
history: routerMode === 'hash'
? createWebHashHistory(import.meta.env.BASE_URL)
: createWebHistory(import.meta.env.BASE_URL),
routes
})
export function resetRoute (routes: any): void {
router.addRoute({
path: '/',
component: Layout,
redirect: '/home',
children: getAllFirstRoute(routes)
})
}
function getAllFirstRoute (routes: any, result: any = []): any {
routes.forEach((route: any) => {
result.push({
...route,
children: [],
path: route.meta.realPath
})
if (route.children && route.children.length > 0) {
result = getAllFirstRoute(route.children, result)
}
})
return result
}
router.beforeEach(async (to, from, next) => {
VabProgress.start()
let hasToken = store.getters['user/token']
if (!loginInterception) hasToken = true
if (routesWhiteList.includes(to.path)) {
next()
} else if (hasToken) {
if (routerLoad) {
next()
} else {
await store.dispatch('router/setRoutes')
routerLoad = true
next(to.path)
}
} else {
next(store.getters['router/logoutUrl'])
}
VabProgress.done()
})
router.afterEach(route => {
if (route.meta && route.meta.title) {
if (titleReverse) {
document.title = `${route.meta.title} - ${title}`
} else {
document.title = `${title} - ${route.meta.title}`
}
}
})
export default router
- 状态管理新增router模块,更新state.d.ts ,新建 router.ts
declare namespace MyStore {
type language = 'zh-cn' | 'en'
type RouterRoleType = 'administrators' | 'headquarters' | 'subsidiary'
interface RouteMeta {
title: string,
icon: string,
hidden: boolean,
noKeepAlive: boolean,
fullPath: string,
realPath: string
}
interface Route {
path: string,
component: any,
redirect: string,
name: string,
meta: RouteMeta,
children: Array<Route>
}
interface State {
count: number
}
interface SettingState {
language: language
}
interface RouterState {
roleType: RouterRoleType,
routes: Route,
cachedRoutes: Array<Route>
}
}
import routers from '@/router/router'
import { convertRouter } from '@/utils/routes'
import { resetRoute } from '@/router'
export default {
name: 'router',
namespaced: true,
state: () => {
const state: MyStore.RouterState = {
roleType: 'headquarters',
routes: {
children: []
},
cachedRoutes: []
}
return state
},
getters: {
logoutUrl () {
return '/login'
}
},
mutations: {
setRoutes (state: MyStore.RouterState, routes: MyStore.Route) {
state.routes = routes
},
setRoleType (state: MyStore.RouterState, type: MyStore.RouterRoleType) {
state.roleType = type
},
setCachedRoutes (state: MyStore.RouterState, routes: Array<MyStore.Route>) {
state.cachedRoutes = routes
}
},
actions: {
async setRoutes ({ commit, state }: any) {
const routes = routers[state.roleType]
commit('setRoutes', routes)
const syncRoutes = convertRouter([routes])
resetRoute(syncRoutes)
},
async setCachedRoutes ({ commit }: any, routes: any) {
commit('setCachedRoutes', routes)
}
}
}
- 创建工具方法,解析路由
export function convertRouter (asyncRoutes: Array<any>, parentUrl: string = ''): any {
return asyncRoutes.map(route => {
if (!route.meta) route.meta = { hidden: false, fullPath: '', realPath: '' }
if (!parentUrl) {
route.meta.fullPath = route.path
route.meta.realPath = route.path
} else if (route.meta.hidden) {
route.meta.fullPath = parentUrl
route.meta.realPath = `${parentUrl}/${route.path}`
} else {
route.meta.fullPath = `${parentUrl}/${route.path}`
route.meta.realPath = `${parentUrl}/${route.path}`
}
if (route.children && route.children.length) { route.children = convertRouter(route.children, route.meta.fullPath) }
if (route.children && route.children.length === 0) delete route.children
return route
})
}
- 在登录页更新状态管理器 router/roleType,再进行页面跳转,在前置钩子处就会自动匹配并注册当前权限下的路由了。