45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
import { createRouter, createWebHashHistory } from "vue-router";
|
|
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from "vue-router";
|
|
import routes from "./routes";
|
|
import { useDefaultStore } from "@/store";
|
|
import { UserRoleEnum } from "@/types";
|
|
|
|
const router = createRouter({
|
|
history: createWebHashHistory(),
|
|
routes,
|
|
});
|
|
|
|
/**
|
|
* 当前角色是否可访问目标路由(meta.roles 未声明 = 登录即可访问)。
|
|
* 供路由守卫与 App.vue 角色落定后的复核共用。
|
|
*/
|
|
export function isRouteAllowed(
|
|
route: RouteLocationNormalizedLoaded | RouteRecordRaw,
|
|
role: UserRoleEnum
|
|
): boolean {
|
|
const roles = (route.meta?.roles ?? null) as UserRoleEnum[] | null;
|
|
return !roles || roles.length === 0 || roles.includes(role);
|
|
}
|
|
|
|
/**
|
|
* 路由守卫(防止直接敲 URL 绕过菜单):
|
|
* - 角色未落定(roleLoaded=false,首次导航早于 App.vue 的 GetUserScope)时放行,
|
|
* 由 App.vue 在角色落定后调用本守卫的复核逻辑重定向;
|
|
* - 角色已落定后按 meta.roles 校验,不满足重定向 /no-auth。
|
|
*/
|
|
router.beforeEach(to => {
|
|
if (to.path === "/no-auth") {
|
|
return true;
|
|
}
|
|
const store = useDefaultStore();
|
|
if (!store.roleLoaded) {
|
|
return true;
|
|
}
|
|
if (!isRouteAllowed(to, store.userRole)) {
|
|
return { path: "/no-auth", replace: true };
|
|
}
|
|
return true;
|
|
});
|
|
|
|
export default router;
|