Files
vue3-element-admin/src/store/modules/permission.store.ts
2025-06-18 16:21:34 +08:00

126 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { RouteRecordRaw } from "vue-router";
import { constantRoutes } from "@/router";
import { store } from "@/store";
import router from "@/router";
import MenuAPI, { type RouteVO } from "@/api/system/menu.api";
const modules = import.meta.glob("../../views/**/**.vue");
const Layout = () => import("@/layouts/index.vue");
export const usePermissionStore = defineStore("permission", () => {
// 存储所有路由,包括静态路由和动态路由
const routes = ref<RouteRecordRaw[]>([]);
// 混合模式左侧菜单路由
const sideMenuRoutes = ref<RouteRecordRaw[]>([]);
// 路由是否加载完成
const routesLoaded = ref(false);
/**
* 获取后台动态路由数据,解析并注册到全局路由
*
* @returns Promise<RouteRecordRaw[]> 解析后的动态路由列表
*/
function generateRoutes() {
return new Promise<RouteRecordRaw[]>((resolve, reject) => {
MenuAPI.getRoutes()
.then((data) => {
const dynamicRoutes = parseDynamicRoutes(data);
routes.value = [...constantRoutes, ...dynamicRoutes];
routesLoaded.value = true;
resolve(dynamicRoutes);
})
.catch((error) => {
console.error("❌ Failed to generate routes:", error);
// 即使失败也要设置状态,避免无限重试
routesLoaded.value = false;
reject(error);
});
});
}
/**
* 根据父菜单路径设置侧边菜单
*
* @param parentPath 父菜单的路径,用于查找对应的菜单项
*/
const updateSideMenu = (parentPath: string) => {
const matchedItem = routes.value.find((item) => item.path === parentPath);
if (matchedItem && matchedItem.children) {
sideMenuRoutes.value = matchedItem.children;
}
};
/**
* 重置路由
*/
const resetRouter = () => {
// 创建常量路由名称集合用于O(1)时间复杂度的查找
const constantRouteNames = new Set(constantRoutes.map((route) => route.name).filter(Boolean));
// 从 router 实例中移除动态路由
routes.value.forEach((route) => {
if (route.name && !constantRouteNames.has(route.name)) {
router.removeRoute(route.name);
}
});
// 重置为仅包含常量路由
routes.value = [...constantRoutes];
sideMenuRoutes.value = [];
routesLoaded.value = false;
};
return {
routes,
sideMenuRoutes,
routesLoaded,
generateRoutes,
updateSideMenu,
resetRouter,
};
});
/**
* 解析后端返回的路由数据并转换为 Vue Router 兼容的路由配置
*
* @param rawRoutes 后端返回的原始路由数据
* @returns 解析后的路由集合
*/
const parseDynamicRoutes = (rawRoutes: RouteVO[]): RouteRecordRaw[] => {
const parsedRoutes: RouteRecordRaw[] = [];
rawRoutes.forEach((route) => {
const normalizedRoute = { ...route } as RouteRecordRaw;
// 处理组件路径
normalizedRoute.component =
normalizedRoute.component?.toString() === "Layout"
? Layout
: modules[`../../views/${normalizedRoute.component}.vue`] ||
modules["../../views/error-page/404.vue"];
// 递归解析子路由
if (normalizedRoute.children) {
normalizedRoute.children = parseDynamicRoutes(route.children);
}
parsedRoutes.push(normalizedRoute);
});
return parsedRoutes;
};
/**
* 导出此hook函数用于在非组件环境(如其他store、工具函数等)中获取权限store实例
*
* 在组件中可直接使用usePermissionStore()但在组件外部需要传入store实例
* 此函数简化了这个过程避免每次都手动传入store参数
*/
export function usePermissionStoreHook() {
return usePermissionStore(store);
}