feat(codegen): 代码生成支持本地写入和代码优化

This commit is contained in:
Ray.Hao
2025-08-21 18:28:38 +08:00
parent 72b89e2894
commit 07206576a8
61 changed files with 1045 additions and 1087 deletions

View File

@@ -0,0 +1,160 @@
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 mixLayoutSideMenus = ref<RouteRecordRaw[]>([]);
// 动态路由是否已生成
const isDynamicRoutesGenerated = ref(false);
const allCacheRoutes = ref<string[][]>([]);
/**
* 生成动态路由
*/
async function generateRoutes(): Promise<RouteRecordRaw[]> {
try {
const data = await MenuAPI.getRoutes(); // 获取当前登录人拥有的菜单路由
const dynamicRoutes = parseDynamicRoutes(data);
routes.value = [...constantRoutes, ...dynamicRoutes];
setAllCacheRoutes(routes.value);
isDynamicRoutesGenerated.value = true;
return dynamicRoutes;
} catch (error) {
console.error("❌ Failed to generate routes:", error);
isDynamicRoutesGenerated.value = false;
throw error;
}
}
/**
* 设置混合布局的左侧菜单
*/
const setMixLayoutSideMenus = (parentPath: string) => {
const parentMenu = routes.value.find((item) => item.path === parentPath);
mixLayoutSideMenus.value = parentMenu?.children || [];
};
/**
* 重置路由状态
*/
const resetRouter = () => {
// 移除动态路由
const constantRouteNames = new Set(constantRoutes.map((route) => route.name).filter(Boolean));
routes.value.forEach((route) => {
if (route.name && !constantRouteNames.has(route.name)) {
router.removeRoute(route.name);
}
});
// 重置状态
routes.value = [...constantRoutes];
mixLayoutSideMenus.value = [];
isDynamicRoutesGenerated.value = false;
};
/**
* 获取所有的缓存路由
* @param userRoutes 用户路由配置
*/
const setAllCacheRoutes = (userRoutes: RouteRecordRaw[]) => {
if (!userRoutes?.length) {
allCacheRoutes.value = [];
return;
}
const result: string[][] = [];
userRoutes.forEach((route) => {
if (route.children?.length) {
traverseRoutes(route.children, [], result);
}
});
allCacheRoutes.value = result;
};
return {
routes,
mixLayoutSideMenus,
isDynamicRoutesGenerated,
allCacheRoutes,
generateRoutes,
setMixLayoutSideMenus,
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;
};
/**
* 遍历路由树收集缓存路由
* @param nodes 路由节点
* @param path 当前路径
* @param result 结果数组
*/
const traverseRoutes = (nodes: RouteRecordRaw[], path: string[], result: string[][]) => {
nodes.forEach((node) => {
const newPath: string[] = node.name ? [...path, String(node.name)] : [...path];
// 叶子节点且需要缓存
if (!node.children?.length && node.meta?.keepAlive) {
result.push(newPath);
}
// 递归处理子节点
if (node.children?.length) {
traverseRoutes(node.children, newPath, result);
}
});
};
/**
* 导出此hook函数用于在非组件环境(如其他store、工具函数等)中获取权限store实例
*
* 在组件中可直接使用usePermissionStore()但在组件外部需要传入store实例
* 此函数简化了这个过程避免每次都手动传入store参数
*/
export function usePermissionStoreHook() {
return usePermissionStore(store);
}