mirror of
https://gitee.com/bootx/dax-pay-h5
synced 2026-08-09 22:45:58 +08:00
feat(h5/cashier): 成功态升级为完整结果卡片并接入 returnUrl 倒计时跳转
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
* 负责订单展示、支付项选择、发起支付与 payBody 分发
|
||||
*/
|
||||
import type { CashierItemPublic, GatewayOrderInfo } from '@/shared/api/gateway'
|
||||
import { showNotify, showSuccessToast } from 'vant'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { showNotify } from 'vant'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
@@ -21,6 +21,7 @@ import { useGatewayOrderPoll } from '@/shared/hooks/use-gateway-order-poll'
|
||||
import { closeWebview } from '@/shared/pay/close-webview'
|
||||
import { useGatewayAuth } from '@/shared/pay/use-gateway-auth'
|
||||
import { detectClientEnv, isValidH5ClientEnv } from '@/shared/utils/client-env'
|
||||
import { formatDateTime } from '@/shared/utils/datetime'
|
||||
import { cacheOrder, clearCachedOrder, getCachedOrder } from '@/shared/utils/order-cache'
|
||||
import { fenToYuan } from '@/shared/utils/pay-amount'
|
||||
import { invokeJsapiByEnv } from '@/shared/utils/pay-jsapi'
|
||||
@@ -72,13 +73,17 @@ const isTerminal = computed(() =>
|
||||
paid.value || closed.value || failed.value || order.value.status === 'expired' || expired.value,
|
||||
)
|
||||
|
||||
// 结果态类型(关单/失败/过期/加载失败),非空时渲染结果卡片
|
||||
type ResultState = 'closed' | 'failed' | 'expired' | 'loadError'
|
||||
// 结果态类型(成功/关单/失败/过期/加载失败),非空时渲染结果卡片
|
||||
type ResultState = 'paid' | 'closed' | 'failed' | 'expired' | 'loadError'
|
||||
const resultState = computed<ResultState | ''>(() => {
|
||||
// 订单加载失败优先(订单不存在/网络异常)
|
||||
if (loadError.value) {
|
||||
return 'loadError'
|
||||
}
|
||||
// 支付成功(优先于其他终态,展示完整成功卡片)
|
||||
if (paid.value) {
|
||||
return 'paid'
|
||||
}
|
||||
if (closed.value) {
|
||||
return 'closed'
|
||||
}
|
||||
@@ -94,6 +99,9 @@ const resultState = computed<ResultState | ''>(() => {
|
||||
// 结果态展示元数据(图标/标题/副文案/主题色)
|
||||
const resultMeta = computed(() => {
|
||||
switch (resultState.value) {
|
||||
case 'paid':
|
||||
// 支付成功: 微信绿(与码牌/聚合一致)
|
||||
return { icon: 'success', titleKey: 'cashier.paid', tipKey: 'cashier.paidTip', color: '#07c160' }
|
||||
case 'closed':
|
||||
return { icon: 'lock', titleKey: 'cashier.closed', tipKey: 'cashier.closedTip', color: '#fa8c16' }
|
||||
case 'failed':
|
||||
@@ -118,9 +126,8 @@ const { startPoll, stopPoll } = useGatewayOrderPoll({
|
||||
order.value = latest
|
||||
},
|
||||
onPaid(latest) {
|
||||
// 轮询检测到已支付: 仅更新订单,成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
|
||||
order.value = latest
|
||||
showSuccessToast(t('cashier.paid'))
|
||||
redirectIfNeeded()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -148,16 +155,59 @@ function selectPayMethod(itemId: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付成功后跳转商户 returnUrl
|
||||
* 支付成功后跳转商户 returnUrl - 倒计时方案
|
||||
*
|
||||
* 进入 paid 终态后,若有 returnUrl,展示 3 秒倒计时,归零自动 location.href;
|
||||
* 用户也可点「返回商户」按钮立即跳转。替代原 1.2s setTimeout 闪跳,
|
||||
* 让用户能看清成功卡片再跳。
|
||||
*/
|
||||
function redirectIfNeeded() {
|
||||
if (order.value.returnUrl) {
|
||||
setTimeout(() => {
|
||||
window.location.href = order.value.returnUrl!
|
||||
}, 1200)
|
||||
const REDIRECT_COUNTDOWN_SECONDS = 3
|
||||
const redirectCountdown = ref(0)
|
||||
let redirectTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
function clearRedirectTimer() {
|
||||
if (redirectTimer) {
|
||||
clearInterval(redirectTimer)
|
||||
redirectTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动可视化倒计时,由 watch(resultState) 在进入 paid 终态时触发
|
||||
*/
|
||||
function startRedirectCountdown() {
|
||||
if (!order.value.returnUrl || redirectTimer) {
|
||||
return
|
||||
}
|
||||
redirectCountdown.value = REDIRECT_COUNTDOWN_SECONDS
|
||||
redirectTimer = setInterval(() => {
|
||||
redirectCountdown.value--
|
||||
if (redirectCountdown.value <= 0) {
|
||||
clearRedirectTimer()
|
||||
if (order.value.returnUrl) {
|
||||
window.location.href = order.value.returnUrl
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户点击「返回商户」立即跳转(不等倒计时)
|
||||
*/
|
||||
function redirectNow() {
|
||||
clearRedirectTimer()
|
||||
if (order.value.returnUrl) {
|
||||
window.location.href = order.value.returnUrl
|
||||
}
|
||||
}
|
||||
|
||||
// 进入 paid 终态时启动倒计时跳转(由 status 变化驱动,所有触发 paid 的路径统一在此处理)
|
||||
watch(resultState, (state) => {
|
||||
if (state === 'paid') {
|
||||
startRedirectCountdown()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 启动过期倒计时
|
||||
*/
|
||||
@@ -324,9 +374,8 @@ async function ensureOpenIdOrRedirect(): Promise<string | null> {
|
||||
async function handleJsapi(payload: string) {
|
||||
const status = await invokeJsapiByEnv(clientEnvParam, payload)
|
||||
if (status === 'ok') {
|
||||
// JSAPI 调起支付成功: 仅更新状态,成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
|
||||
order.value.status = 'paid'
|
||||
showSuccessToast(t('cashier.paid'))
|
||||
redirectIfNeeded()
|
||||
return
|
||||
}
|
||||
if (status === 'cancel') {
|
||||
@@ -375,10 +424,9 @@ async function pay() {
|
||||
switch (action.type) {
|
||||
case 'success':
|
||||
order.value.status = 'paid'
|
||||
// 支付成功后订单状态已变,清除缓存避免回显未付态
|
||||
// 支付成功后订单状态已变,清除缓存避免回显未付态
|
||||
clearCachedOrder(orderNo)
|
||||
showSuccessToast(t('cashier.paid'))
|
||||
redirectIfNeeded()
|
||||
// 成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
|
||||
break
|
||||
case 'redirect':
|
||||
redirectToPayUrl(action.url)
|
||||
@@ -421,6 +469,7 @@ onUnmounted(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
}
|
||||
clearRedirectTimer()
|
||||
stopPoll()
|
||||
})
|
||||
</script>
|
||||
@@ -433,13 +482,51 @@ onUnmounted(() => {
|
||||
:tip-key="paying ? 'cashier.paying' : ''"
|
||||
/>
|
||||
|
||||
<!-- 结果态:订单已关闭/支付失败/已过期/加载失败 -->
|
||||
<!-- 结果态: 支付成功/订单已关闭/支付失败/已过期/加载失败 -->
|
||||
<div v-else-if="resultState" class="cashier__result">
|
||||
<!-- 成功态: 实心彩色圆底 + 白色对勾(对齐 AggregateResultCard);其他态: 半透明底 + 彩色 SVG -->
|
||||
<div
|
||||
class="cashier__result-icon"
|
||||
:style="{ color: resultMeta.color, background: `${resultMeta.color}1a` }"
|
||||
:style="{
|
||||
color: resultMeta.color,
|
||||
background: resultState === 'paid' ? resultMeta.color : `${resultMeta.color}1a`,
|
||||
}"
|
||||
>
|
||||
<van-icon :name="resultMeta.icon" size="56" />
|
||||
<svg
|
||||
viewBox="0 0 48 48"
|
||||
width="40"
|
||||
height="40"
|
||||
fill="none"
|
||||
:stroke="resultState === 'paid' ? '#fff' : resultMeta.color"
|
||||
:stroke-width="resultState === 'paid' ? 4 : 3.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<!-- 成功: 对勾 -->
|
||||
<template v-if="resultState === 'paid'">
|
||||
<path d="M14 24 L21 31 L34 16" />
|
||||
</template>
|
||||
<!-- 订单已关闭: 锁 -->
|
||||
<template v-else-if="resultState === 'closed'">
|
||||
<rect x="10" y="22" width="28" height="18" rx="3" />
|
||||
<path d="M15 22 V15 a9 9 0 0 1 18 0 V22" />
|
||||
</template>
|
||||
<!-- 支付失败: 叉号 -->
|
||||
<template v-else-if="resultState === 'failed'">
|
||||
<path d="M14 14 L34 34 M34 14 L14 34" stroke-width="4" />
|
||||
</template>
|
||||
<!-- 已过期: 时钟 -->
|
||||
<template v-else-if="resultState === 'expired'">
|
||||
<circle cx="24" cy="24" r="17" />
|
||||
<path d="M24 13 V24 L32 28" />
|
||||
</template>
|
||||
<!-- 加载失败: 警告三角 -->
|
||||
<template v-else>
|
||||
<path d="M24 6 L43 39 H5 Z" />
|
||||
<path d="M24 19 V29" />
|
||||
<circle cx="24" cy="35" r="1.5" :fill="resultMeta.color" stroke="none" />
|
||||
</template>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="cashier__result-title">
|
||||
{{ t(resultMeta.titleKey) }}
|
||||
@@ -461,8 +548,23 @@ onUnmounted(() => {
|
||||
<span>{{ t('cashier.payableAmount') }}</span>
|
||||
<span class="cashier__result-amount">¥{{ amountYuan }}</span>
|
||||
</div>
|
||||
<!-- 成功态额外展示支付时间 -->
|
||||
<div v-if="resultState === 'paid' && order.payTime" class="cashier__result-order-row">
|
||||
<span>{{ t('cashier.payTime') }}</span>
|
||||
<span :title="order.payTime">{{ formatDateTime(order.payTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="cashier__result-btn" @click="closeWebview">
|
||||
<!-- 成功态且有 returnUrl: 倒计时提示 + 返回商户按钮 -->
|
||||
<template v-if="resultState === 'paid' && order.returnUrl">
|
||||
<p v-if="redirectCountdown > 0" class="cashier__result-countdown">
|
||||
{{ t('cashier.autoRedirectTip', { n: redirectCountdown }) }}
|
||||
</p>
|
||||
<button class="cashier__result-btn" @click="redirectNow">
|
||||
{{ t('cashier.backToMerchant') }}
|
||||
</button>
|
||||
</template>
|
||||
<!-- 其他情况: 关闭页面按钮 -->
|
||||
<button v-else class="cashier__result-btn" @click="closeWebview">
|
||||
{{ t('cashier.closePage') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -496,15 +598,8 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已支付 -->
|
||||
<div v-if="paid" class="cashier__body enter-y">
|
||||
<div class="cashier__section-title">
|
||||
{{ t('cashier.paid') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 二维码支付 -->
|
||||
<div v-else-if="showQrcode" class="cashier__body enter-y">
|
||||
<div v-if="showQrcode" class="cashier__body enter-y">
|
||||
<div class="cashier__section-title">
|
||||
{{ t('cashier.qrcodePay') }}
|
||||
</div>
|
||||
@@ -548,8 +643,8 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部支付按钮 -->
|
||||
<div v-if="!paid && !showQrcode" class="cashier__footer enter-y">
|
||||
<!-- 底部支付按钮(paid 已由 resultState 卡片接管,此处不会进入 paid 态) -->
|
||||
<div v-if="!showQrcode" class="cashier__footer enter-y">
|
||||
<button
|
||||
class="cashier__pay-btn"
|
||||
:disabled="paying || !selectId || expired"
|
||||
@@ -675,6 +770,15 @@ onUnmounted(() => {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// 自动跳转倒计时提示(仅成功态 + 有 returnUrl)
|
||||
&__result-countdown {
|
||||
margin: 16px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--h5-text-secondary);
|
||||
line-height: 1.6;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
&__result-btn {
|
||||
// 流式底栏,不再 fixed,避免被内容遮挡
|
||||
margin-top: 32px;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* PC WEB 收银台(cashierType=web,不按 clientEnv 分桶)
|
||||
*/
|
||||
import type { CashierItemPublic, GatewayOrderInfo } from '@/shared/api/gateway'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cashierPay, getGatewayOrder, listCashierItems } from '@/shared/api/gateway'
|
||||
@@ -12,6 +12,7 @@ import PayMethodIcon from '@/shared/components/pay/PayMethodIcon.vue'
|
||||
import QrCodeDisplay from '@/shared/components/pay/QrCodeDisplay.vue'
|
||||
import { useGatewayOrderPoll } from '@/shared/hooks/use-gateway-order-poll'
|
||||
import { closeWebview } from '@/shared/pay/close-webview'
|
||||
import { formatDateTime } from '@/shared/utils/datetime'
|
||||
import { fenToYuan } from '@/shared/utils/pay-amount'
|
||||
import {
|
||||
redirectToPayUrl,
|
||||
@@ -52,13 +53,17 @@ const isTerminal = computed(() =>
|
||||
paid.value || closed.value || failed.value || order.value.status === 'expired' || expired.value,
|
||||
)
|
||||
|
||||
// 结果态类型(关单/失败/过期/加载失败),非空时渲染结果卡片
|
||||
type ResultState = 'closed' | 'failed' | 'expired' | 'loadError'
|
||||
// 结果态类型(成功/关单/失败/过期/加载失败),非空时渲染结果卡片
|
||||
type ResultState = 'paid' | 'closed' | 'failed' | 'expired' | 'loadError'
|
||||
const resultState = computed<ResultState | ''>(() => {
|
||||
// 订单加载失败优先(订单不存在/网络异常)
|
||||
if (loadError.value) {
|
||||
return 'loadError'
|
||||
}
|
||||
// 支付成功(优先于其他终态,展示完整成功卡片)
|
||||
if (paid.value) {
|
||||
return 'paid'
|
||||
}
|
||||
if (closed.value) {
|
||||
return 'closed'
|
||||
}
|
||||
@@ -74,14 +79,17 @@ const resultState = computed<ResultState | ''>(() => {
|
||||
// 结果态展示元数据(图标/标题/副文案/主题色)
|
||||
const resultMeta = computed(() => {
|
||||
switch (resultState.value) {
|
||||
case 'paid':
|
||||
// 支付成功: 微信绿(与 H5/码牌/聚合一致)
|
||||
return { icon: 'check', titleKey: 'cashier.paid', tipKey: 'cashier.paidTip', color: '#07c160' }
|
||||
case 'closed':
|
||||
return { icon: 'lock', titleKey: 'cashier.closed', tipKey: 'cashier.closedTip', color: '#fa8c16' }
|
||||
case 'failed':
|
||||
return { icon: 'cross', titleKey: 'cashier.failed', tipKey: 'cashier.failedTip', color: '#ff4d4f' }
|
||||
case 'expired':
|
||||
return { icon: 'clock-o', titleKey: 'cashier.expired', tipKey: 'cashier.expiredTip', color: '#8c8c8c' }
|
||||
return { icon: 'clock', titleKey: 'cashier.expired', tipKey: 'cashier.expiredTip', color: '#8c8c8c' }
|
||||
default:
|
||||
return { icon: 'warning-o', titleKey: 'cashier.loadFail', tipKey: 'cashier.loadFailTip', color: '#fa8c16' }
|
||||
return { icon: 'warning', titleKey: 'cashier.loadFail', tipKey: 'cashier.loadFailTip', color: '#fa8c16' }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -98,13 +106,62 @@ const { startPoll, stopPoll } = useGatewayOrderPoll({
|
||||
order.value = latest
|
||||
},
|
||||
onPaid(latest) {
|
||||
// 轮询检测到已支付: 仅更新订单,成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
|
||||
order.value = latest
|
||||
if (latest.returnUrl) {
|
||||
window.location.href = latest.returnUrl
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* 支付成功后跳转商户 returnUrl - 倒计时方案
|
||||
*
|
||||
* 进入 paid 终态后,若有 returnUrl,展示 3 秒倒计时,归零自动 location.href;
|
||||
* 用户也可点「返回商户」按钮立即跳转。替代原 onPaid 内的立即跳转,
|
||||
* 让用户能看清成功卡片再跳。
|
||||
*/
|
||||
const REDIRECT_COUNTDOWN_SECONDS = 3
|
||||
const redirectCountdown = ref(0)
|
||||
let redirectTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
function clearRedirectTimer() {
|
||||
if (redirectTimer) {
|
||||
clearInterval(redirectTimer)
|
||||
redirectTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function startRedirectCountdown() {
|
||||
if (!order.value.returnUrl || redirectTimer) {
|
||||
return
|
||||
}
|
||||
redirectCountdown.value = REDIRECT_COUNTDOWN_SECONDS
|
||||
redirectTimer = setInterval(() => {
|
||||
redirectCountdown.value--
|
||||
if (redirectCountdown.value <= 0) {
|
||||
clearRedirectTimer()
|
||||
if (order.value.returnUrl) {
|
||||
window.location.href = order.value.returnUrl
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户点击「返回商户」立即跳转(不等倒计时)
|
||||
*/
|
||||
function redirectNow() {
|
||||
clearRedirectTimer()
|
||||
if (order.value.returnUrl) {
|
||||
window.location.href = order.value.returnUrl
|
||||
}
|
||||
}
|
||||
|
||||
// 进入 paid 终态时启动倒计时跳转(由 status 变化驱动,所有触发 paid 的路径统一在此处理)
|
||||
watch(resultState, (state) => {
|
||||
if (state === 'paid') {
|
||||
startRedirectCountdown()
|
||||
}
|
||||
})
|
||||
|
||||
function methodName(item?: CashierItemPublic | null) {
|
||||
if (!item) {
|
||||
return ''
|
||||
@@ -200,9 +257,7 @@ async function pay() {
|
||||
switch (action.type) {
|
||||
case 'success':
|
||||
order.value.status = 'paid'
|
||||
if (order.value.returnUrl) {
|
||||
window.location.href = order.value.returnUrl
|
||||
}
|
||||
// 成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
|
||||
break
|
||||
case 'redirect':
|
||||
redirectToPayUrl(action.url)
|
||||
@@ -253,6 +308,7 @@ onUnmounted(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
}
|
||||
clearRedirectTimer()
|
||||
stopPoll()
|
||||
})
|
||||
</script>
|
||||
@@ -262,11 +318,15 @@ onUnmounted(() => {
|
||||
<div class="pc-cashier__box">
|
||||
<!-- 加载态:统一全屏遮罩(替代原卡片内 spinner) -->
|
||||
<InitLoadingMask v-if="loading" />
|
||||
<!-- 结果态:订单已关闭/支付失败/已过期/加载失败 -->
|
||||
<!-- 结果态: 支付成功/订单已关闭/支付失败/已过期/加载失败 -->
|
||||
<div v-else-if="resultState" class="pc-cashier__result">
|
||||
<!-- 成功态: 实心彩色圆底 + 白色对勾(对齐 H5/AggregateResultCard);其他态: 半透明底 + 彩色 SVG -->
|
||||
<div
|
||||
class="pc-cashier__result-icon"
|
||||
:style="{ color: resultMeta.color, background: `${resultMeta.color}1a` }"
|
||||
:style="{
|
||||
color: resultMeta.color,
|
||||
background: resultState === 'paid' ? resultMeta.color : `${resultMeta.color}1a`,
|
||||
}"
|
||||
>
|
||||
<svg
|
||||
class="pc-cashier__result-svg"
|
||||
@@ -274,13 +334,17 @@ onUnmounted(() => {
|
||||
width="56"
|
||||
height="56"
|
||||
fill="none"
|
||||
:stroke="resultMeta.color"
|
||||
stroke-width="3.5"
|
||||
:stroke="resultState === 'paid' ? '#fff' : resultMeta.color"
|
||||
:stroke-width="resultState === 'paid' ? 4 : 3.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<!-- 支付成功: 对勾 -->
|
||||
<template v-if="resultState === 'paid'">
|
||||
<path d="M14 24 L21 31 L34 16" />
|
||||
</template>
|
||||
<!-- 支付失败: 叉号 -->
|
||||
<template v-if="resultState === 'failed'">
|
||||
<template v-else-if="resultState === 'failed'">
|
||||
<path d="M14 14 L34 34 M34 14 L14 34" stroke-width="4" />
|
||||
</template>
|
||||
<!-- 已过期: 时钟 -->
|
||||
@@ -321,8 +385,23 @@ onUnmounted(() => {
|
||||
<span>{{ t('cashier.payableAmount') }}</span>
|
||||
<span class="pc-cashier__result-amount">¥{{ amountYuan }}</span>
|
||||
</div>
|
||||
<!-- 成功态额外展示支付时间 -->
|
||||
<div v-if="resultState === 'paid' && order.payTime" class="pc-cashier__result-row">
|
||||
<span>{{ t('cashier.payTime') }}</span>
|
||||
<span :title="order.payTime">{{ formatDateTime(order.payTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="pc-cashier__result-btn" @click="closePage">
|
||||
<!-- 成功态且有 returnUrl: 倒计时提示 + 返回商户按钮 -->
|
||||
<template v-if="resultState === 'paid' && order.returnUrl">
|
||||
<p v-if="redirectCountdown > 0" class="pc-cashier__result-countdown">
|
||||
{{ t('cashier.autoRedirectTip', { n: redirectCountdown }) }}
|
||||
</p>
|
||||
<button class="pc-cashier__result-btn" @click="redirectNow">
|
||||
{{ t('cashier.backToMerchant') }}
|
||||
</button>
|
||||
</template>
|
||||
<!-- 其他情况: 关闭页面按钮 -->
|
||||
<button v-else class="pc-cashier__result-btn" @click="closePage">
|
||||
{{ t('cashier.closePage') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -357,10 +436,7 @@ onUnmounted(() => {
|
||||
|
||||
<!-- 内容区 -->
|
||||
<div class="pc-cashier__content">
|
||||
<div v-if="paid" class="pc-cashier__paid">
|
||||
{{ t('cashier.paid') }}
|
||||
</div>
|
||||
<div v-else-if="payError" class="pc-cashier__error">
|
||||
<div v-if="payError" class="pc-cashier__error">
|
||||
{{ payError }}
|
||||
</div>
|
||||
<!-- 支付方式选择 -->
|
||||
@@ -574,14 +650,6 @@ onUnmounted(() => {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.pc-cashier__paid {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #07c160;
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.pc-cashier__error {
|
||||
color: #ff4d4f;
|
||||
font-size: 14px;
|
||||
@@ -672,6 +740,15 @@ onUnmounted(() => {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 自动跳转倒计时提示(仅成功态 + 有 returnUrl) */
|
||||
.pc-cashier__result-countdown {
|
||||
margin: 20px 0 0;
|
||||
font-size: 13px;
|
||||
color: var(--h5-text-secondary);
|
||||
line-height: 1.6;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.pc-cashier__result-btn {
|
||||
margin-top: 32px;
|
||||
width: 240px;
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "Payment was unsuccessful. Please try again.",
|
||||
"expiredTip": "The order was not paid in time and has expired.",
|
||||
"loadFailTip": "Please check that the order link is correct.",
|
||||
"closePage": "Close"
|
||||
"closePage": "Close",
|
||||
"paidTip": "Payment completed. Thank you",
|
||||
"autoRedirectTip": "Auto-return to merchant in {n}s",
|
||||
"backToMerchant": "Return to merchant",
|
||||
"payTime": "Payment time"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "Pembayaran tidak berhasil. Silakan coba lagi.",
|
||||
"expiredTip": "Pesanan tidak dibayar tepat waktu dan telah kedaluwarsa.",
|
||||
"loadFailTip": "Pastikan tautan pesanan sudah benar.",
|
||||
"closePage": "Tutup"
|
||||
"closePage": "Tutup",
|
||||
"paidTip": "Pembayaran selesai. Terima kasih",
|
||||
"autoRedirectTip": "Kembali ke merchant otomatis dalam {n} detik",
|
||||
"backToMerchant": "Kembali ke merchant",
|
||||
"payTime": "Waktu pembayaran"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "支払いが完了しませんでした。もう一度お試しください",
|
||||
"expiredTip": "支払い期限を過ぎたため注文は期限切れとなりました",
|
||||
"loadFailTip": "注文リンクが正しいかご確認ください",
|
||||
"closePage": "ページを閉じる"
|
||||
"closePage": "ページを閉じる",
|
||||
"paidTip": "支払いが完了しました。ありがとうございます",
|
||||
"autoRedirectTip": "{n} 秒後に自動的に店舗に戻ります",
|
||||
"backToMerchant": "店舗に戻る",
|
||||
"payTime": "支払い日時"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "결제가 완료되지 않았습니다. 다시 시도해 주세요",
|
||||
"expiredTip": "시간 내에 결제하지 않아 주문이 만료되었습니다",
|
||||
"loadFailTip": "주문 링크가 올바른지 확인해 주세요",
|
||||
"closePage": "페이지 닫기"
|
||||
"closePage": "페이지 닫기",
|
||||
"paidTip": "결제가 완료되었습니다. 감사합니다",
|
||||
"autoRedirectTip": "{n}초 후 자동으로 가맹점으로 돌아갑니다",
|
||||
"backToMerchant": "가맹점으로 돌아가기",
|
||||
"payTime": "결제 시간"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "Pembayaran tidak berjaya. Sila cuba lagi.",
|
||||
"expiredTip": "Pesanan tidak dibayar tepat masa dan telah tamat tempoh.",
|
||||
"loadFailTip": "Sila pastikan pautan pesanan adalah betul.",
|
||||
"closePage": "Tutup"
|
||||
"closePage": "Tutup",
|
||||
"paidTip": "Pembayaran selesai. Terima kasih",
|
||||
"autoRedirectTip": "Kembali ke merchant automatik dalam {n} saat",
|
||||
"backToMerchant": "Kembali ke merchant",
|
||||
"payTime": "Masa pembayaran"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "การชำระเงินไม่สำเร็จ กรุณาลองอีกครั้ง",
|
||||
"expiredTip": "ชำระเงินไม่ทันเวลา คำสั่งซื้อหมดอายุแล้ว",
|
||||
"loadFailTip": "กรุณาตรวจสอบว่าลิงก์คำสั่งซื้อถูกต้อง",
|
||||
"closePage": "ปิด"
|
||||
"closePage": "ปิด",
|
||||
"paidTip": "ชำระเงินสำเร็จ ขอบคุณที่ใช้บริการ",
|
||||
"autoRedirectTip": "กลับสู่ร้านค้าอัตโนมัติใน {n} วินาที",
|
||||
"backToMerchant": "กลับสู่ร้านค้า",
|
||||
"payTime": "เวลาที่ชำระ"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "Thanh toán không thành công. Vui lòng thử lại.",
|
||||
"expiredTip": "Đơn hàng quá hạn thanh toán và đã hết hạn.",
|
||||
"loadFailTip": "Vui lòng kiểm tra lại đường dẫn đơn hàng.",
|
||||
"closePage": "Đóng"
|
||||
"closePage": "Đóng",
|
||||
"paidTip": "Thanh toán hoàn tất. Cảm ơn bạn",
|
||||
"autoRedirectTip": "Tự động quay lại merchant sau {n} giây",
|
||||
"backToMerchant": "Quay lại merchant",
|
||||
"payTime": "Thời gian thanh toán"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "支付未成功,请重新发起",
|
||||
"expiredTip": "订单超时未支付,已自动关闭",
|
||||
"loadFailTip": "请确认订单链接是否正确",
|
||||
"closePage": "关闭页面"
|
||||
"closePage": "关闭页面",
|
||||
"paidTip": "支付已完成,感谢使用",
|
||||
"autoRedirectTip": "{n} 秒后自动返回商户",
|
||||
"backToMerchant": "返回商户",
|
||||
"payTime": "支付时间"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "支付未成功,請重新發起",
|
||||
"expiredTip": "訂單逾時未支付,已自動關閉",
|
||||
"loadFailTip": "請確認訂單連結是否正確",
|
||||
"closePage": "關閉頁面"
|
||||
"closePage": "關閉頁面",
|
||||
"paidTip": "支付已完成,感謝使用",
|
||||
"autoRedirectTip": "{n} 秒後自動返回商戶",
|
||||
"backToMerchant": "返回商戶",
|
||||
"payTime": "支付時間"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,9 @@
|
||||
"failedTip": "支付未成功,請重新發起",
|
||||
"expiredTip": "訂單逾時未支付,已自動關閉",
|
||||
"loadFailTip": "請確認訂單連結是否正確",
|
||||
"closePage": "關閉頁面"
|
||||
"closePage": "關閉頁面",
|
||||
"paidTip": "支付已完成,感謝使用",
|
||||
"autoRedirectTip": "{n} 秒後自動返回商戶",
|
||||
"backToMerchant": "返回商戶",
|
||||
"payTime": "支付時間"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user