diff --git a/src/mobile/router/modules.ts b/src/mobile/router/modules.ts index 892a89d..4c56dcc 100644 --- a/src/mobile/router/modules.ts +++ b/src/mobile/router/modules.ts @@ -100,82 +100,49 @@ const routeModuleList: Array = [ }, ], }, - // 码牌支付-微信端(静态段须在 /h/:code 之前注册) + // 码牌支付:入口 + 各宿主环境页 统一在 CodePay 父级下作为子路由 + // 让 App.vue 顶层 transition key(matched[0].name)在 entry → 环境页跳转时保持不变(都是 CodePay), + // 避免整页 remount 与 Entry 的 onBeforeMount router.replace 竞争导致白屏 + // (曾发生在支付宝 webview:跨顶层 replace 与 的 enter 死锁, + // 与聚合 Aggregate 同源 bug,修复范式见 commits da82489 / 8138d93) + // 注意:静态段(wechat/alipay/union-pay/douyin)必须排在 :code 之前,否则会被当 code 吞掉 + // URL 契约不变,仍是 /h/:code、/h/{env}/:code { - path: RoutePath.CODE_PAY_WECHAT, - name: 'CodePayWechat', - component: Layout, - meta: { - title: 'route.codePay', // 码牌支付 - }, - children: [ - { - path: '', - name: 'CodePayWechatPage', - component: () => import('@/mobile/views/code-pay/wechat/index.vue'), - }, - ], - }, - // 码牌支付-支付宝端 - { - path: RoutePath.CODE_PAY_ALIPAY, - name: 'CodePayAlipay', - component: Layout, - meta: { - title: 'route.codePay', // 码牌支付 - }, - children: [ - { - path: '', - name: 'CodePayAlipayPage', - component: () => import('@/mobile/views/code-pay/alipay/index.vue'), - }, - ], - }, - // 码牌支付-云闪付端(静态段须在 /h/:code 之前注册) - { - path: RoutePath.CODE_PAY_UNION, - name: 'CodePayUnion', - component: Layout, - meta: { - title: 'route.codePay', // 码牌支付 - }, - children: [ - { - path: '', - name: 'CodePayUnionPage', - component: () => import('@/mobile/views/code-pay/union-pay/index.vue'), - }, - ], - }, - // 码牌支付-抖音端 - { - path: RoutePath.CODE_PAY_DOUYIN, - name: 'CodePayDouyin', - component: Layout, - meta: { - title: 'route.codePay', // 码牌支付 - }, - children: [ - { - path: '', - name: 'CodePayDouyinPage', - component: () => import('@/mobile/views/code-pay/douyin/index.vue'), - }, - ], - }, - // 码牌支付入口分发(跨端:PC 为扫码引导页) - { - path: RoutePath.CODE_PAY, + path: RoutePath.CODE_PAY_GROUP, name: 'CodePay', component: Layout, meta: { title: 'route.codePay', // 码牌支付 }, children: [ + // 微信环境页 { - path: '', - name: 'CodePayPage', + path: 'wechat/:code', + name: 'CodePayWechatPage', + component: () => import('@/mobile/views/code-pay/wechat/index.vue'), + }, + // 支付宝环境页 + { + path: 'alipay/:code', + name: 'CodePayAlipayPage', + component: () => import('@/mobile/views/code-pay/alipay/index.vue'), + }, + // 云闪付环境页 + { + path: 'union-pay/:code', + name: 'CodePayUnionPage', + component: () => import('@/mobile/views/code-pay/union-pay/index.vue'), + }, + // 抖音环境页 + { + path: 'douyin/:code', + name: 'CodePayDouyinPage', + component: () => import('@/mobile/views/code-pay/douyin/index.vue'), + }, + // 入口:UA 探测后 replace 到环境页(静态段已注册完毕,:code 兜底匹配编码) + { + path: ':code', + name: 'CodePayEntry', component: () => import('@/mobile/views/code-pay/index.vue'), }, ], diff --git a/src/mobile/router/router-guards.ts b/src/mobile/router/router-guards.ts index 259cf74..eddf762 100644 --- a/src/mobile/router/router-guards.ts +++ b/src/mobile/router/router-guards.ts @@ -20,12 +20,12 @@ const AGGREGATE_ENV_ROUTE_NAME: Record = { douyin: 'AggregateDouyinPage', } -/** 码牌 clientEnv → 路由 name */ +/** 码牌 clientEnv → 路由 name(指向 CodePay 父级下的环境页子路由,避免跨顶层触发 transition remount) */ const CODE_PAY_ENV_ROUTE_NAME: Record = { - wechat: 'CodePayWechat', - alipay: 'CodePayAlipay', - union_pay: 'CodePayUnion', - douyin: 'CodePayDouyin', + wechat: 'CodePayWechatPage', + alipay: 'CodePayAlipayPage', + union_pay: 'CodePayUnionPage', + douyin: 'CodePayDouyinPage', } /** @@ -59,8 +59,8 @@ function resolveEntryRedirect(to: RouteLocationNormalized): RouteLocationNormali } } } - // 码牌分发入口 → 各宿主端页(非钱包宿主留在 CodePayPage 显示扫码提示) - if (to.name === 'CodePayPage') { + // 码牌分发入口 → 各宿主端页(非钱包宿主留在 CodePayEntry 显示扫码提示) + if (to.name === 'CodePayEntry') { const code = to.params.code as string if (code) { const env = detectClientEnv() @@ -68,7 +68,7 @@ function resolveEntryRedirect(to: RouteLocationNormalized): RouteLocationNormali if (routeName) { return { name: routeName, params: { code }, replace: true } } - // browser 等非钱包宿主:留在 CodePayPage(onMounted 显示"请用钱包扫码") + // browser 等非钱包宿主:留在 CodePayEntry(onBeforeMount 显示"请用钱包扫码") } } return null diff --git a/src/mobile/views/code-pay/alipay/index.vue b/src/mobile/views/code-pay/alipay/index.vue index 033d7c1..e34a317 100644 --- a/src/mobile/views/code-pay/alipay/index.vue +++ b/src/mobile/views/code-pay/alipay/index.vue @@ -9,7 +9,7 @@ import InitLoadingMask from '@/shared/components/pay/InitLoadingMask.vue' import CodePayShell from '../shared/CodePayShell.vue' import { useCodePayPage } from '../shared/useCodePayPage' -defineOptions({ name: 'CodePayAlipay' }) +defineOptions({ name: 'CodePayAlipayPage' }) // 支付宝品牌色 const BRAND_COLOR = '#1677ff' diff --git a/src/mobile/views/code-pay/douyin/index.vue b/src/mobile/views/code-pay/douyin/index.vue index 1cdb86e..2655b47 100644 --- a/src/mobile/views/code-pay/douyin/index.vue +++ b/src/mobile/views/code-pay/douyin/index.vue @@ -9,7 +9,7 @@ import InitLoadingMask from '@/shared/components/pay/InitLoadingMask.vue' import CodePayShell from '../shared/CodePayShell.vue' import { useCodePayPage } from '../shared/useCodePayPage' -defineOptions({ name: 'CodePayDouyin' }) +defineOptions({ name: 'CodePayDouyinPage' }) // 抖音黑白主视觉 const BRAND_COLOR = '#161823' diff --git a/src/mobile/views/code-pay/index.vue b/src/mobile/views/code-pay/index.vue index 26ac892..2aea1dc 100644 --- a/src/mobile/views/code-pay/index.vue +++ b/src/mobile/views/code-pay/index.vue @@ -5,8 +5,12 @@ * 二维码恒为 /h/:code; 按 UA replace 到微信/支付宝/云闪付/抖音端页, * 其它环境(browser 等)提示用钱包扫码。 * 不支持页:四钱包图标 + 加粗标题 + 小号副文案(与聚合 unsupported 一致)。 + * + * 注:跳转须用 onBeforeMount(不用 onMounted),与 App.vue + * 的 enter 动画竞争(onMounted 跳转曾导致首屏白屏、刷新才正常,与聚合 unsupported 同源 bug)。 + * 同时各环境页须与入口同属 CodePay 父级子路由(见 modules.ts),保证 matched[0].name 不变。 */ -import { onMounted, ref } from 'vue' +import { onBeforeMount, ref } from 'vue' import { useI18n } from 'vue-i18n' import { useRoute, useRouter } from 'vue-router' import alipaySvg from '@/shared/assets/icons/channel/alipay.svg' @@ -15,7 +19,7 @@ import unionPaySvg from '@/shared/assets/icons/channel/union_pay.svg' import wechatSvg from '@/shared/assets/icons/channel/wechat.svg' import { detectClientEnv } from '@/shared/utils/client-env' -defineOptions({ name: 'CodePayDispatch' }) +defineOptions({ name: 'CodePayEntry' }) const { t } = useI18n() const route = useRoute() @@ -33,15 +37,15 @@ const walletIcons = [ { src: douyinSvg, altKey: 'codePay.wallet.douyin' }, ] -/** clientEnv → 码牌分端路由 name */ +/** clientEnv → 码牌分端路由 name(指向 CodePay 父级下的环境页子路由,不跨顶层以避免 transition 竞争) */ const ENV_ROUTE_NAME: Record = { - wechat: 'CodePayWechat', - alipay: 'CodePayAlipay', - union_pay: 'CodePayUnion', - douyin: 'CodePayDouyin', + wechat: 'CodePayWechatPage', + alipay: 'CodePayAlipayPage', + union_pay: 'CodePayUnionPage', + douyin: 'CodePayDouyinPage', } -onMounted(() => { +onBeforeMount(() => { const env = detectClientEnv() const routeName = ENV_ROUTE_NAME[env] if (routeName) { diff --git a/src/mobile/views/code-pay/union-pay/index.vue b/src/mobile/views/code-pay/union-pay/index.vue index 0208590..6fa821b 100644 --- a/src/mobile/views/code-pay/union-pay/index.vue +++ b/src/mobile/views/code-pay/union-pay/index.vue @@ -9,7 +9,7 @@ import InitLoadingMask from '@/shared/components/pay/InitLoadingMask.vue' import CodePayShell from '../shared/CodePayShell.vue' import { useCodePayPage } from '../shared/useCodePayPage' -defineOptions({ name: 'CodePayUnion' }) +defineOptions({ name: 'CodePayUnionPage' }) // 云闪付品牌红 const BRAND_COLOR = '#e21836' diff --git a/src/mobile/views/code-pay/wechat/index.vue b/src/mobile/views/code-pay/wechat/index.vue index 1ce6fac..a5019d5 100644 --- a/src/mobile/views/code-pay/wechat/index.vue +++ b/src/mobile/views/code-pay/wechat/index.vue @@ -9,7 +9,7 @@ import InitLoadingMask from '@/shared/components/pay/InitLoadingMask.vue' import CodePayShell from '../shared/CodePayShell.vue' import { useCodePayPage } from '../shared/useCodePayPage' -defineOptions({ name: 'CodePayWechat' }) +defineOptions({ name: 'CodePayWechatPage' }) // 微信品牌色 const BRAND_COLOR = '#07c160' diff --git a/src/shared/router/paths.ts b/src/shared/router/paths.ts index 6e9eada..fbb703c 100644 --- a/src/shared/router/paths.ts +++ b/src/shared/router/paths.ts @@ -43,6 +43,16 @@ export const RoutePath = { AGGREGATE_GROUP: '/aggregate', /** 聚合-不支持的宿主环境提示(Mobile + PC 注册用) */ AGGREGATE_UNSUPPORTED: '/aggregate/unsupported', + /** + * 码牌支付父级分组(仅 Mobile 路由注册用) + * Mobile 把入口 + 各宿主环境页(wechat/alipay/union-pay/douyin)全部合并到此父级下作为 + * 子路由,让 App.vue 顶层 transition key(matched[0].name)在 entry → 环境页跳转时保持不变 + * (都是 CodePay),避免整页 remount 与 Entry 的 onBeforeMount router.replace 竞争导致白屏 + * (曾发生在支付宝 webview:跨顶层 replace 与 的 enter 死锁)。 + * URL 契约不变:/h/{code}、/h/{env}/{code}。 + * PC 仍用各 CODE_PAY_*(/h/:code、/h/wechat/:code 等)作为单层顶层路由。 + */ + CODE_PAY_GROUP: '/h', /** 码牌支付入口(跨端:Mobile 分发/收款,PC 扫码引导;path 与后端 getCodeLink /h/{code} 一致) */ CODE_PAY: '/h/:code', /** 码牌支付-微信端(Mobile 收款;PC 同入口扫码引导) */