feat: update payment view UI and flow

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-02-04 14:58:23 +08:00
parent bc9e5d9293
commit a7e2e8da1c

View File

@@ -19,6 +19,9 @@ const isScanning = ref(false);
const isSuccess = ref(false);
let timer = null;
let pollTimer = null;
let pollRetries = 0;
const MAX_POLL_RETRIES = 20;
const errorMessage = ref("");
const paymentMethodName = computed(() => {
return paymentMethod.value === "alipay" ? "支付宝" : "余额";
@@ -46,33 +49,28 @@ const loadOrder = async () => {
isSuccess.value = true;
isScanning.value = false;
}
return res;
} catch (e) {
console.error("Load order failed", e);
throw e;
}
};
const payOrder = async () => {
try {
isScanning.value = true;
errorMessage.value = "";
await orderApi.pay(orderId, { method: paymentMethod.value });
// 支付接口若未立即更新状态,将继续依赖轮询
// 触发一次立即刷新,后续由轮询收敛
await loadOrder();
} catch (e) {
console.error("Pay order failed", e);
errorMessage.value = "支付请求失败,请重试";
} finally {
isScanning.value = false;
}
};
const simulateSuccess = () => {
isScanning.value = true;
setTimeout(() => {
isScanning.value = false;
isSuccess.value = true;
setTimeout(() => {
router.replace(tenantRoute(`/me/orders/${orderId}`));
}, 1500);
}, 1000);
};
onMounted(() => {
timer = setInterval(() => {
if (timeLeft.value > 0) timeLeft.value--;
@@ -80,9 +78,10 @@ onMounted(() => {
loadOrder();
// Poll Status
// Poll Status (节流 + 成功即停)
pollTimer = setInterval(async () => {
try {
pollRetries += 1;
const res = await orderApi.status(orderId);
orderStatus.value = res?.status || "";
if (res?.amount_paid !== undefined) {
@@ -94,14 +93,23 @@ onMounted(() => {
if (orderStatus.value === "paid" || orderStatus.value === "completed") {
isScanning.value = false;
isSuccess.value = true;
errorMessage.value = "";
clearInterval(pollTimer);
setTimeout(
() => router.replace(tenantRoute(`/me/orders/${orderId}`)),
1500,
1200,
);
}
if (pollRetries >= MAX_POLL_RETRIES && pollTimer) {
clearInterval(pollTimer);
isScanning.value = false;
errorMessage.value = "支付结果获取超时,请刷新页面或稍后重试";
}
} catch (e) {
console.error("Poll status failed", e);
isScanning.value = false;
errorMessage.value = "获取支付状态失败,请重试";
if (pollTimer) clearInterval(pollTimer);
}
}, 3000);
});
@@ -143,16 +151,20 @@ onUnmounted(() => {
<div class="p-8 md:p-12">
<!-- Amount -->
<div class="text-center mb-10">
<p class="text-slate-500 mb-2">订单提交成功请尽快支付</p>
<div class="text-4xl font-bold text-slate-900">¥ {{ amount }}</div>
<div class="text-sm text-slate-500 mt-2">
商品{{ productName || "加载中..." }}
<div class="text-center mb-10">
<p class="text-slate-500 mb-2">订单提交成功请尽快支付</p>
<div class="text-4xl font-bold text-slate-900">¥ {{ amount }}</div>
<div class="text-sm text-slate-500 mt-2">
商品{{ productName || "加载中..." }}
</div>
<div class="text-xs text-slate-400 mt-1">
状态{{ orderStatus || "pending" }}
</div>
<div v-if="errorMessage" class="text-xs text-red-500 mt-2">
{{ errorMessage }}
</div>
</div>
<div class="text-xs text-slate-400 mt-1">
状态{{ orderStatus || "pending" }}
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-12">
<!-- Payment Methods -->
@@ -237,19 +249,13 @@ onUnmounted(() => {
<p class="text-xs text-slate-400">二维码有效时间 2小时</p>
</div>
<!-- Dev Tool -->
<div class="flex flex-col items-center gap-3 mt-6">
<button
@click="payOrder"
class="px-4 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700"
class="px-4 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700 disabled:opacity-60"
:disabled="isScanning || isSuccess"
>
立即支付
</button>
<button
@click="simulateSuccess"
class="text-xs text-slate-300 hover:text-slate-500 underline"
>
[开发调试] 模拟支付成功
{{ isScanning ? "支付中..." : "立即支付" }}
</button>
</div>
</div>