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:
2026-09-30 09:16:36 +08:00
parent 85207e1648
commit 57543cfade
6 changed files with 690 additions and 550 deletions
+15 -2
View File
@@ -68,6 +68,15 @@ export function useHjcPayStatus(
const error = ref('')
/** 支付码是否已超出计时(优先用后端下发的到期时刻,拿不到时才用本地兜底值) */
const expired = ref(false)
/**
* 渠道侧那张支付单已经终态、且不是成功(FAILED / CANCELLED / TIMEOUT / REFUND_FAILED)。
*
* 为什么要单独给一个结构化标记、而不是只给 `error` 里那句文案:收银台现在分两段
* (选择态 / 出码态),同一件事在**还没出过码**时该说"请重新选择支付方式",出过码才该说
* "请重新获取二维码"。让调用方去匹配那句 message 是一改就静默失效的写法,
* 所以**状态**归这里、**措辞**归调用方(见 `useHjcPayFlow` 的 failureText)。
*/
const channelClosed = ref(false)
/** 轮询是否应当进行(已开始、未付款、未过期) */
let active = false
@@ -109,12 +118,15 @@ export function useHjcPayStatus(
error.value = ''
const name = res.data?.paymentStatus ? String(res.data.paymentStatus) : null
status.value = name
channelClosed.value = false
if (name && SETTLED_STATUS.has(name)) {
paid.value = true
stop()
} else if (name && DEAD_STATUS.has(name)) {
// 渠道侧已终态且不是成功:这张支付码已经没用了,继续轮询只是空转
// 渠道侧已终态且不是成功:这张支付码已经没用了,继续轮询只是空转。
// 这里的文案是按"已经出过码"写的;还没出过码的调用方用 channelClosed 换成自己的措辞。
stop()
channelClosed.value = true
error.value = `该支付单在${channelLabel.value}已关闭,请重新获取二维码`
}
return { handled: false, status: name, settled: paid.value }
@@ -154,6 +166,7 @@ export function useHjcPayStatus(
orderNo = no
paid.value = false
expired.value = false
channelClosed.value = false
error.value = ''
status.value = null
deadline = typeof deadlineAtMs === 'number' && Number.isFinite(deadlineAtMs)
@@ -189,5 +202,5 @@ export function useHjcPayStatus(
document.removeEventListener('visibilitychange', onVisibilityChange)
})
return { status, paid, querying, error, expired, channelLabel, start, stop, query }
return { status, paid, querying, error, expired, channelClosed, channelLabel, start, stop, query }
}