From f630c3aa0f9b535bfc95022106a90ac8f702c541 Mon Sep 17 00:00:00 2001 From: chenxing Date: Thu, 15 Oct 2026 21:03:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(p87-menu):=20=E5=A1=AB=E6=8A=A5=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E8=A1=8C=20/f/{code}=20=E8=A7=A3=E6=9E=90=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E8=87=B4=E8=8F=9C=E5=8D=95=E7=82=B9=E5=87=BB=E6=8A=A5?= =?UTF-8?q?=E3=80=8C=E6=9C=AA=E9=85=8D=E7=BD=AE=E6=9C=89=E6=95=88=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E8=B7=AF=E5=BE=84=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根因:resolveMenuPage 的 ROUTE_PATHS 构建时排除带参数路由(含 : ), /f/:code 永远不在精确匹配集;填报行的文档形态 path=/f/{code} (方案 §6.2 / H2 登记脚本)任何写法都解析不出页面,点击只弹告警。 T8 快应用桩的 /f/drill-honour 行同样失败,演练轮未点过该行未暴露。 - 修复:matchRoute 增补第 3 判据——形态识别 /f/{code}(code 字符集同表单 code:字母/数字/下划线/中划线),ctx 前缀剥除后复用同一条链;命中原样 交回由注册的 /f/:code 吃参数,code 大小写即表单身份不归一化。 - 附带生效面:layout 侧边栏填报行可点;AppContext 单表单直落 (isSingleForm 判 navItems[0].page 形如 /f/)与轻导航填报跳转恢复可用。 - 验证:esbuild 打包真实模块跑断言 15/0(四种配置形态 + 六项回归 + 四项仍须拒绝形态);vue-tsc --noEmit 干净、vite build 通过。 --- src/utils/menuRoute.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) 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; }