This commit is contained in:
Mrx
2026-06-08 15:28:53 +08:00
parent 47981086a5
commit 958139d149
2 changed files with 173 additions and 40 deletions

View File

@@ -429,6 +429,7 @@
:close-on-click-modal="false"
:close-on-press-escape="false"
class="qr-code-dialog"
@close="handleQrCodeDialogClose"
>
<div class="qr-code-container">
<div class="qr-code-wrapper">
@@ -437,6 +438,7 @@
<p class="qr-code-tip">请使用微信扫描上方二维码完成支付</p>
<p class="qr-code-amount">支付金额¥{{ formatPrice(wechatForm.amount) }}</p>
<p v-if="isCheckingPayment" class="qr-code-checking">正在确认支付状态请稍候...</p>
<p class="qr-code-tip">关闭窗口后将继续确认支付结果请勿重复支付</p>
<el-button type="primary" @click="closeQrCodeDialog" class="close-qr-btn">关闭</el-button>
</div>
</el-dialog>
@@ -476,6 +478,11 @@ const showQrCodeDialog = ref(false)
const qrCodeCanvas = ref(null)
const currentWechatOrderNo = ref(null)
const isCheckingPayment = ref(false)
const wechatPaymentResolved = ref(false)
const skipQrCloseRedirect = ref(false)
const wechatDialogPollCount = ref(0)
const WECHAT_POLL_INTERVAL_MS = 5000
const WECHAT_DIALOG_MAX_POLL_COUNT = 12 // 弹窗内最多轮询12次约1分钟之后跳转处理页
let wechatOrderPollTimer = null
// 钱包信息
@@ -993,26 +1000,78 @@ const showQrCode = async (codeUrl) => {
}
}
// 关闭二维码弹窗
// 关闭二维码弹窗(用户主动关闭,跳转处理页继续轮询)
const closeQrCodeDialog = () => {
stopWechatOrderPolling()
showQrCodeDialog.value = false
}
const handleQrCodeDialogClose = () => {
if (wechatPaymentResolved.value) {
wechatPaymentResolved.value = false
stopWechatOrderPolling()
currentWechatOrderNo.value = null
isCheckingPayment.value = false
wechatDialogPollCount.value = 0
return
}
if (skipQrCloseRedirect.value) {
skipQrCloseRedirect.value = false
isCheckingPayment.value = false
wechatDialogPollCount.value = 0
return
}
const orderNo = currentWechatOrderNo.value
const orderAmount = wechatForm.amount
stopWechatOrderPolling()
currentWechatOrderNo.value = null
isCheckingPayment.value = false
wechatDialogPollCount.value = 0
if (orderNo) {
router.push({
path: '/finance/wallet/processing',
query: {
out_trade_no: orderNo,
amount: orderAmount,
payment_type: 'wechat',
},
})
}
}
const redirectToWechatProcessingPage = () => {
const orderNo = currentWechatOrderNo.value
const orderAmount = wechatForm.amount
if (!orderNo) return
stopWechatOrderPolling()
skipQrCloseRedirect.value = true
currentWechatOrderNo.value = null
wechatDialogPollCount.value = 0
showQrCodeDialog.value = false
isCheckingPayment.value = false
router.push({
path: '/finance/wallet/processing',
query: {
out_trade_no: orderNo,
amount: orderAmount,
payment_type: 'wechat',
},
})
}
// 开始轮询微信订单状态
const startWechatOrderPolling = () => {
// 清除之前的定时器
stopWechatOrderPolling()
// 立即检查一次
wechatDialogPollCount.value = 0
checkWechatOrderStatus()
// 每3秒轮询一次
wechatOrderPollTimer = setInterval(() => {
wechatDialogPollCount.value++
checkWechatOrderStatus()
}, 3000)
}, WECHAT_POLL_INTERVAL_MS)
}
// 停止轮询
@@ -1029,47 +1088,49 @@ const checkWechatOrderStatus = async () => {
return
}
if (wechatDialogPollCount.value >= WECHAT_DIALOG_MAX_POLL_COUNT) {
redirectToWechatProcessingPage()
return
}
try {
isCheckingPayment.value = true
const response = await callProtectedAPI(financeApi.getWechatOrderStatus, {
const response = await financeApi.getWechatOrderStatus({
out_trade_no: currentWechatOrderNo.value
})
if (!response || !response.data) {
if (!response?.data) {
isCheckingPayment.value = false
return
}
const orderStatus = response.data.status
// 订单状态pending, success, failed, closed
if (orderStatus === 'success') {
// 支付成功
wechatPaymentResolved.value = true
stopWechatOrderPolling()
isCheckingPayment.value = false
closeQrCodeDialog()
showQrCodeDialog.value = false
ElMessage.success('充值成功!')
// 刷新钱包余额
await loadWalletInfo()
// 重置表单
wechatForm.amount = ''
selectedPresetAmount.value = null
currentWechatOrderNo.value = null
wechatDialogPollCount.value = 0
} else if (orderStatus === 'failed' || orderStatus === 'closed') {
// 支付失败或关闭
wechatPaymentResolved.value = true
stopWechatOrderPolling()
isCheckingPayment.value = false
showQrCodeDialog.value = false
ElMessage.error('支付失败,请重新支付')
closeQrCodeDialog()
currentWechatOrderNo.value = null
wechatDialogPollCount.value = 0
} else {
// pending 状态继续轮询
isCheckingPayment.value = false
}
} catch (error) {
console.error('查询微信订单状态失败:', error)
isCheckingPayment.value = false
// 不显示错误,继续轮询
}
}

View File

@@ -119,7 +119,7 @@
<span class="font-medium">{{ orderInfo.out_trade_no }}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-500">支付宝交易号</span>
<span class="text-gray-500">{{ tradeNoLabel }}</span>
<span class="font-medium">{{ orderInfo.trade_no || '暂无' }}</span>
</div>
<div class="flex justify-between">
@@ -245,6 +245,57 @@
</div>
</div>
<!-- 轮询超时支付可能仍在处理中 -->
<div v-else-if="orderStatus === 'timeout'" class="text-center">
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-yellow-100">
<svg
class="h-6 w-6 text-yellow-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
></path>
</svg>
</div>
<h2 class="mt-6 text-3xl font-extrabold text-gray-900">仍在确认支付结果</h2>
<p class="mt-2 text-sm text-gray-600">
若您已完成支付余额通常会在几分钟内到账请稍后刷新钱包查看
</p>
<div class="mt-8 bg-white shadow rounded-lg p-6">
<div class="space-y-4">
<div class="flex justify-between">
<span class="text-gray-500">订单号</span>
<span class="font-medium">{{ orderInfo.out_trade_no }}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-500">金额</span>
<span class="font-medium text-green-600">¥{{ orderInfo.amount }}</span>
</div>
</div>
</div>
<div class="mt-8 space-y-3">
<button
@click="goToWallet"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
返回钱包查看余额
</button>
<button
@click="contactService"
class="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
联系客服
</button>
</div>
</div>
<!-- 其他状态 -->
<div v-else class="text-center">
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-yellow-100">
@@ -291,12 +342,16 @@ export default {
const orderStatus = ref('processing')
const isProcessing = ref(true)
const pollCount = ref(0)
const maxPollCount = ref(30) // 最多轮询30次
const maxPollCount = ref(36) // 最多轮询36次约3分钟
const pollIntervalMs = 5000 // 每5秒查询一次避免过于频繁
const pollInterval = ref(null)
// 获取URL参数
const outTradeNo = route.query.out_trade_no
const amount = route.query.amount
const paymentType = route.query.payment_type === 'wechat' ? 'wechat' : 'alipay'
const isWechatPay = computed(() => paymentType === 'wechat')
const tradeNoLabel = computed(() => (isWechatPay.value ? '微信交易号' : '支付宝交易号'))
// 初始化订单信息
orderInfo.value = {
@@ -304,6 +359,15 @@ export default {
amount: amount,
}
const normalizeOrderData = (data) => {
if (!data) return data
return {
...data,
amount: data.amount ?? amount,
trade_no: data.trade_no || data.transaction_id || null,
}
}
// 格式化时间
const formatTime = (timeStr) => {
if (!timeStr) return '暂无'
@@ -313,41 +377,45 @@ export default {
// 查询订单状态
const queryOrderStatus = async () => {
try {
const response = await financeApi.getAlipayOrderStatus({ out_trade_no: outTradeNo })
orderInfo.value = response.data
// 根据状态更新页面
const response = isWechatPay.value
? await financeApi.getWechatOrderStatus({ out_trade_no: outTradeNo })
: await financeApi.getAlipayOrderStatus({ out_trade_no: outTradeNo })
if (!response?.data) return
orderInfo.value = normalizeOrderData(response.data)
if (response.data.status === 'success') {
orderStatus.value = 'success'
isProcessing.value = false
stopPolling()
} else if (response.data.status === 'failed') {
} else if (response.data.status === 'failed' || response.data.status === 'closed') {
orderStatus.value = 'failed'
isProcessing.value = false
stopPolling()
} else if (response.data.status === 'pending') {
// 继续轮询轮询次数在setInterval中已经增加
if (pollCount.value >= maxPollCount.value) {
// 超过最大轮询次数,停止轮询
} else if (pollCount.value >= maxPollCount.value) {
orderStatus.value = 'timeout'
isProcessing.value = false
stopPolling()
// 可以显示超时提示
}
}
} catch (error) {
console.error('查询订单状态失败:', error)
// 查询失败时继续轮询,不要停止
if (pollCount.value >= maxPollCount.value) {
orderStatus.value = 'timeout'
isProcessing.value = false
stopPolling()
}
}
}
// 开始轮询
const startPolling = () => {
// 立即查询一次
queryOrderStatus()
// 每3秒查询一次
pollInterval.value = setInterval(() => {
pollCount.value++ // 增加轮询次数
pollCount.value++
queryOrderStatus()
}, 3000)
}, pollIntervalMs)
}
// 停止轮询
@@ -368,10 +436,13 @@ export default {
}
const retryPayment = () => {
// 重新创建支付订单
router.push({
path: '/finance/wallet',
query: { retry: 'true', amount: amount },
query: {
retry: 'true',
amount: amount,
method: isWechatPay.value ? 'wechat' : 'alipay',
},
})
}
@@ -404,6 +475,7 @@ export default {
isProcessing,
pollCount,
maxPollCount,
tradeNoLabel,
formatTime,
goToWallet,
goToRechargeRecords,