diff --git a/src/pages/403.vue b/src/pages/403.vue
new file mode 100644
index 0000000..74be3d0
--- /dev/null
+++ b/src/pages/403.vue
@@ -0,0 +1,25 @@
+
+
+
+
+ 回到首页
+
+
+
+
+
+
+
+
diff --git a/src/router/index.ts b/src/router/index.ts
index 40b2030..9d82cd2 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -9,6 +9,7 @@ import { routes } from 'vue-router/auto-routes'
import { setupLayouts } from 'virtual:generated-layouts'
import i18n, { loadLanguage } from '@/i18n'
import { getAccessToken } from '@/utils/token'
+import { useMenuStore } from '@/stores/modules/menu'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -39,15 +40,49 @@ async function loadRouteLocales(to: RouteLocationNormalized) {
}
}
+// 无需菜单权限校验的路径(首页 + 错误页)
+const FREE_PATHS = new Set(['/', '/403'])
+
+/** 收集菜单树中所有可访问 path。 */
+function collectMenuPaths(nodes: { path?: string | null; children?: unknown[] }[]): string[] {
+ const paths: string[] = []
+ for (const n of nodes) {
+ if (n.path) paths.push(n.path)
+ if (n.children?.length) paths.push(...collectMenuPaths(n.children as { path?: string | null; children?: unknown[] }[]))
+ }
+ return paths
+}
+
// 路由前置守卫
router.beforeEach(async (to, from, next) => {
await loadRouteLocales(to)
// 未标记 requiresAuth: false 的路由默认需要登录
const requiresAuth = to.meta.requiresAuth !== false
- if (requiresAuth && !getAccessToken()) {
- next('/login')
- } else {
+ if (!requiresAuth) {
next()
+ return
+ }
+ if (!getAccessToken()) {
+ next('/login')
+ return
+ }
+ // 路由级权限:自由路径放行,其余须在可访问菜单中
+ if (FREE_PATHS.has(to.path)) {
+ next()
+ return
+ }
+ const menuStore = useMenuStore()
+ try {
+ await menuStore.fetch()
+ } catch {
+ next('/login')
+ return
+ }
+ const accessible = new Set(collectMenuPaths(menuStore.menus))
+ if (accessible.has(to.path)) {
+ next()
+ } else {
+ next('/403')
}
})
diff --git a/typed-router.d.ts b/typed-router.d.ts
index daa4a1c..470712c 100644
--- a/typed-router.d.ts
+++ b/typed-router.d.ts
@@ -37,6 +37,13 @@ declare module 'vue-router/auto-routes' {
{ all: ParamValue },
| never
>,
+ '/403': RouteRecordInfo<
+ '/403',
+ '/403',
+ Record,
+ Record,
+ | never
+ >,
'/demo': RouteRecordInfo<
'/demo',
'/demo',
@@ -174,6 +181,12 @@ declare module 'vue-router/auto-routes' {
views:
| never
}
+ 'src/pages/403.vue': {
+ routes:
+ | '/403'
+ views:
+ | never
+ }
'src/pages/demo.vue': {
routes:
| '/demo'