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