import { createRouter, createWebHistory } from "vue-router"; import type { RouteRecordRaw } from "vue-router"; import { nextTick } from "vue"; import { useAuthStore } from "@/stores/auth"; import { convertMenusToRoutes } from "@/utils/menu"; import "@/types/router"; // 基础路由(不需要动态加载的) const baseRoutes: RouteRecordRaw[] = [ { path: "/:tenantCode/login", name: "Login", component: () => import("@/views/auth/Login.vue"), meta: { requiresAuth: false }, }, { path: "/login", name: "LoginFallback", component: () => import("@/views/auth/Login.vue"), meta: { requiresAuth: false }, }, { path: "/:tenantCode", name: "Main", component: () => import("@/layouts/BasicLayout.vue"), redirect: (to) => { return { path: `/${to.params.tenantCode}/workbench` }; }, meta: {}, children: [ // 创建比赛路由(不需要在菜单中显示) { path: "contests/create", name: "ContestsCreate", component: () => import("@/views/contests/Create.vue"), meta: { title: "创建比赛", requiresAuth: true, permissions: ["contest:create"], }, }, // 赛事详情路由(不需要在菜单中显示) { path: "contests/:id", name: "ContestsDetail", component: () => import("@/views/contests/Detail.vue"), meta: { title: "赛事详情", requiresAuth: true, permissions: ["contest:read"], }, }, // 编辑比赛路由(不需要在菜单中显示) { path: "contests/:id/edit", name: "ContestsEdit", component: () => import("@/views/contests/Create.vue"), meta: { title: "编辑比赛", requiresAuth: true, permissions: ["contest:update"], }, }, // 赛事评委管理路由(不需要在菜单中显示) { path: "contests/:id/judges", name: "ContestsJudges", component: () => import("@/views/contests/judges/Index.vue"), meta: { title: "评委管理", requiresAuth: true, permissions: ["contest:read"], }, }, // 动态路由将在这里添加 ], }, { path: "/403", name: "Forbidden", component: () => import("@/views/error/403.vue"), meta: { requiresAuth: false }, }, { path: "/:pathMatch(.*)*", name: "NotFound", component: () => import("@/views/error/404.vue"), }, ]; const router = createRouter({ history: createWebHistory(), routes: baseRoutes, }); // 标记是否已经添加了动态路由 let dynamicRoutesAdded = false; /** * 添加动态路由 * @returns Promise,当路由添加完成并生效后 resolve */ async function addDynamicRoutes(): Promise { if (dynamicRoutesAdded) return; const authStore = useAuthStore(); if (!authStore.menus || authStore.menus.length === 0) return; // 将菜单转换为路由 const dynamicRoutes = convertMenusToRoutes(authStore.menus); // 添加动态路由到根路由下 dynamicRoutes.forEach((route) => { router.addRoute("Main", route); }); dynamicRoutesAdded = true; // 等待下一个 tick,确保路由已完全注册 await nextTick(); // 额外等待一个 tick,确保路由系统完全更新 await nextTick(); } /** * 从路径中提取租户编码 */ function extractTenantCodeFromPath(path: string): string | null { const match = path.match(/^\/([^/]+)/); return match ? match[1] : null; } /** * 构建带租户编码的路径 */ function buildPathWithTenantCode(tenantCode: string, path: string): string { // 如果路径已经包含租户编码,直接返回 if (path.startsWith(`/${tenantCode}/`)) { return path; } // 移除开头的斜杠(如果有) const cleanPath = path.startsWith("/") ? path.slice(1) : path; // 如果路径是根路径,返回租户编码路径 if (cleanPath === "" || cleanPath === tenantCode) { return `/${tenantCode}/workbench`; } return `/${tenantCode}/${cleanPath}`; } router.beforeEach(async (to, _from, next) => { console.log("to -----", to); const authStore = useAuthStore(); // 从URL中提取租户编码 const tenantCodeFromUrl = extractTenantCodeFromPath(to.path); // 如果 token 存在但用户信息不存在,先获取用户信息 if (authStore.token && !authStore.user) { try { const userInfo = await authStore.fetchUserInfo(); // 如果获取用户信息失败或用户信息为空,跳转到登录页 if (!userInfo) { authStore.logout(); const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } return; } // 获取用户信息后,检查租户编码一致性 const userTenantCode = userInfo?.tenantCode; if (userTenantCode) { // 如果URL中的租户编码与用户信息不一致,更正URL if (tenantCodeFromUrl && tenantCodeFromUrl !== userTenantCode) { const correctedPath = buildPathWithTenantCode( userTenantCode, to.path.replace(`/${tenantCodeFromUrl}`, "") ); next({ path: correctedPath, replace: true }); return; } // 如果URL中没有租户编码,添加租户编码 if (!tenantCodeFromUrl) { const correctedPath = buildPathWithTenantCode( userTenantCode, to.path ); next({ path: correctedPath, replace: true }); return; } } // 获取用户信息后,添加动态路由并等待生效 await addDynamicRoutes(); // 保存原始目标路径 const targetPath = to.fullPath; // 路由已生效,重新解析目标路由 const resolved = router.resolve(targetPath); console.log("resolved -----", resolved); // 如果解析后的路由不是404,说明路由存在,重新导航 if (resolved.name !== "NotFound") { next({ path: targetPath, replace: true }); } else { // 如果路由不存在,但需要认证,跳转到登录页(而不是404) if (to.meta.requiresAuth === false) { // 路由确实不存在,允许继续(会显示404页面) next(); } else { const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } } } return; } catch (error) { // 获取失败,清除 token 并跳转到登录页 console.error("获取用户信息失败:", error); authStore.logout(); const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } return; } } // 如果 token 不存在,但需要认证,跳转到登录页 if (!authStore.token && to.meta.requiresAuth !== false) { const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } return; } // 如果已登录,检查租户编码一致性 if (authStore.isAuthenticated && authStore.user) { const userTenantCode = authStore.user.tenantCode; if (userTenantCode) { // 如果URL中的租户编码与用户信息不一致,更正URL if (tenantCodeFromUrl && tenantCodeFromUrl !== userTenantCode) { const correctedPath = buildPathWithTenantCode( userTenantCode, to.path.replace(`/${tenantCodeFromUrl}`, "") ); next({ path: correctedPath, replace: true }); return; } // 如果URL中没有租户编码,添加租户编码 if (!tenantCodeFromUrl && to.path !== "/login") { const correctedPath = buildPathWithTenantCode(userTenantCode, to.path); next({ path: correctedPath, replace: true }); return; } } } // 如果已登录且有菜单数据,但动态路由未添加,则添加 if ( authStore.isAuthenticated && authStore.menus.length > 0 && !dynamicRoutesAdded ) { // 添加动态路由并等待生效 await addDynamicRoutes(); // 保存原始目标路径 const targetPath = to.fullPath; // 路由已生效,重新解析目标路由 const resolved = router.resolve(targetPath); // 如果解析后的路由不是404,说明路由存在,重新导航 if (resolved.name !== "NotFound") { next({ path: targetPath, replace: true }); } else { // 如果路由不存在,但需要认证,跳转到登录页(而不是404) if (to.meta.requiresAuth !== false) { const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } } else { // 路由确实不存在,允许继续(会显示404页面) next(); } } return; } // 检查是否需要认证 if (to.meta.requiresAuth !== false) { // 如果没有 token,跳转到登录页 if (!authStore.token) { const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } return; } // 如果有 token 但没有用户信息,跳转到登录页 if (!authStore.user) { const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path); if (tenantCode) { next({ path: `/${tenantCode}/login`, query: { redirect: to.fullPath }, }); } else { next({ name: "LoginFallback", query: { redirect: to.fullPath } }); } return; } } // 如果已登录,访问登录页则重定向到首页 if ( (to.name === "Login" || to.name === "LoginFallback") && authStore.isAuthenticated ) { // 确保动态路由已添加并等待生效 if (!dynamicRoutesAdded && authStore.menus.length > 0) { await addDynamicRoutes(); } // 重定向到带租户编码的工作台 const userTenantCode = authStore.user?.tenantCode || "default"; next({ path: `/${userTenantCode}/workbench` }); return; } // 处理登录页面的租户编码 if (to.name === "LoginFallback" && !tenantCodeFromUrl) { // 如果访问的是 /login,但没有租户编码,检查是否有用户信息中的租户编码 if (authStore.isAuthenticated && authStore.user?.tenantCode) { const userTenantCode = authStore.user.tenantCode; next({ path: `/${userTenantCode}/login`, replace: true }); return; } // 如果没有租户编码,允许访问(会显示租户输入框) next(); return; } // 检查角色权限 const requiredRoles = to.meta.roles; if (requiredRoles && requiredRoles.length > 0) { if (!authStore.hasAnyRole(requiredRoles)) { // 没有所需角色,跳转到 403 页面 next({ name: "Forbidden" }); return; } } // 检查权限 const requiredPermissions = to.meta.permissions; if (requiredPermissions && requiredPermissions.length > 0) { if (!authStore.hasAnyPermission(requiredPermissions)) { // 没有所需权限,跳转到 403 页面 next({ name: "Forbidden" }); return; } } next(); }); export default router;