diff --git a/src/mobile/router/modules.ts b/src/mobile/router/modules.ts index 8205640..e70fcf6 100644 --- a/src/mobile/router/modules.ts +++ b/src/mobile/router/modules.ts @@ -161,6 +161,38 @@ const routeModuleList: Array = [ }, ], }, + // 码牌支付-云闪付端(静态段须在 /h/:code 之前注册) + { + path: RoutePath.CODE_PAY_UNION, + name: 'CodePayUnion', + component: Layout, + meta: { + title: '码牌支付', + }, + 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: '码牌支付', + }, + children: [ + { + path: '', + name: 'CodePayDouyinPage', + component: () => import('@/mobile/views/code-pay/douyin/index.vue'), + }, + ], + }, // 码牌支付入口分发(跨端:PC 为扫码引导页) { path: RoutePath.CODE_PAY, diff --git a/src/mobile/views/code-pay/alipay/index.vue b/src/mobile/views/code-pay/alipay/index.vue index 1ff5074..32b041f 100644 --- a/src/mobile/views/code-pay/alipay/index.vue +++ b/src/mobile/views/code-pay/alipay/index.vue @@ -70,6 +70,7 @@ onMounted(() => { +/** + * 码牌支付-抖音端(黑白主视觉) + */ +import { onMounted } from 'vue' +import { useI18n } from 'vue-i18n' +import { useRoute } from 'vue-router' +import CodePayShell from '../shared/CodePayShell.vue' +import { useCodePayPage } from '../shared/useCodePayPage' + +defineOptions({ name: 'CodePayDouyin' }) + +const { t } = useI18n() +const route = useRoute() +const code = route.params.code as string + +const { + loading, + paying, + loadError, + info, + description, + setDescription, + displayAmount, + paid, + orderNo, + authRedirecting, + init, + onInput, + onDelete, + pay, + closePage, +} = useCodePayPage({ code, clientEnv: 'douyin' }) + +onMounted(() => { + init() +}) + + + + + diff --git a/src/mobile/views/code-pay/index.vue b/src/mobile/views/code-pay/index.vue index ef6e96b..dcc5f0e 100644 --- a/src/mobile/views/code-pay/index.vue +++ b/src/mobile/views/code-pay/index.vue @@ -2,7 +2,8 @@ /** * 码牌支付入口分发页 * - * 二维码恒为 /h/:code; 按 UA replace 到微信/支付宝端页, 其它环境提示用钱包扫码。 + * 二维码恒为 /h/:code; 按 UA replace 到微信/支付宝/云闪付/抖音端页, + * 其它环境(browser 等)提示用钱包扫码。 * 不支持页:四钱包图标 + 加粗标题 + 小号副文案(与聚合 unsupported 一致)。 */ import { onMounted, ref } from 'vue' @@ -32,17 +33,22 @@ const walletIcons = [ { src: douyinSvg, altKey: 'codePay.wallet.douyin' }, ] +/** clientEnv → 码牌分端路由 name */ +const ENV_ROUTE_NAME: Record = { + wechat: 'CodePayWechat', + alipay: 'CodePayAlipay', + union_pay: 'CodePayUnion', + douyin: 'CodePayDouyin', +} + onMounted(() => { const env = detectClientEnv() - if (env === 'wechat') { - router.replace({ name: 'CodePayWechat', params: { code } }) + const routeName = ENV_ROUTE_NAME[env] + if (routeName) { + router.replace({ name: routeName, params: { code } }) return } - if (env === 'alipay') { - router.replace({ name: 'CodePayAlipay', params: { code } }) - return - } - // 云闪付/抖音/浏览器: 提示用钱包扫码 + // browser 等非钱包宿主: 提示用钱包扫码 redirecting.value = false unsupported.value = true }) diff --git a/src/mobile/views/code-pay/shared/CodePayShell.vue b/src/mobile/views/code-pay/shared/CodePayShell.vue index fca1b99..cb8980e 100644 --- a/src/mobile/views/code-pay/shared/CodePayShell.vue +++ b/src/mobile/views/code-pay/shared/CodePayShell.vue @@ -13,6 +13,8 @@ defineOptions({ name: 'CodePayShell' }) const props = withDefaults(defineProps<{ /** 商户/码牌名称 */ merchantName?: string + /** 商户简称(副标题「向{name}付款」) */ + mchShortName?: string /** 展示金额(元字符串) */ displayAmount: string /** 金额类型 */ @@ -31,6 +33,7 @@ const props = withDefaults(defineProps<{ brandDark?: string }>(), { merchantName: '', + mchShortName: '', amountType: 'random', description: '', paying: false, @@ -41,8 +44,8 @@ const props = withDefaults(defineProps<{ }) const emit = defineEmits<{ - /** 金额键盘输入 */ - 'input': [key: string] + /** 金额键盘输入(Vant 数字键可能为 number) */ + 'input': [key: string | number] /** 金额删除 */ 'delete': [] /** 确认支付 */ @@ -60,6 +63,13 @@ const remarkDraft = ref('') /** 是否固定金额布局 */ const isFixed = computed(() => props.amountType === 'fixed') +/** 副标题:向{商户简称}付款 */ +const payToText = computed(() => { + const name = props.mchShortName || t('codePay.merchantDefault') + // 向{name}付款 + return t('codePay.payToMerchant', { name }) +}) + /** * 打开备注弹窗 */ @@ -151,9 +161,9 @@ function onPay() {
{{ merchantName || t('codePay.merchantDefault') }}
- +
- {{ t('codePay.payToMerchant') }} + {{ payToText }}
@@ -216,7 +226,7 @@ function onPay() { :show="!paying" :z-index="100" @close="onPay" - @input="(k: string) => emit('input', k)" + @input="(k: string | number) => emit('input', k)" @delete="emit('delete')" /> diff --git a/src/mobile/views/code-pay/shared/useCodePayPage.ts b/src/mobile/views/code-pay/shared/useCodePayPage.ts index e33d687..f13bba3 100644 --- a/src/mobile/views/code-pay/shared/useCodePayPage.ts +++ b/src/mobile/views/code-pay/shared/useCodePayPage.ts @@ -13,7 +13,7 @@ import { getCodePayInfo, } from '@/shared/api/code-pay' import { invokeJsapiByEnv } from '@/shared/pay/jsapi' -import { yuanToFen } from '@/shared/utils/pay-amount' +import { isAmountOverMax, yuanToFen } from '@/shared/utils/pay-amount' import { clearPayOpenId, getPayOpenId } from '@/shared/utils/pay-openid' import { redirectToPayUrl, @@ -25,7 +25,7 @@ export interface UseCodePayPageOptions { /** 码牌编码 */ code: string /** 写死的客户端环境 */ - clientEnv: 'wechat' | 'alipay' + clientEnv: 'wechat' | 'alipay' | 'union_pay' | 'douyin' } /** @@ -107,32 +107,43 @@ export function useCodePayPage(options: UseCodePayPageOptions) { /** * 金额键盘输入 + * Vant NumberKeyboard 数字键 @input 传 number,须统一转 string,否则 amount 被污染后 indexOf 崩溃 */ - function onInput(key: string) { + function onInput(key: string | number) { if (paying.value || paid.value) { return } - if (key === '.' && amount.value.includes('.')) { + // 统一为字符串(Vant 数字键为 number) + const k = String(key) + // 保证 amount 始终是字符串 + let current = String(amount.value) + if (k === '.' && current.includes('.')) { return } // 最多两位小数 - const dot = amount.value.indexOf('.') - if (dot >= 0 && amount.value.length - dot > 2 && key !== '.') { + const dot = current.indexOf('.') + if (dot >= 0 && current.length - dot > 2 && k !== '.') { return } - if (amount.value === '0' && key !== '.') { - amount.value = key + if (current === '0' && k !== '.') { + current = k } else { - amount.value += key + current += k } + // 上限:对齐后端 amount @Max(9999999999 分),超限则忽略本次按键 + if (isAmountOverMax(current)) { + return + } + amount.value = current } function onDelete() { if (paying.value || paid.value) { return } - amount.value = amount.value.slice(0, -1) || '0' + // String 加固:防止历史脏值导致 slice 失败 + amount.value = String(amount.value).slice(0, -1) || '0' } /** @@ -204,11 +215,16 @@ export function useCodePayPage(options: UseCodePayPageOptions) { } let amountFen: number | undefined if (info.value.amountType === 'random') { - amountFen = yuanToFen(amount.value) + amountFen = yuanToFen(String(amount.value)) if (!amountFen) { showNotify({ type: 'warning', message: t('codePay.amountZero') }) return } + // 提交前再拦一层(与键盘上限一致) + if (isAmountOverMax(String(amount.value))) { + showNotify({ type: 'warning', message: t('codePay.amountOverMax') }) + return + } } if (info.value.needOpenId && !openId.value) { showNotify({ type: 'warning', message: t('codePay.needAuth') }) diff --git a/src/mobile/views/code-pay/union-pay/index.vue b/src/mobile/views/code-pay/union-pay/index.vue new file mode 100644 index 0000000..8b4a0db --- /dev/null +++ b/src/mobile/views/code-pay/union-pay/index.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/src/mobile/views/code-pay/wechat/index.vue b/src/mobile/views/code-pay/wechat/index.vue index 784e09d..810b6f4 100644 --- a/src/mobile/views/code-pay/wechat/index.vue +++ b/src/mobile/views/code-pay/wechat/index.vue @@ -70,6 +70,7 @@ onMounted(() => { import('@/pc/views/code-pay/Index.vue'), meta: { title: '码牌支付' }, }, + { + path: RoutePath.CODE_PAY_UNION, + name: 'PcCodePayUnion', + component: () => import('@/pc/views/code-pay/Index.vue'), + meta: { title: '码牌支付' }, + }, + { + path: RoutePath.CODE_PAY_DOUYIN, + name: 'PcCodePayDouyin', + component: () => import('@/pc/views/code-pay/Index.vue'), + meta: { title: '码牌支付' }, + }, { path: RoutePath.CODE_PAY, name: 'PcCodePay', diff --git a/src/pc/views/code-pay/Index.vue b/src/pc/views/code-pay/Index.vue index 2a29dbe..5062db9 100644 --- a/src/pc/views/code-pay/Index.vue +++ b/src/pc/views/code-pay/Index.vue @@ -1,7 +1,7 @@