Files
mversion-ui/src/router/index.ts
2026-01-07 18:59:34 +08:00

285 lines
7.1 KiB
TypeScript

import {
createWebHistory,
createRouter,
type RouteRecordRaw,
} from "vue-router";
import { useUserStore } from "@/store";
import { getRouteMenus } from "@/api";
import TokenManager from "@/utils/storage";
import Login from "@/pages/Login/index.vue";
import HomeView from "@/pages/Layout/index.vue";
const tokenManager = TokenManager.getInstance();
// 基础路由(不需要权限验证)
const constantRoutes: RouteRecordRaw[] = [
{
path: "/login",
name: "Login",
component: Login,
meta: {
title: "登录",
requiresAuth: false,
},
},
];
// 动态路由(需要权限验证)
const asyncRoutes: RouteRecordRaw[] = [
{
path: "/",
name: "Layout",
component: HomeView,
// redirect: '/home',
meta: {
requiresAuth: true,
},
children: [] as RouteRecordRaw[],
},
];
const router = createRouter({
history: createWebHistory(),
routes: [...constantRoutes, ...asyncRoutes],
});
// 白名单路由(不需要登录即可访问)
const whiteList = ["/login"];
// 1. 预先加载所有可能的页面组件
// 这里的路径模式要覆盖到你所有的业务组件
const modules = import.meta.glob("@/pages/**/*.vue");
const loadComponent = (componentPath: string) => {
if (!componentPath) return null;
// 统一路径格式处理
let fullPath = "";
if (componentPath.startsWith("@/")) {
fullPath = componentPath;
} else if (componentPath.includes("/")) {
// 补全路径,确保以 @/pages 开头
fullPath = componentPath.startsWith("pages/")
? `@/${componentPath}`
: `@/pages/${componentPath}/index.vue`;
} else {
// 补全 index.vue
fullPath = `@/pages/${componentPath}/index.vue`;
}
// 重点:将 @/ 转换为 /src/,因为 glob 默认生成的 key 是相对于项目根目录的
const key = fullPath.replace("@/", "/src/");
// 从 modules 中查找对应的导入函数
if (modules[key]) {
return modules[key];
} else {
console.error(`未找到组件文件: ${key}. 请检查路径或大小写。`);
return null;
}
};
// 将后端返回的路由数据转换为 Vue Router 路由
const transformRoutes = (
routes: any[],
parentCode: string = ""
): RouteRecordRaw[] => {
console.log("transformRoutes", routes);
return routes.flatMap((route) => {
const fullCode = parentCode ? `${parentCode}/${route.code}` : route.code;
// 如果当前路由有子路由,说明它是一个路由前缀,不需要组件
if (route.children && route.children.length > 0) {
const childRoutes = transformRoutes(route.children, fullCode);
if (route.code) {
// 创建一个父路由,将子路由作为其 children
const parentRoute: RouteRecordRaw = {
path: `/${fullCode}`,
name: route.code,
meta: {
title: route.name,
icon: route.icon,
...route.meta,
},
children: childRoutes,
};
return parentRoute;
} else {
return childRoutes;
}
} else {
// 叶子节点才需要组件和路由配置
const component = fullCode ? loadComponent(fullCode) : undefined;
const routeRecord: any = {
path: route.code,
name: route.code,
meta: {
title: route.name,
icon: route.icon,
...route.meta,
},
};
if (component) {
routeRecord.component = component;
}
return routeRecord as RouteRecordRaw;
}
});
};
// 添加动态路由
const addDynamicRoutes = async () => {
const userStore = useUserStore();
// 如果路由已加载,直接返回
if (userStore.isRoutesLoaded) {
return;
}
try {
// 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
let allRoutes:any[] = [];
if (userStore.isBackendUser) {
// const backendResponse = await getRouteMenus();
const backendResponse = [
{
"name": "字典管理",
"code": "dict",
"icon": "OfficeBuilding",
"metadata": null,
"children": null
},
{
"name": "组织管理",
"code": "origanization",
"icon": "OfficeBuilding",
"metadata": null,
"children": null
},
{
"name": "人员管理",
"code": "personnel",
"icon": "OfficeBuilding",
"metadata": null,
"children": null
},
{
"name": "权限管理",
"code": "permission",
"icon": "OfficeBuilding",
"metadata": null,
"children": null
},
{
"name": "流程管理",
"code": "flow",
"icon": "OfficeBuilding",
"metadata": null,
"children": null
}
];
allRoutes = [
{
code: "stage",
name: "管理中心",
icon: "",
meta:{
title:'管理中心'
},
children: backendResponse,
},
];
} else {
// TODO:获取用户端的数据信息
// response = await getUserMenus();
allRoutes = [];
}
if (allRoutes) {
// 转换路由数据
const dynamicRoutes = transformRoutes(
Array.isArray(allRoutes) ? allRoutes : [allRoutes]
);
// 将动态路由添加到 Layout 的 children 中
const layoutRoute = router
.getRoutes()
.find((route) => route.name === "Layout");
if (layoutRoute) {
dynamicRoutes.forEach((route) => {
router.addRoute("Layout", route);
});
} else {
// 如果找不到 Layout 路由,直接添加到根路由
dynamicRoutes.forEach((route) => {
router.addRoute(route);
});
}
// 保存路由数据到 store
userStore.setRoutes(dynamicRoutes);
// 标记路由已加载
userStore.isRoutesLoaded = true;
}
} catch (error) {
// 如果获取路由失败,清除用户数据并跳转到登录页
userStore.clearUserData();
router.push("/login");
}
};
// 路由导航守卫
router.beforeEach(async (to, _from, next) => {
const userStore = useUserStore();
const accessToken = tokenManager.getToken("accessToken");
// 获取 token
const token = accessToken || userStore.token;
// 如果已登录,更新 store 中的 token
if (token) {
userStore.setToken(token);
}
// 判断是否在白名单中
if (whiteList.includes(to.path)) {
// 如果在白名单中且已登录,重定向到首页
if (token) {
next("/");
} else {
next();
}
return;
}
// 需要登录验证
if (!token) {
// 未登录,重定向到登录页
next({
path: "/login",
query: { redirect: to.fullPath }, // 保存当前路径,登录后可以跳转回来
});
return;
}
// 已登录,检查路由是否已加载
if (!userStore.isRoutesLoaded) {
try {
// 加载动态路由
await addDynamicRoutes();
// 路由加载完成后,重新导航到目标路由
next({ ...to, replace: true });
} catch (error) {
console.error("Route loading error:", error);
next("/login");
}
} else {
// 路由已加载,直接放行
next();
}
});
export default router;