From c0c06222be5f3be1129de194cedff60fea0cbec3 Mon Sep 17 00:00:00 2001 From: bootx Date: Wed, 24 Jun 2026 12:27:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(h5):=20=E5=BB=BA=E7=AB=8B=E5=8F=8C?= =?UTF-8?q?=E7=AB=AF=E8=B7=AF=E7=94=B1=E4=B8=89=E6=A1=B6=E7=BA=A6=E5=AE=9A?= =?UTF-8?q?=E4=B8=8E=E8=AE=BE=E5=A4=87=E7=8B=AC=E5=8D=A0=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 src/shared/router/paths.ts:RoutePath 常量,两端路由共用,杜绝 path 漂移 - 新增 src/shared/router/exclusive.ts:设备独占注册表 + otherDeviceExclusives 派生助手 - 两端路由分三桶:业务路由 → 对端独占提示存根(注册表派生)→ catch-all 404 - 新增 mobile/pc device-only 提示页:错设备打开独占页时提示而非 404 - 首页 path 改用 RoutePath.HOME;注册表初始为空,零副作用 --- src/mobile/router/index.ts | 11 +++++- src/mobile/router/modules.ts | 3 +- src/mobile/views/device-only.vue | 47 ++++++++++++++++++++++++ src/pc/router/index.ts | 14 ++++++- src/pc/views/device-only.vue | 63 ++++++++++++++++++++++++++++++++ src/shared/router/exclusive.ts | 33 +++++++++++++++++ src/shared/router/paths.ts | 18 +++++++++ 7 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 src/mobile/views/device-only.vue create mode 100644 src/pc/views/device-only.vue create mode 100644 src/shared/router/exclusive.ts create mode 100644 src/shared/router/paths.ts diff --git a/src/mobile/router/index.ts b/src/mobile/router/index.ts index 4e4409e..ecb7f95 100644 --- a/src/mobile/router/index.ts +++ b/src/mobile/router/index.ts @@ -1,6 +1,7 @@ import type { App } from 'vue' import type { RouteRecordRaw } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router' +import { otherDeviceExclusives } from '@/shared/router/exclusive' import { useRouteStoreWithOut } from '@/shared/store/modules/route' import { ErrorPageRoute } from './base' import routeModuleList from './modules' @@ -14,9 +15,17 @@ const routeStore = useRouteStoreWithOut() routeStore.setMenus(routeModuleList) routeStore.setRouters(constantRouter.concat(routeModuleList)) +// PC 独占路径:在移动端渲染"仅限电脑端"提示存根(由注册表派生,勿手写) +const pcOnlyStubs: RouteRecordRaw[] = otherDeviceExclusives('mobile').map(r => ({ + path: r.path, + name: `${r.name}__stub`, + component: () => import('@/mobile/views/device-only.vue'), +})) + const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes: constantRouter.concat(...routeModuleList), + // 顺序:业务路由 → PC 独占提示存根 → catch-all 404 + routes: [...routeModuleList, ...pcOnlyStubs, ErrorPageRoute], strict: true, scrollBehavior: () => ({ left: 0, top: 0 }), }) diff --git a/src/mobile/router/modules.ts b/src/mobile/router/modules.ts index 821222c..c8e47f3 100644 --- a/src/mobile/router/modules.ts +++ b/src/mobile/router/modules.ts @@ -1,4 +1,5 @@ import type { RouteRecordRaw } from 'vue-router' +import { RoutePath } from '@/shared/router/paths' const Layout = () => import('@/mobile/layout/index.vue') @@ -6,7 +7,7 @@ const Layout = () => import('@/mobile/layout/index.vue') const routeModuleList: Array = [ // 首页(根路径 /,与 PC 端首页地址统一) { - path: '/', + path: RoutePath.HOME, name: 'Home', component: Layout, meta: { diff --git a/src/mobile/views/device-only.vue b/src/mobile/views/device-only.vue new file mode 100644 index 0000000..50a73f5 --- /dev/null +++ b/src/mobile/views/device-only.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/pc/router/index.ts b/src/pc/router/index.ts index 4743538..e7a0ca4 100644 --- a/src/pc/router/index.ts +++ b/src/pc/router/index.ts @@ -1,15 +1,25 @@ import type { App } from 'vue' import type { RouteRecordRaw } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router' +import { otherDeviceExclusives } from '@/shared/router/exclusive' +import { RoutePath } from '@/shared/router/paths' + +// 移动独占路径:在 PC 端渲染"仅限手机端"提示存根(由注册表派生,勿手写) +const mobileOnlyStubs: RouteRecordRaw[] = otherDeviceExclusives('pc').map(r => ({ + path: r.path, + name: `${r.name}__stub`, + component: () => import('@/pc/views/device-only.vue'), +})) // PC 端路由树(独立于移动端,按需在此扩展业务路由,例如收银台 PC 版) -// 内页(收银台等)建议套 PcLayout;首页为全屏独立页,贴合移动端首页视觉 +// 顺序:真实路由 → 移动独占提示存根 → catch-all 404 const routes: RouteRecordRaw[] = [ { - path: '/', + path: RoutePath.HOME, name: 'PcHome', component: () => import('@/pc/views/Home.vue'), }, + ...mobileOnlyStubs, // 兜底:移动端专属路径(如 /home/index)或任何未匹配路径,渲染 PC 404 页 // 用 component 而非 redirect——vue-router 5 下 catch-all + redirect 在初始导航不触发 { diff --git a/src/pc/views/device-only.vue b/src/pc/views/device-only.vue new file mode 100644 index 0000000..cad3185 --- /dev/null +++ b/src/pc/views/device-only.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/shared/router/exclusive.ts b/src/shared/router/exclusive.ts new file mode 100644 index 0000000..e7672db --- /dev/null +++ b/src/shared/router/exclusive.ts @@ -0,0 +1,33 @@ +/** + * 设备独占路由注册表 + * + * 仅"一端"实现的业务页登记在此。两端路由器据此自动派生"对端提示存根": + * 当用户在错误设备打开独占页时(如手机打开 PC-only 页),渲染 device-only + * 提示页而非 404。 + * + * 约束:每项需保证其所属端路由器里有对应的"真实路由",否则该路径在本端 + * (所属端)反而会落 catch-all 变 404。 + * + * 新增时参照示例(取消注释,并确保所属端已注册真实路由): + * { path: RoutePath.MERCHANT_DASHBOARD, name: 'MerchantDashboard', device: 'pc' }, + * { path: RoutePath.WECHAT_BIND, name: 'WechatBind', device: 'mobile' }, + */ +export interface ExclusiveRoute { + /** 路由 path(引用 RoutePath 常量) */ + path: string + /** 路由 name(全端唯一;对端存根会用 `${name}__stub`) */ + name: string + /** 仅哪端实现 */ + device: 'pc' | 'mobile' +} + +// 设备独占业务页登记(暂无;新增时按上方示例添加) +export const EXCLUSIVE_ROUTES: ExclusiveRoute[] = [] + +/** + * 返回"非指定端"的独占路由,供该端生成 device-only 提示存根。 + * 例:forDevice='mobile' → 返回所有 device='pc' 的独占项(供移动端生成存根) + */ +export function otherDeviceExclusives(forDevice: 'pc' | 'mobile'): ExclusiveRoute[] { + return EXCLUSIVE_ROUTES.filter(r => r.device !== forDevice) +} diff --git a/src/shared/router/paths.ts b/src/shared/router/paths.ts new file mode 100644 index 0000000..c3a3d1d --- /dev/null +++ b/src/shared/router/paths.ts @@ -0,0 +1,18 @@ +/** + * 业务路由 path 常量 + * + * 两端路由(mobile/pc)注册路由时必须从此引用,杜绝 path 字面量漂移。 + * 跨端页(两端都实现)尤其关键:必须用同一常量保证 path 形态完全一致。 + * + * 注意:本常量仅用于路由注册(route record 的 path 字段)。 + * 导航时请用 router.push({ name, params }),不要手工拼接 path 字符串。 + * + * 新增业务页示例(按需取消注释并配合对应 view): + * 跨端页(如收银台): CASHIER: '/cashier/:id', + * PC 独占(如商户后台):MERCHANT_DASHBOARD: '/merchant/dashboard', + * 移动独占(如微信绑定):WECHAT_BIND: '/wechat/bind', + */ +export const RoutePath = { + /** 首页(跨端) */ + HOME: '/', +} as const