# 基本配置
| import { createRouter, createWebHistory } from 'vue-router'; | |
| import pinia from './pinia'; | |
| import { useUserStore } from '../store/user'; | |
| const user = useUserStore(pinia); | |
| // 不需要权限的页面 | |
| const constantRoutes = [ | |
|   { | |
|     // 登录 | |
| path: '/login', | |
| name: 'login', | |
| component: () => import('../views/login/index.vue') | |
| }, | |
|   { | |
|     // 404 | |
| path: '/:pathMatch(.*)', | |
| name: 'notFound', | |
| component: () => import('../views/error/notFound.vue') | |
| }, | |
|   { | |
|     // 无权限 | |
| path: '/noPermission', | |
| name: 'noPermission', | |
| component: () => import('../views/error/noPermission.vue') | |
|   } | |
| ]; | |
| const asyncRoutes = { | |
| path: '/', | |
| name: 'main', | |
| component: () => import('../views/mainPage.vue'), | |
| children: [ | |
|     { | |
|       // 首页 | |
| path: '/', | |
| name: 'home', | |
| component: () => import('../views/home/index.vue') | |
| }, | |
|     { | |
|       // 用户管理 | |
| path: '/settingUser', | |
| name: 'settingUser', | |
| component: () => import('../views/setting/user.vue') | |
|     } | |
|   ] | |
| }; | |
| const router = createRouter({ | |
| history: createWebHistory('/'), | |
|   routes: constantRoutes | |
| }); | |
| router.addRoute(asyncRoutes); | |
| router.beforeEach((to, from, next) => { | |
|   // 切换 router 时,取消 pending 中的请求 | |
| if (window.__axiosPromiseArr) { | |
| window.__axiosPromiseArr.forEach((ele, ind) => { | |
| ele.cancel(); | |
| delete window.__axiosPromiseArr[ind]; | |
| }); | |
|   } | |
|   //token 过期 | |
| if (localStorage.getItem('expires') && (new Date().getTime() - localStorage.getItem('expires')) / 1000 > 1) { | |
| this.$message.error('登录失效,请重新登录', () => { | |
| localStorage.removeItem('userInfon'); | |
| localStorage.removeItem('token'); | |
| localStorage.removeItem('expires'); | |
| location.href = '/login'; | |
| }); | |
| return; | |
|   } | |
|   // 登录判断 | |
| if (user.token) { | |
| if (to.path === '/login') { | |
| next({ path: '/' }); | |
| } else { | |
|       // 权限判断 | |
| next(); | |
|     } | |
| } else { | |
| if (to.path === '/login') { | |
| next(); | |
| } else { | |
| next({ name: 'login' }); | |
|     } | |
|   } | |
| }); | |
| // 跳转完成后,将滚动条位置重置 | |
| router.afterEach(to => { | |
| window.scrollTo(0, 0); | |
| }); | |
| export default router; | 
# 1. 安装 vue-router
| npm install vue-router | 
# 2. 路由实例
创建路由实例,顺带初始化静态路由,而动态路由需要用户登录,根据用户拥有的角色进行权限校验后进行初始化。
| // src/router/index.ts | |
| import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; | |
| export const Layout = () => import('@/layout/index.vue'); | |
| // 静态路由 | |
| export const constantRoutes: RouteRecordRaw[] = [ | |
|   { | |
| path: '/redirect', | |
| component: Layout, | |
| meta: { hidden: true }, | |
| children: [ | |
|       { | |
| path: '/redirect/:path(.*)', | |
| component: () => import('@/views/redirect/index.vue') | |
|       } | |
|     ] | |
| }, | |
|   { | |
| path: '/login', | |
| component: () => import('@/views/login/index.vue'), | |
| meta: { hidden: true } | |
| }, | |
|   { | |
| path: '/', | |
| component: Layout, | |
| redirect: '/dashboard', | |
| children: [ | |
|       { | |
| path: 'dashboard', | |
| component: () => import('@/views/dashboard/index.vue'), | |
| name: 'Dashboard', | |
| meta: { title: 'dashboard', icon: 'homepage', affix: true } | |
|       } | |
|     ] | |
|   } | |
| ]; | |
| /** | |
| * 创建路由 | |
| */ | |
| const router = createRouter({ | |
| history: createWebHashHistory(), | |
| routes: constantRoutes as RouteRecordRaw[], | |
|   // 刷新时,滚动条位置还原 | |
| scrollBehavior: () => ({ left: 0, top: 0 }) | |
| }); | |
| /** | |
| * 重置路由 | |
| */ | |
| export function resetRouter() { | |
| router.replace({ path: '/login' }); | |
| location.reload(); | |
| } | |
| export default router; | 
# 3. 全局注册路由实例
| // main.ts | |
| import router from "@/router"; | |
| app.use(router).mount('#app') | 
# 4. 动态权限路由
路由守卫 src/permission.ts ,获取当前登录用户的角色信息进行动态路由的初始化

最终调用 permissionStore.generateRoutes(roles) 方法生成动态路由
| // src/store/modules/permission.ts  | |
| import { listRoutes } from '@/api/menu'; | |
| export const usePermissionStore = defineStore('permission', () => { | |
| const routes = ref<RouteRecordRaw[]>([]); | |
| function setRoutes(newRoutes: RouteRecordRaw[]) { | |
| routes.value = constantRoutes.concat(newRoutes); | |
|   } | |
|   /** | |
| * 生成动态路由 | |
| * | |
| * @param roles 用户角色集合 | |
| * @returns | |
| */ | |
| function generateRoutes(roles: string[]) { | |
| return new Promise<RouteRecordRaw[]>((resolve, reject) => { | |
|       // 接口获取所有路由 | |
| listRoutes() | |
| .then(({ data: asyncRoutes }) => { | |
|           // 根据角色获取有访问权限的路由 | |
| const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles); | |
| setRoutes(accessedRoutes); | |
| resolve(accessedRoutes); | |
| }) | |
| .catch(error => { | |
| reject(error); | |
| }); | |
| }); | |
|   } | |
|   // 导出 store 的动态路由数据 routes  | |
| return { routes, setRoutes, generateRoutes }; | |
| }); | 
接口获取得到的路由数据

根据路由数据 (routes) 生成菜单的关键代码
| src/layout/componets/Sidebar/index.vue | src/layout/componets/Sidebar/SidebarItem.vue | 
|---|---|
|  |  | 
