feat(order): 收银台与购标流程改为先选支付方式再发起支付
- 新增 useHjcPayFlow(select/code 两段状态机)与 HjcPayPanel(两处共用): 进页只查单、不发起支付,不预选支付方式;出码后不再有切换控件, 只能显式「更换支付方式」回到选择态(不自动重发、不停查单) - 发起失败留在选择态并保留所选(渠道侧什么都没产生,没有理由锁住用户) - 收银台 pages/tender/pay.vue 只留订单侧职责:取单、终态屏、超时锁、付款成功跳列表 - 购标流程 BuyDocument:下单后停在选择态(不再按默认微信自动发起), 补支付剩余时间与超时锁,超时后回表单步重新下单 - 订单详情:只有已完成订单才显示支付方式渠道名,其余显示「—」; 标签按前缀判,修掉 ALIPAY_QR 被显示成「微信支付」 - useHjcPayStatus:新增结构化 channelClosed——「渠道单已关闭」在选择态说 「重新选择支付方式」、出码态才说「重新获取二维码」,不靠匹配 message 字符串 Refs .scratch/cashier-pay-method-selection/issues/01-pay-method-before-pay.md
This commit is contained in:
@@ -185,6 +185,7 @@ import type { HjcOrder } from '~/types/tender'
|
||||
import {
|
||||
hjcCanActOn,
|
||||
hjcOrderStatusClass,
|
||||
hjcOrderStatusKey,
|
||||
hjcOrderStatusLabel
|
||||
} from '~/utils/orderStatus'
|
||||
|
||||
@@ -221,7 +222,26 @@ const cancelling = ref(false)
|
||||
|
||||
/** 已支付:标书附件是否下发的唯一依据(与后端 `isPaid` 同一口径,只看 payStatus) */
|
||||
const isPaid = computed(() => order.value?.payStatus === 1)
|
||||
const payMethodLabel = computed(() => (order.value?.payMethod === 'ALIPAY' ? '支付宝' : '微信支付'))
|
||||
|
||||
/**
|
||||
* 「支付方式」这一行的显示口径。
|
||||
*
|
||||
* **只有已完成的订单才把 `payMethod` 当事实**:它建单时就被写成 `WECHAT_NATIVE`,只有真发起过
|
||||
* 一次支付才会被渠道响应改写——所以未支付订单上的值并不代表买家选过什么,也区分不了
|
||||
* "没选过"与"选过微信没付"(见 CONTEXT.md 的「支付渠道」)。未支付一律显示「—」,
|
||||
* 不去声称一种买家可能根本没选过的方式。
|
||||
*
|
||||
* 标签按**前缀**判而不是精确等值:支付宝侧有 `ALIPAY`(电脑网站支付)与 `ALIPAY_QR`(扫码),
|
||||
* 原先只认 `ALIPAY` 一个值,扫码支付过的单在详情里会被显示成"微信支付"。
|
||||
*/
|
||||
const payMethodLabel = computed(() => {
|
||||
const o = order.value
|
||||
if (!o || hjcOrderStatusKey(o) !== 'done') return '—'
|
||||
const method = String(o.payMethod || '')
|
||||
if (method.startsWith('ALIPAY')) return '支付宝'
|
||||
if (method.startsWith('WECHAT')) return '微信支付'
|
||||
return '—'
|
||||
})
|
||||
|
||||
/**
|
||||
* 还能不能操作(去支付 / 取消):共享判定,**不在这里重写一遍**——
|
||||
|
||||
+36
-312
@@ -24,7 +24,7 @@
|
||||
|
||||
<!--
|
||||
终态:已支付 / 已取消 / 已退款。
|
||||
**这一块必须先于发起支付判定**——进一张已支付的单却先拉起一次统一下单,既无意义,
|
||||
**这一块必须先于支付面板判定**——进一张已支付的单却先拉起一次统一下单,既无意义,
|
||||
又会多撞一次跨端 OUT_TRADE_NO_USED(多一次失败尝试不改变结论)。
|
||||
-->
|
||||
<div v-else-if="finalState" class="mx-auto mt-6 max-w-2xl rounded-lg border p-8 text-center" :class="finalState.cls">
|
||||
@@ -40,7 +40,7 @@
|
||||
再出一张码要么被渠道拒绝,要么给出一张我们马上就要取消的订单的码。
|
||||
超时 = 这张订单终了,想要就重新下单。
|
||||
-->
|
||||
<div v-else-if="timedOut" class="mx-auto mt-6 max-w-2xl rounded-lg border border-amber-200 bg-amber-50 p-8 text-center text-amber-800">
|
||||
<div v-else-if="deadlineExpired" class="mx-auto mt-6 max-w-2xl rounded-lg border border-amber-200 bg-amber-50 p-8 text-center text-amber-800">
|
||||
<p class="text-xl font-bold">支付已超时</p>
|
||||
<p class="mt-2 text-sm opacity-80">该订单因支付超时已取消,如需购买请重新下单。</p>
|
||||
<p class="mt-1 text-sm opacity-80">订单号:{{ orderNo }}</p>
|
||||
@@ -50,134 +50,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 待支付:进入即自动发起支付(与购标流程的支付步骤、h5 收银台一致) -->
|
||||
<div v-else class="mx-auto mt-6 max-w-2xl rounded-lg border border-gray-200 p-8 text-center">
|
||||
<h2 class="text-xl font-bold text-gray-900">请使用{{ channelLabel }}支付</h2>
|
||||
<p class="mt-2 text-sm text-gray-500">订单号:{{ orderNo }}</p>
|
||||
<p class="mt-1 text-sm text-gray-500">{{ order?.projectName }}</p>
|
||||
|
||||
<!--
|
||||
倒计时:驱动值来自**后端下发的 payDeadline**,不是前端拿创建时刻自己加时限。
|
||||
它是「这张码还有多久失效」的唯一告知渠道——不给它,用户会拿一张已经不能付的码去扫。
|
||||
没下发到期时刻(旧订单/字段缺失)时不显示,也不锁页面。
|
||||
-->
|
||||
<p v-if="remainText" class="mt-3 text-sm" :class="remainMs !== null && remainMs <= 60_000 ? 'text-red-500' : 'text-gray-500'">
|
||||
支付剩余时间 <span class="font-mono font-semibold">{{ remainText }}</span>
|
||||
</p>
|
||||
<!-- 金额一律来自**订单本身**(by-no),不来自支付响应:
|
||||
支付一失败页面就会停在 0.00,而收银台恰恰是最需要看到金额的地方 -->
|
||||
<p class="my-4 text-2xl font-bold text-blue-600">¥{{ formatMoney(amount) }}</p>
|
||||
|
||||
<!--
|
||||
支付方式选择。**切换方式即重新发起支付**——同一订单号不能换支付类型重复下单
|
||||
(微信会回 OUT_TRADE_NO_USED),所以切换前把上一张码清掉,避免两张码同时留在页面上。
|
||||
-->
|
||||
<div class="mb-6 inline-flex rounded-md border border-gray-200 p-1">
|
||||
<button
|
||||
v-for="opt in payOptions"
|
||||
:key="opt.value"
|
||||
type="button"
|
||||
class="rounded px-4 py-1.5 text-sm font-medium transition-colors disabled:opacity-50"
|
||||
:class="payType === opt.value
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'text-gray-600 hover:bg-gray-50'"
|
||||
:disabled="starting || refreshing"
|
||||
@click="switchPayType(opt.value)"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
支付宝走**电脑网站支付**:后端返回一段自提交表单,写进新页面即跳转支付宝收银台
|
||||
(当面付扫码需单独签约并提交资料审核,见 ADR 0010 补记,故不作默认)。
|
||||
微信仍是出码:两者在模板里各占一支。
|
||||
-->
|
||||
<template v-if="alipayForm">
|
||||
<div class="rounded-md bg-blue-50 px-4 py-6 text-sm text-blue-700">
|
||||
已为你打开支付宝收银台。若新页面没有自动打开,请点击下方按钮。
|
||||
</div>
|
||||
<button
|
||||
class="mt-6 w-full rounded-md bg-blue-600 py-3 font-medium text-white hover:bg-blue-700 disabled:opacity-50"
|
||||
:disabled="confirming"
|
||||
@click="openAlipayForm"
|
||||
>
|
||||
前往支付宝付款
|
||||
</button>
|
||||
<button
|
||||
class="mt-3 w-full rounded-md border border-gray-300 py-3 font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
||||
:disabled="confirming"
|
||||
@click="confirmPaid"
|
||||
>
|
||||
{{ confirming ? '确认中...' : '我已完成支付,确认订单' }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="codeUrl">
|
||||
<!-- 过期只是置灰提示,仍留在页面上:万一用户是刚过点就扫的,还能看见自己扫的是哪张码 -->
|
||||
<div :class="payExpired ? 'opacity-30' : ''">
|
||||
<HjcPayQrcode :value="codeUrl" :size="240" :alt="qrAlt" />
|
||||
</div>
|
||||
<!--
|
||||
这里**不写具体时长**:支付时限由后端配置、且随订单剩余窗口变化,
|
||||
页面上写死一个数字(原先写的是 2 小时)迟早与上面那行倒计时自相矛盾。
|
||||
-->
|
||||
<p class="mt-3 text-sm text-gray-500">请在支付时限内用{{ channelLabel }}扫描上方二维码完成支付</p>
|
||||
<p v-if="payExpired" class="mt-2 text-sm text-amber-600">二维码已过期,请重新获取后再扫码</p>
|
||||
</template>
|
||||
<div v-else class="rounded-md bg-gray-50 px-4 py-6 text-sm text-gray-500">
|
||||
{{ starting ? `正在获取${channelLabel}支付` : '暂未获取到支付信息,请点击下方按钮重新获取' }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="codeUrl && !alipayForm"
|
||||
class="mt-6 w-full rounded-md bg-blue-600 py-3 font-medium text-white hover:bg-blue-700 disabled:opacity-50"
|
||||
:disabled="confirming"
|
||||
@click="confirmPaid"
|
||||
>
|
||||
{{ confirming ? '确认中...' : '我已完成支付,确认订单' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!alipayForm"
|
||||
class="mt-6 w-full rounded-md py-3 font-medium disabled:opacity-50"
|
||||
:class="codeUrl
|
||||
? 'border border-gray-300 text-gray-700 hover:bg-gray-50'
|
||||
: 'bg-blue-600 text-white hover:bg-blue-700'"
|
||||
:disabled="refreshing || starting"
|
||||
@click="refreshQr"
|
||||
>
|
||||
{{ (refreshing || starting) ? '获取中...' : (codeUrl ? '重新获取二维码' : '获取支付信息') }}
|
||||
</button>
|
||||
|
||||
<p v-if="error || payError" class="mt-3 text-sm text-red-500">{{ error || payError }}</p>
|
||||
<p v-else-if="querying" class="mt-3 text-sm text-gray-400">正在确认支付状态...</p>
|
||||
|
||||
<div class="mt-6 border-t border-gray-100 pt-4">
|
||||
<NuxtLink to="/tender/orders" class="text-sm text-blue-600 hover:underline">← 返回我的订单</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
待支付:付款界面全在面板里(先选支付方式、点「立即支付」才发起;出码后只能显式更换)。
|
||||
本页只负责订单侧的事——金额与到期时刻都来自上面 by-no 取回的那张订单。
|
||||
-->
|
||||
<HjcPayPanel
|
||||
v-else
|
||||
:order-no="orderNo"
|
||||
:amount="amount"
|
||||
:deadline="order?.payDeadline"
|
||||
:project-name="order?.projectName"
|
||||
:redirect="route.fullPath"
|
||||
@paid="goOrders"
|
||||
@timeout="deadlineExpired = true"
|
||||
>
|
||||
<NuxtLink to="/tender/orders" class="text-sm text-blue-600 hover:underline">← 返回我的订单</NuxtLink>
|
||||
</HjcPayPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
hjcFormatCountdown,
|
||||
hjcIsTimeoutCancelled,
|
||||
hjcOrderStatusKey,
|
||||
hjcPayDeadlineMs,
|
||||
hjcPayRemainMs
|
||||
} from '~/utils/orderStatus'
|
||||
import { hjcIsPayExpired, hjcIsTimeoutCancelled, hjcOrderStatusKey } from '~/utils/orderStatus'
|
||||
|
||||
/**
|
||||
* 收银台:买家为一张**已创建的订单**付款的独立页面(CONTEXT.md 的「收银台」)。
|
||||
*
|
||||
* 与购标流程(`/buy` 的 BuyDocument `step==='pay'`)的分界:购标流程是「先建单、随即付款」,
|
||||
* 收银台是事后回到某张待支付订单继续付款。**本轮不重构 BuyDocument**,
|
||||
* 两份实现共享的只有 `HjcPayQrcode` 与 `useHjcPayStatus`(重活已经都在那里了)。
|
||||
* 与购标流程(`/buy` 的 BuyDocument 支付步)的分界:购标流程是「先建单、随即付款」,
|
||||
* 收银台是事后回到某张待支付订单继续付款。两份实现共用的**付款界面**已经收进
|
||||
* `HjcPayPanel`(+ `useHjcPayFlow`);本页只管订单侧的事:取单、终态、超时锁,
|
||||
* 以及渠道侧确认收款后去哪。
|
||||
*
|
||||
* 本页只服务微信外(ADR 0010):不因 UA 在微信内就跳 hjc-h5。
|
||||
* 支付方式可选**微信扫码**或**支付宝**;微信出二维码、支付宝走电脑网站支付
|
||||
* (返回自提交表单,跳转支付宝收银台)。两者共用同一个查单轮询与状态判定。
|
||||
*/
|
||||
const route = useRoute()
|
||||
const { isLoggedIn, handleAuthCode } = useHjcAuth()
|
||||
@@ -192,64 +95,16 @@ const loading = ref(false)
|
||||
/** by-no 失败(订单不存在/无权限/网络),与"发起支付失败"分开 */
|
||||
const loadError = ref('')
|
||||
const order = shallowRef<any | null>(null)
|
||||
/** 终态(已完成/已取消/已退款):终态下**绝不发起支付** */
|
||||
/** 终态(已完成/已取消/已退款):终态下**绝不渲染支付面板** */
|
||||
const finalState = shallowRef<{ title: string; cls: string } | null>(null)
|
||||
/**
|
||||
* 支付时限已到:进页就过期,或者面板挂着挂着归零(面板的 `timeout` 事件)。
|
||||
* 两种情况都锁死页面——不再出码、不再查单。
|
||||
*/
|
||||
const deadlineExpired = ref(false)
|
||||
|
||||
/** 当前支付方式。默认微信,与接入支付宝之前的行为一致 */
|
||||
const payType = ref<'wechat' | 'alipay'>('wechat')
|
||||
const payOptions = [
|
||||
{ value: 'wechat' as const, label: '微信支付' },
|
||||
{ value: 'alipay' as const, label: '支付宝' }
|
||||
]
|
||||
|
||||
const codeUrl = ref('')
|
||||
/** 支付宝电脑网站支付的自提交表单(后端只在该渠道下返回) */
|
||||
const alipayForm = ref('')
|
||||
/** 金额来自**订单本身**,与"发起支付"解耦:支付一失败页面就会停在 0.00 */
|
||||
const amount = ref(0)
|
||||
const starting = ref(false)
|
||||
const refreshing = ref(false)
|
||||
const confirming = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
/** 提示文案里的渠道名(只影响措辞,不影响判定) */
|
||||
const channelLabel = computed(() => (payType.value === 'alipay' ? '支付宝' : '微信'))
|
||||
const qrAlt = computed(() => (payType.value === 'alipay' ? '支付宝支付二维码' : '微信支付二维码'))
|
||||
|
||||
/**
|
||||
* 支付确认一律以**渠道侧**为准(轮询 + 手动按钮都走它)。
|
||||
* 绝不调用 `mark-paid`:那个接口不查单,点一下就能把订单置为已支付(见 ADR 0010)。
|
||||
*/
|
||||
const {
|
||||
paid,
|
||||
querying,
|
||||
error: payError,
|
||||
expired: payExpired,
|
||||
start: startPayPolling,
|
||||
query: queryPayStatus
|
||||
} = useHjcPayStatus(() => route.fullPath, () => payType.value)
|
||||
|
||||
// 渠道侧确认收款后跳订单列表(与 h5 收银台的 redirectTo 列表对齐)
|
||||
watch(paid, async (v) => {
|
||||
if (v) await navigateTo('/tender/orders')
|
||||
})
|
||||
|
||||
function formatMoney(n?: number | string) {
|
||||
return Number(n ?? 0).toFixed(2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒计时用的"当前时刻"。
|
||||
*
|
||||
* 每秒只更新一个 ref(而不是让每个用到它的地方各自 setInterval),
|
||||
* 且**秒级**足够了:支付时限是 30 分钟量级,没必要为 1 秒的精度付出多次渲染。
|
||||
*/
|
||||
const now = ref(Date.now())
|
||||
let clock: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
const remainMs = computed(() => hjcPayRemainMs(order.value, now.value))
|
||||
const remainText = computed(() => (remainMs.value == null ? '' : hjcFormatCountdown(remainMs.value)))
|
||||
/** 到期时刻已知且已归零 —— 页面锁死,不再出码、不再轮询 */
|
||||
const timedOut = computed(() => remainMs.value !== null && remainMs.value <= 0)
|
||||
|
||||
/** 「重新下单」的目标:回到该项目详情(不是购标流程,购标流程会再建一张新单) */
|
||||
const projectLink = computed(() => {
|
||||
@@ -275,118 +130,9 @@ function terminalStateOf(o: any) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道错误码 → 可行动的中文提示。
|
||||
*
|
||||
* `OUT_TRADE_NO_USED` = 同一订单号换了支付类型重复下单。微信内(h5 → 小程序,JSAPI)与
|
||||
* PC 端(Native)各发起一次时必现,而"从订单列表回到旧单支付"这条路径正是本轮新开的。
|
||||
* 错误码由后端从支付异常的 cause 链上取,前端**不要**去匹配 message 字符串——
|
||||
* 那种写法会在错误包装方式一改时静默失效。
|
||||
*/
|
||||
function payErrorMessage(res: any) {
|
||||
if (res?.data?.wechatCode === 'OUT_TRADE_NO_USED') {
|
||||
return '该订单已在微信内发起过支付,请回到微信内的收银台继续完成付款。'
|
||||
}
|
||||
return res?.message || '发起支付失败,请稍后重试'
|
||||
}
|
||||
|
||||
async function startPay() {
|
||||
// 已过支付时限就不出码:后端也会拒(它按「本单剩余窗口」算,剩余不足一分钟即拒绝),
|
||||
// 前端先拦住只是省一次注定失败的请求,并让页面停在"支付已超时"而不是闪一下错误。
|
||||
if (timedOut.value) return
|
||||
error.value = ''
|
||||
starting.value = true
|
||||
try {
|
||||
const res: any = await $fetch('/api/tender/pay', {
|
||||
method: 'POST',
|
||||
body: { orderNo: orderNo.value, payType: payType.value }
|
||||
})
|
||||
const auth = await handleAuthCode(res, route.fullPath)
|
||||
if (auth.handled) {
|
||||
error.value = auth.message || ''
|
||||
return
|
||||
}
|
||||
if (res?.code === 0) {
|
||||
codeUrl.value = res.data?.codeUrl || ''
|
||||
alipayForm.value = res.data?.alipayForm || ''
|
||||
if (res.data?.amount) amount.value = Number(res.data.amount)
|
||||
if (!codeUrl.value && !alipayForm.value) {
|
||||
error.value = '未获取到支付信息,请稍后重试'
|
||||
} else if (alipayForm.value) {
|
||||
// 网页支付:立刻把表单送去新页面,用户不必再点一次
|
||||
openAlipayForm()
|
||||
}
|
||||
} else {
|
||||
error.value = payErrorMessage(res)
|
||||
}
|
||||
// 无论这次有没有拿到支付码都开始查单:订单可能**已经**在渠道侧付过了
|
||||
//(例如在微信内由小程序付掉、或上一次支付成功但页面被关掉),查单会把它认出来并跳列表。
|
||||
// 到期时刻传后端下发的那个:让轮询的终点与后端判定超时、渠道单失效对齐(ADR 0019)。
|
||||
if (orderNo.value) startPayPolling(orderNo.value, hjcPayDeadlineMs(order.value))
|
||||
} catch (e: any) {
|
||||
error.value = e?.data?.message || e?.message || '发起支付失败,请稍后重试'
|
||||
} finally {
|
||||
starting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把支付宝返回的自提交表单写进一个新页面。
|
||||
*
|
||||
* 用 `document.write` 而不是 `v-html`:表单里带一段自动提交的脚本,
|
||||
* 而 `v-html` 插入的脚本标签**不会执行**(HTML 规范如此),表单永远不会自动提交。
|
||||
* 写进新窗口则脚本正常执行,且原收银台页面保留在原标签页继续轮询查单。
|
||||
*
|
||||
* 弹窗被拦截时不报"失败"——按钮复用本函数,用户允许弹窗后再点一次即可。
|
||||
*
|
||||
* 注意:本注释不要写出脚本标签的字面量——SFC 的 script 块会被它提前闭合。
|
||||
*/
|
||||
function openAlipayForm() {
|
||||
if (!alipayForm.value) return
|
||||
const w = window.open('', '_blank')
|
||||
if (!w) {
|
||||
error.value = '浏览器拦截了新窗口,请允许弹出窗口后点「前往支付宝付款」'
|
||||
return
|
||||
}
|
||||
w.document.open()
|
||||
w.document.write(alipayForm.value)
|
||||
w.document.close()
|
||||
error.value = ''
|
||||
}
|
||||
|
||||
/** 切换支付方式:清掉上一张码再重新发起,避免两种支付信息同时留在页面上 */
|
||||
async function switchPayType(next: 'wechat' | 'alipay') {
|
||||
if (next === payType.value) return
|
||||
payType.value = next
|
||||
codeUrl.value = ''
|
||||
alipayForm.value = ''
|
||||
error.value = ''
|
||||
await startPay()
|
||||
}
|
||||
|
||||
/** 重新获取二维码:允许对同一订单号重复发起下单,顺带重置计时 */
|
||||
async function refreshQr() {
|
||||
await startPay()
|
||||
}
|
||||
|
||||
/** 手动确认(扫码后的兜底):**也走查单**,不代替渠道下结论 */
|
||||
async function confirmPaid() {
|
||||
error.value = ''
|
||||
confirming.value = true
|
||||
try {
|
||||
const res = await queryPayStatus()
|
||||
if (res.handled) {
|
||||
error.value = res.message || ''
|
||||
return
|
||||
}
|
||||
if (res.settled) {
|
||||
await navigateTo('/tender/orders')
|
||||
return
|
||||
}
|
||||
error.value = res.message || `${channelLabel.value}侧还未收到付款,请完成支付后再试`
|
||||
} finally {
|
||||
confirming.value = false
|
||||
}
|
||||
/** 渠道侧确认收款后跳订单列表(与 h5 收银台的 redirectTo 列表对齐) */
|
||||
async function goOrders() {
|
||||
await navigateTo('/tender/orders')
|
||||
}
|
||||
|
||||
loggedIn.value = isLoggedIn()
|
||||
@@ -417,35 +163,13 @@ onMounted(async () => {
|
||||
finalState.value = terminal
|
||||
return
|
||||
}
|
||||
// 已在这张单上发起过支付宝支付,就默认用支付宝继续——避免用户上次选了支付宝、
|
||||
// 这次进来又被默认拉回微信,撞一次渠道侧的「订单号已使用」
|
||||
if (typeof o?.payMethod === 'string' && o.payMethod.startsWith('ALIPAY')) {
|
||||
payType.value = 'alipay'
|
||||
}
|
||||
loading.value = false
|
||||
// 已过支付时限:锁在"支付已超时"那一屏,不出码、不轮询
|
||||
if (timedOut.value) return
|
||||
// 只有"确实还没付、也没取消"才发起支付
|
||||
await startPay()
|
||||
// 已过支付时限:锁在"支付已超时"那一屏——面板根本不会挂上,也就不会出码、不会查单。
|
||||
// 到期时刻未知时**不算**超时(拿一个假值去锁死收银台更糟)。
|
||||
deadlineExpired.value = hjcIsPayExpired(o)
|
||||
} catch (e: any) {
|
||||
loadError.value = e?.data?.message || e?.message || '订单加载失败'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 秒级 ticking 只服务倒计时显示;真正的"到点锁页面"由 timedOut 这个 computed 判定,
|
||||
// 所以哪怕这一拍晚了几百毫秒,也不会出现"倒计时 00:00 了还能付款"。
|
||||
clock = setInterval(() => {
|
||||
now.value = Date.now()
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (clock) {
|
||||
clearInterval(clock)
|
||||
clock = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user