From be19ed066ac19d53209a9d44e127819891cdc1a1 Mon Sep 17 00:00:00 2001 From: chenxing Date: Sat, 19 Sep 2026 17:47:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(walkthrough):=20menuRoute=20=E8=AF=86?= =?UTF-8?q?=E5=88=AB=20/sch/{code}=20=E5=A3=B3=E8=B7=AF=E5=BE=84=E5=85=8D?= =?UTF-8?q?=E5=9B=9E=E9=80=80=E5=91=8A=E8=AD=A6=EF=BC=9B=E6=A0=8F=E7=9B=AE?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E6=8C=89=E9=92=AE=20type=3Dlink=20=E6=94=B9?= =?UTF-8?q?=20text=EF=BC=9Bmock=20=E8=A1=A5=20cherry-bloom/violet-iris=20?= =?UTF-8?q?=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/data.ts | 4 +-- mock/index.ts | 28 +++++++++++++++++ src/utils/menuRoute.spec.ts | 54 ++++++++++++++++++++++++++++++++ src/utils/menuRoute.ts | 6 +++- src/views/cms/CategoryManage.vue | 2 +- 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/utils/menuRoute.spec.ts diff --git a/mock/data.ts b/mock/data.ts index 30cd7e1..e4c3228 100644 --- a/mock/data.ts +++ b/mock/data.ts @@ -231,7 +231,7 @@ export function seedDb(): MockDb { schId: 1001, schName: "云海大学", name: "机电学院", - theme: "default", + theme: "cherry-bloom", configJson: JSON.stringify({ public_base_url: "http://www.yunhai-demo.edu.cn/college/jdx", copyright: "机电学院 · Mock 数据", @@ -247,7 +247,7 @@ export function seedDb(): MockDb { schId: 1001, schName: "云海大学", name: "信息工程学院", - theme: "default", + theme: "violet-iris", configJson: JSON.stringify({ public_base_url: "http://www.yunhai-demo.edu.cn/college/xxgc", copyright: "信息工程学院 · Mock 数据", diff --git a/mock/index.ts b/mock/index.ts index 6fdce6b..55f445a 100644 --- a/mock/index.ts +++ b/mock/index.ts @@ -103,6 +103,34 @@ const THEMES: MockTheme[] = [ { key: "bg", cssVar: "--bg", label: "页面底色", type: "color", defaultValue: "#fff8f0" }, ], }, + { + name: "cherry-bloom", + title: "樱语粉韵", + description: "温柔樱粉风门户:樱花粉品牌色 + 大圆角 16px 卡片网格列表,适合幼儿园与小学。", + version: "1.0.0", + author: "yunhai", + bannerSize: "1920x560", + screenshotUrl: "/api/theme/cherry-bloom/asset/screenshot.png", + params: [ + { key: "primary", cssVar: "--primary", label: "主色", type: "color", defaultValue: "#DB5A8B" }, + { key: "primaryDark", cssVar: "--primary-dark", label: "主色加深", type: "color", defaultValue: "#B23A68" }, + { key: "bg", cssVar: "--bg", label: "页面底色", type: "color", defaultValue: "#FFFAFB" }, + ], + }, + { + name: "violet-iris", + title: "鸢尾紫韵", + description: "雅致鸢尾紫门户:沉静书卷气 + 圆角 12px 卡片网格列表,适合高校与教科研机构。", + version: "1.0.0", + author: "yunhai", + bannerSize: "1920x560", + screenshotUrl: "/api/theme/violet-iris/asset/screenshot.png", + params: [ + { key: "primary", cssVar: "--primary", label: "主色", type: "color", defaultValue: "#6552D9" }, + { key: "primaryDark", cssVar: "--primary-dark", label: "主色加深", type: "color", defaultValue: "#4A39B0" }, + { key: "bg", cssVar: "--bg", label: "页面底色", type: "color", defaultValue: "#F8F7FD" }, + ], + }, ]; function themeByName(name: string): MockTheme | undefined { diff --git a/src/utils/menuRoute.spec.ts b/src/utils/menuRoute.spec.ts new file mode 100644 index 0000000..5fa1e06 --- /dev/null +++ b/src/utils/menuRoute.spec.ts @@ -0,0 +1,54 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("@/store", () => ({ + useCmsStore: () => ({ schoolCode: "demo" }), +})); + +import { resolveMenuPage } from "./menuRoute"; + +describe("resolveMenuPage 学校壳具体路径匹配", () => { + let warn: ReturnType; + beforeEach(() => { + warn = vi.spyOn(console, "warn").mockImplementation(() => {}); + }); + + it("/sch/{code}/article 直接命中,不回退名称识别、不告警", () => { + expect(resolveMenuPage("/sch/demo/article", null, "文章列表")).toEqual({ + path: "/sch/demo/article", + external: false, + }); + expect(warn).not.toHaveBeenCalled(); + }); + + it("他校编码的壳路径重映射到当前学校上下文", () => { + expect(resolveMenuPage("/sch/fsz/category", null, "栏目管理")).toEqual({ + path: "/sch/demo/category", + external: false, + }); + expect(warn).not.toHaveBeenCalled(); + }); + + it("裸子路径 /article 仍命中学校壳路由", () => { + expect(resolveMenuPage("/article", null, "文章列表")).toEqual({ + path: "/sch/demo/article", + external: false, + }); + expect(warn).not.toHaveBeenCalled(); + }); + + it("顶层绝对路由不受影响(/workbench)", () => { + expect(resolveMenuPage("/workbench", null, "工作台")).toEqual({ + path: "/workbench", + external: false, + }); + expect(warn).not.toHaveBeenCalled(); + }); + + it("配错路径仍走名称回退(行为保持)", () => { + expect(resolveMenuPage("/no-such-page", null, "友情链接")).toEqual({ + path: "/sch/demo/link", + external: false, + }); + expect(warn).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/utils/menuRoute.ts b/src/utils/menuRoute.ts index 9e2ffca..565cf64 100644 --- a/src/utils/menuRoute.ts +++ b/src/utils/menuRoute.ts @@ -119,7 +119,11 @@ function matchRoute(candidate: string): string | null { // 2. 学校壳内子路径匹配(/article → /sch/{code}/article),兼容新旧上下文前缀(历史 /cms、现网 /yschcms) const stripped = lower.startsWith("/cms/") ? lower.slice("/cms".length) : lower.startsWith("/yschcms/") ? lower.slice("/yschcms".length) : lower; - const entry = ROUTE_ENTRIES.find(e => !e.absolute && e.sub.toLowerCase() === stripped); + // 平台侧已配成具体壳路径(/sch/{任意编码}/article):剥壳后按子路径命中, + // 运行时经 toConcrete 重映射到当前学校上下文(他校编码不直接透传) + const deshelled = /^\/sch\/[^/]+(?=\/)/.test(stripped) ? stripped.replace(/^\/sch\/[^/]+/, "") : stripped; + const entry = ROUTE_ENTRIES.find(e => !e.absolute && + (e.sub.toLowerCase() === stripped || e.sub.toLowerCase() === deshelled)); if (entry) { return toConcrete(entry); } diff --git a/src/views/cms/CategoryManage.vue b/src/views/cms/CategoryManage.vue index 73588ff..4dd27c4 100644 --- a/src/views/cms/CategoryManage.vue +++ b/src/views/cms/CategoryManage.vue @@ -563,7 +563,7 @@ function openLink(url: string) {