# 基本配置
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 |
|---|---|
![]() | ![]() |

