library-picturebook-activity/frontend/src/router/index.ts

403 lines
12 KiB
TypeScript
Raw Normal View History

2025-11-23 14:04:20 +08:00
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: [
2026-01-08 09:17:46 +08:00
// 创建比赛路由(不需要在菜单中显示)
{
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"],
},
},
2025-11-23 14:04:20 +08:00
// 动态路由将在这里添加
],
},
{
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<void> {
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;