From 2a42833c4cfce80e2ba4d007ccd47f3568604658 Mon Sep 17 00:00:00 2001 From: Lucky <1646721+luckydududu@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:31:15 +0000 Subject: [PATCH] fix(admin-ui): wire require_force into the refund dialog AdminRefundDialog already implements a `requireForce` prop that renders the force checkbox and blocks submit until it is checked, but AdminOrdersView never consumed `require_force` from the refund response nor bound the prop, so the checkbox could never render. Admins hitting a require-force case (for example a user who spent their balance after requesting the refund) only saw an error toast and had no way to complete the refund from the UI. Keep the dialog open when the backend answers `require_force`, surface the checkbox and the backend warning, and reset the flag whenever the dialog is opened or closed. Verified with `npm run typecheck` (vue-tsc --noEmit, clean). --- .../views/admin/orders/AdminOrdersView.vue | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/admin/orders/AdminOrdersView.vue b/frontend/src/views/admin/orders/AdminOrdersView.vue index 7fe129968c..c467f086af 100644 --- a/frontend/src/views/admin/orders/AdminOrdersView.vue +++ b/frontend/src/views/admin/orders/AdminOrdersView.vue @@ -111,7 +111,7 @@ - + @@ -153,6 +153,8 @@ const selectedOrder = ref(null) const showDetailDialog = ref(false) const showRefundDialog = ref(false) const refundSubmitting = ref(false) +const refundRequireForce = ref(false) +const refundWarning = ref('') const refundQueryingIds = ref(new Set()) const orderAuditLogs = ref([]) const creditedAmountSymbol = currencySymbol('USD') @@ -235,7 +237,18 @@ async function handleRetryOrder(order: PaymentOrder) { catch (err: unknown) { appStore.showError(extractI18nErrorMessage(err, t, 'payment.errors', t('common.error'))) } } -function openRefundDialog(order: PaymentOrder) { selectedOrder.value = order; showRefundDialog.value = true } +function openRefundDialog(order: PaymentOrder) { + selectedOrder.value = order + refundRequireForce.value = false + refundWarning.value = '' + showRefundDialog.value = true +} + +function closeRefundDialog() { + showRefundDialog.value = false + refundRequireForce.value = false + refundWarning.value = '' +} function isRefundPendingWarning(warning: string | undefined): boolean { return /pending|处理中|待/.test(String(warning || '').toLowerCase()) @@ -248,16 +261,24 @@ async function handleRefund(data: { amount: number; reason: string; deduct_balan const res = await adminPaymentAPI.refundOrder(selectedOrder.value.id, { amount: data.amount, reason: data.reason, deduct_balance: data.deduct_balance, force: data.force }) if (res.data.success) { appStore.showSuccess(t('payment.admin.refundSuccess')) - showRefundDialog.value = false + closeRefundDialog() loadOrders() return } if (isRefundPendingWarning(res.data.warning)) { appStore.showSuccess(t('payment.admin.refundPending')) - showRefundDialog.value = false + closeRefundDialog() loadOrders() return } + if (res.data.require_force) { + // Backend needs an explicit force confirmation (e.g. the user spent their + // balance after requesting the refund). Keep the dialog open and surface + // the force checkbox instead of dropping the admin back to the list. + refundRequireForce.value = true + refundWarning.value = res.data.warning || '' + return + } appStore.showError(res.data.warning || t('common.error')) } catch (err: unknown) { appStore.showError(extractI18nErrorMessage(err, t, 'payment.errors', t('common.error'))) } finally { refundSubmitting.value = false }