import routes from "@/router/routes"; /** * 平台模块配置 → 前端内部路由的统一解析器(对齐 NoticeFront @/utils/menuRoute)。 * 平台配置的 path 格式不可控(旧前缀 / index.html#/hash / 完整 URL / 配错), * 侧边栏跳转与高亮、登录后默认跳转都必须先经过这里,禁止手拼路由。 */ const ROUTE_PATHS: string[] = routes .map((r: any) => (typeof r.redirect === "string" ? r.redirect : r.path)) .filter((p: any): p is string => !!p && !String(p).includes(":")); const ROUTE_BY_MODID = new Map(); for (const r of routes as any[]) { const modId = r.meta?.modId as number | undefined; if (!modId || !r.path || String(r.path).includes(":") || r.redirect) continue; if (!ROUTE_BY_MODID.has(modId)) ROUTE_BY_MODID.set(modId, r.path); } const ROUTE_BY_TITLE = new Map(); for (const r of routes as any[]) { if (r.redirect || !r.path || String(r.path).includes(":")) continue; const title = r.meta?.title as string | undefined; if (title && !ROUTE_BY_TITLE.has(title)) ROUTE_BY_TITLE.set(title, r.path); } /** 平台历史模块名 → 路由(旧菜单叫法与现页面标题不一致时的对照) */ const NAME_ALIASES: Record = { 学制设置: "/studySystemChoice", 时段管理: "/itemSet", 学校管理: "/schoolSetting", 课程管理: "/subjectManForZy", 学科设置: "/subjectSetzhi", 班级列表: "/classManage", 选修班: "/electiveClassManage", 教师列表: "/teacherManage", 学生列表: "/studentManage", 日志查询: "/dailySearch", }; /** 本应用历史上下文前缀(/bschclass、/userclass、/BschClassSystem)→ 去掉后比对 */ const LEGACY_PREFIXES = ["/bschclass", "/userclass", "/bschclasssystem"]; const EXTERNAL_RE = /^(https?:)?\/\//i; function extractCandidate(raw: string): string { let p = raw.trim(); try { p = decodeURIComponent(p); } catch { /* 非法编码保持原样 */ } const hashIdx = p.indexOf("#"); if (hashIdx >= 0) { p = p.slice(hashIdx + 1); } else if (EXTERNAL_RE.test(p)) { try { p = new URL(p, "http://placeholder.local").pathname; } catch { /* 保持原样 */ } } const qIdx = p.indexOf("?"); if (qIdx >= 0) p = p.slice(0, qIdx); p = p.replace(/index\.html?$/i, ""); p = p.replace(/\/+$/, ""); if (p && !p.startsWith("/")) p = "/" + p; return p; } function matchRoute(candidate: string): string | null { if (!candidate) return null; const lower = candidate.toLowerCase(); const exact = ROUTE_PATHS.find((p: string) => p.toLowerCase() === lower); if (exact) return exact; for (const prefix of LEGACY_PREFIXES) { if (lower.startsWith(prefix + "/")) { const stripped = lower.slice(prefix.length); const hit = ROUTE_PATHS.find((p: string) => p.toLowerCase() === stripped); if (hit) return hit; } } return null; } function matchByName(name?: string | null): string | null { const n = String(name ?? "").trim(); if (!n) return null; const byTitle = ROUTE_BY_TITLE.get(n); if (byTitle) return byTitle; const alias = NAME_ALIASES[n]; if (alias) return alias; const hits = [...ROUTE_BY_TITLE.entries()].filter( ([title]) => title.length >= 2 && (n.includes(title) || title.includes(n)) ); return hits.length === 1 ? hits[0][1] : null; } function isAppEntryCandidate(candidate: string): boolean { if (!candidate || candidate === "/") return true; return LEGACY_PREFIXES.some(p => candidate === p); } export interface ResolvedMenuPage { path: string; external: boolean; } export const EMPTY_MENU_PAGE: ResolvedMenuPage = { path: "", external: false }; export function resolveMenuPage( page?: string | null, modId?: number | null, name?: string | null ): ResolvedMenuPage { const raw = String(page ?? "").trim(); const byName = (): string | null => matchByName(name); const byModId = (): string | null => (modId != null ? ROUTE_BY_MODID.get(modId) ?? null : null); if (raw) { const candidate = extractCandidate(raw); const byPath = matchRoute(candidate); if (byPath) return { path: byPath, external: false }; if (EXTERNAL_RE.test(raw) && !raw.includes("#")) { if (!isAppEntryCandidate(candidate)) { return { path: raw, external: true }; } } const fallback = byName() ?? byModId(); if (fallback) return { path: fallback, external: false }; console.warn( "[menuRoute] 模块路径/名称/modId 均无法匹配前端路由,请检查基础平台模块配置:", raw, "modId =", modId, "name =", name ); return EMPTY_MENU_PAGE; } const fallback = byName() ?? byModId(); if (fallback) return { path: fallback, external: false }; console.warn("[menuRoute] 模块未配置路径且名称/modId 无法识别:", name, "modId =", modId); return EMPTY_MENU_PAGE; }