示例前提:路由数组配置时,拆分成静态路由和动态路由,导出时配置的静态路由
定义用户信息仓库(hooks语法)
import { defineStore } from "pinia";
const useUserStore = defineStore('userStore', () => {
这里是一些获取用户数据,储存数据的代码...省略...
return {
token, // 用户token
role, // 用户角色
userInfo, // 用户信息
MyLogin, // 登陆方法
MyGetUserInfo, // 获取用户信息方法
ClearInfo // 清除信息数据
}
})
export default useUserStore
}
定义权限仓库文件
import { defineStore } from "pinia";
// 引入声明文件中自己定义的ts约束类型
import { MyRoutes, MyRoute } from "~/my-router";
// 引入静态路由和动态路由
import { constanceRoutes, asyncRoutes } from "@/router";
export const usePermissionStore = defineStore('permissionStore', () => {
// 是否获取用户信息
const isGetUserInfo = ref(false)
// 最终给导航栏使用的路由数组
const navRoutes = ref<MyRoutes>([])
// 修改用户信息状态
function setUserInfoState(flag: boolean) {
isGetUserInfo.value = flag
}
// 判断单个路由权限,返回布尔值
function hasPermission(route: MyRoute, role: string) {
// meta上有设置roles时则需要判断权限
if (route.meta?.roles) {
return route.meta.roles.includes(role)
}
// 没有设置roles时则有权限
return true
}
// 判断多个路由权限,并筛选出有权限的路由数组
function filterAsyncRoutes(routes: MyRoutes, role: string) {
// 创建临时路由数组
const tempArr: MyRoutes = []
// 遍历传入的多个路由数组
routes.forEach(route => {
// 遍历出来的单个路由判断是否有权限(外层)
if (hasPermission(route, role)) {
// 如果有children,则递归遍历赋值(内层)
if (route.children && route.children.length > 0) {
route.children = filterAsyncRoutes(route.children, role)
}
// 有权限则添加该路由
tempArr.push(route)
}
})
// 返回筛选后的路由数组
return tempArr
}
// 生成动态路由
function generateRoute(role: string) {
// 使用异步便于控制
return new Promise<MyRoutes>(resolve => {
// 传入我们自己分组的动态路由,筛选出符合角色身份的路由数组
const accessRoutes = filterAsyncRoutes(asyncRoutes, role)
// 将静态路由和新的动态路由拼接
navRoutes.value = [...constanceRoutes, ...accessRoutes]
resolve(accessRoutes)
})
}
return {
isGetUserInfo,
navRoutes,
setUserInfoState,
generateRoute
}
})
export default usePermissionStore
路由权限守卫
- 安装滚动条插件
yarn add nprogress
yarn add @types/nprogress
- 配置路由守卫
// 引入滚动条及样式
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
// 引入路由
import router from "./router";
// 引入权限仓库
import usePermissionStore from "./store/permissionStore";
// 引入用户信息仓库
import useUserStore from "./store/userStore";
// 滚动条配置
NProgress.configure({ showSpinner: false })
// 路由白名单
const whiteList = ['/login', '/404']
router.beforeEach(async (to, _from, next) => {
// 开启滚动条
NProgress.start()
// 实例仓库
const permissionStore = usePermissionStore()
const userStore = useUserStore()
// 1 判断是否有token
const { token } = userStore
if (token) {
// 判断是否去login
if (to.path === '/login') {
// 不允许登陆状态切换用户,跳到首页
next('/')
} else {
// 判断是否获取到了用户信息
const { isGetUserInfo } = permissionStore
// 如果有用户信息则放行
if (isGetUserInfo) {
// 放行
next()
} else {
// 没有用户信息则尝试获取用户信息
try {
await userStore.MyGetUserInfo()
// 获取用户信息成功后:
// 1 拿到角色
const { role } = userStore
// 2 传入角色,获取动态路由
const accessRoutes = await permissionStore.generateRoute(role as string)
// 3 遍历添加单个路由
accessRoutes.forEach(route => {
router.addRoute(route)
})
// 4 设置获取用户信息状态为true
permissionStore.setUserInfoState(true)
// hack方法,再跑一次
next({ ...to, replace: true })
} catch (error) {
// 获取用户信息失败则重置信息且跳转到登陆页面
await userStore.ClearInfo()
next('/login')
}
}
}
} else {
// 判断是否去白名单
if (whiteList.includes(to.path)) {
// 是则放行
next()
}
// 不是则去登陆
next(`/login?redirect=${to.fullPath}`)
}
})
// 后置守卫,结束滚动条
router.afterEach(() => {
NProgress.done()
})