mirror of
https://gitee.com/bootx/dax-pay-h5
synced 2026-08-06 05:16:25 +08:00
feat(h5): 建立双端路由三桶约定与设备独占提示机制
- 新增 src/shared/router/paths.ts:RoutePath 常量,两端路由共用,杜绝 path 漂移 - 新增 src/shared/router/exclusive.ts:设备独占注册表 + otherDeviceExclusives 派生助手 - 两端路由分三桶:业务路由 → 对端独占提示存根(注册表派生)→ catch-all 404 - 新增 mobile/pc device-only 提示页:错设备打开独占页时提示而非 404 - 首页 path 改用 RoutePath.HOME;注册表初始为空,零副作用
This commit is contained in:
@@ -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 }),
|
||||
})
|
||||
|
||||
@@ -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<RouteRecordRaw> = [
|
||||
// 首页(根路径 /,与 PC 端首页地址统一)
|
||||
{
|
||||
path: '/',
|
||||
path: RoutePath.HOME,
|
||||
name: 'Home',
|
||||
component: Layout,
|
||||
meta: {
|
||||
|
||||
47
src/mobile/views/device-only.vue
Normal file
47
src/mobile/views/device-only.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'MobileDeviceOnly' })
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function goHome() {
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="device-only">
|
||||
<p class="device-only__title">
|
||||
此页面仅限电脑端访问
|
||||
</p>
|
||||
<p class="device-only__desc">
|
||||
请在电脑浏览器中打开本页面
|
||||
</p>
|
||||
<van-button type="primary" @click="goHome">
|
||||
返回首页
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.device-only {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.device-only__title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.device-only__desc {
|
||||
margin: 0 0 24px;
|
||||
font-size: 14px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
@@ -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 在初始导航不触发
|
||||
{
|
||||
|
||||
63
src/pc/views/device-only.vue
Normal file
63
src/pc/views/device-only.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'PcDeviceOnly' })
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function goHome() {
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pc-device-only">
|
||||
<p class="pc-device-only__title">
|
||||
此页面仅限手机端访问
|
||||
</p>
|
||||
<p class="pc-device-only__desc">
|
||||
请用手机扫描二维码,或在手机浏览器中打开本页面
|
||||
</p>
|
||||
<button class="pc-device-only__btn" type="button" @click="goHome">
|
||||
返回首页
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* PC 端:scoped 原生 px + 媒体查询,不使用 UnoCSS 的 px 原子类 */
|
||||
.pc-device-only {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
|
||||
.pc-device-only__title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
}
|
||||
|
||||
.pc-device-only__desc {
|
||||
margin: 0 0 32px;
|
||||
font-size: 14px;
|
||||
color: #4e5969;
|
||||
}
|
||||
|
||||
.pc-device-only__btn {
|
||||
padding: 10px 28px;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
background: #5d9dfe;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.pc-device-only__btn:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
</style>
|
||||
33
src/shared/router/exclusive.ts
Normal file
33
src/shared/router/exclusive.ts
Normal file
@@ -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)
|
||||
}
|
||||
18
src/shared/router/paths.ts
Normal file
18
src/shared/router/paths.ts
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user