diff --git a/src/utils/menuRoute.ts b/src/utils/menuRoute.ts index 8d8569d..c5f994b 100644 --- a/src/utils/menuRoute.ts +++ b/src/utils/menuRoute.ts @@ -11,7 +11,7 @@ import routes from "@/router/routes"; * 全部落空时返回空结果并 console.warn,绝不返回未注册的原始路径。 */ -/** 路由 path 集合:重定向路由穿透到目标 path;带参数的路由(/f/:code、/app/:appCode)不参与匹配 */ +/** 路由 path 集合:重定向路由穿透到目标 path;带参数的路由(/f/:code、/app/:appCode)不进集合(/f/ 形态由 matchRoute 按填报契约识别) */ const ROUTE_PATHS = routes .map((r) => (typeof r.redirect === "string" ? r.redirect : r.path)) .filter((p): p is string => !!p && !p.includes(":")); @@ -82,6 +82,9 @@ function extractCandidate(raw: string): string { return p; } +/** 填报路由形态 /f/{code}(方案 §6.2 填报模块行的 path 契约)。code 段字符集同表单 code(字母/数字/下划线/中划线) */ +const FILL_ROUTE_RE = /^\/f\/[A-Za-z0-9_-]+$/; + /** 候选串 → 已注册路由 path;未命中返回 null */ function matchRoute(candidate: string): string | null { if (!candidate) return null; @@ -89,12 +92,16 @@ function matchRoute(candidate: string): string | null { // 1. 精确匹配(含重定向穿透后的目标路径) const exact = ROUTE_PATHS.find((p) => p.toLowerCase() === lower); if (exact) return exact; - // 2. 兼容带 ctx 前缀的配置(/form-center/admin/data → /admin/data) - // 不做通用后缀匹配,避免外部系统路径被误命中 + // 2. 兼容带 ctx 前缀的配置(/form-center/admin/data → /admin/data): + // 剥掉前缀后按同一条链重判(精确 → 填报形态),不做通用后缀匹配,避免外部系统路径被误命中 const withCtx = "/" + CTX + "/"; if (lower.startsWith(withCtx)) { - return ROUTE_PATHS.find((p) => p.toLowerCase() === lower.slice(withCtx.length - 1)) ?? null; + return matchRoute(candidate.slice(withCtx.length - 1)); } + // 3. 填报行 /f/{code}(P87):/f/:code 是带参数路由,不进 ROUTE_PATHS(含 : 被排除), + // 之前在这永远解析失败 → 菜单点了只弹「未配置有效页面路径」。按形态识别, + // 原样交回由注册的 /f/:code 吃参数——code 的大小写就是表单身份,不归一化 + if (FILL_ROUTE_RE.test(candidate)) return candidate; return null; }