Prechádzať zdrojové kódy

feat(订单实时): HAR 订单页接线 —— har_order 单通道、分页裸引用改箭头函数、详情联动

alphaH 1 týždeň pred
rodič
commit
027f030b66

+ 67 - 18
frontend/src/views/module_payment/har/order/index.vue

@@ -208,8 +208,8 @@
           :total="total"
           :page-sizes="[10, 20, 50, 100]"
           layout="total, sizes, prev, pager, next, jumper"
-          @size-change="load"
-          @current-change="load"
+          @size-change="() => load()"
+          @current-change="() => load()"
         />
       </div>
     </el-card>
@@ -457,7 +457,7 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted, reactive, ref } from "vue";
+import { onMounted, reactive, ref, computed } from "vue";
 import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
 import dayjs from "dayjs";
 import HarAPI, {
@@ -472,6 +472,7 @@ import HarAPI, {
   type HarOrderVO,
 } from "@/api/module_payment/har";
 import { useEnterpriseStore } from "@/store/modules/enterprise.store";
+import { useLiveRefetch } from "@/composables";
 
 const enterpriseStore = useEnterpriseStore();
 
@@ -563,25 +564,32 @@ const tradeTypeText = (v?: string) =>
 
 const formatTime = (v?: string) => (v ? dayjs(v).format("YYYY-MM-DD HH:mm:ss") : "-");
 
-async function load() {
-  loading.value = true;
+async function load(silent = false) {
+  if (!silent) {
+    loading.value = true;
+  }
   try {
-    const res = await HarAPI.orderList({
-      enterprise_id: searchForm.enterprise_id || undefined,
-      merchant_code: searchForm.merchant_code || undefined,
-      order_no: searchForm.order_no || undefined,
-      out_biz_no: searchForm.out_biz_no || undefined,
-      trade_status: searchForm.trade_status || undefined,
-      trade_type: searchForm.trade_type || undefined,
-      start_date: searchForm.start_date || undefined,
-      end_date: searchForm.end_date || undefined,
-      page_no: pageNo.value,
-      page_size: pageSize.value,
-    });
+    const res = await HarAPI.orderList(
+      {
+        enterprise_id: searchForm.enterprise_id || undefined,
+        merchant_code: searchForm.merchant_code || undefined,
+        order_no: searchForm.order_no || undefined,
+        out_biz_no: searchForm.out_biz_no || undefined,
+        trade_status: searchForm.trade_status || undefined,
+        trade_type: searchForm.trade_type || undefined,
+        start_date: searchForm.start_date || undefined,
+        end_date: searchForm.end_date || undefined,
+        page_no: pageNo.value,
+        page_size: pageSize.value,
+      },
+      silent
+    );
     list.value = res.data.data?.items || res.data.data?.list || [];
     total.value = res.data.data?.total || 0;
   } finally {
-    loading.value = false;
+    if (!silent) {
+      loading.value = false;
+    }
   }
 }
 
@@ -713,6 +721,7 @@ async function handleSync(row: HarOrderVO) {
  */
 async function handleRefund(row: HarOrderVO) {
   let reason = "";
+  confirmingRefund.value = true;
   try {
     const { value } = await ElMessageBox.prompt(
       `订单 ${row.order_no} 将按 ¥${row.price} 发起全额退款,金额不可修改;` +
@@ -727,6 +736,8 @@ async function handleRefund(row: HarOrderVO) {
     reason = value ?? "";
   } catch {
     return; // 取消(ElMessageBox 以 "cancel" reject),不发请求
+  } finally {
+    confirmingRefund.value = false;
   }
 
   refundingId.value = row.id;
@@ -769,6 +780,44 @@ async function copyPayUrl(url?: string) {
   }
 }
 
+// ─────────────────── 实时联动(2026-10-05)───────────────────
+
+/** 退款确认框(ElMessageBox.prompt)开着时也算操作中:弹窗背后列表不该在脚下换行。 */
+const confirmingRefund = ref(false);
+
+/**
+ * 「操作中」判据:创建弹窗开着 / 正在提交 / 退款确认框开着。
+ * ★ 不含 `syncingId` / `refundingId`:这两条腿自己结束时会 `load()`,期间行上按钮是转圈的
+ *   —— 后台静默刷新在这中间落地,只是把同样的数据重画一遍。
+ * ★ 不含 `detailVisible`:详情要跟着实时变(偏差 ①)。
+ */
+const liveBusy = computed(() => createVisible.value || saving.value || confirmingRefund.value);
+
+/** 详情弹层开着时的静默联动:只有还是同一单才覆盖(理由同 Task 11 Step 4)。 */
+async function refreshOpenDetail() {
+  const id = detail.value?.id;
+  if (!detailVisible.value || !id) {
+    return;
+  }
+  try {
+    const res = await HarAPI.orderDetail(id, true);
+    if (detailVisible.value && detail.value?.id === id) {
+      detail.value = res.data.data;
+    }
+  } catch {
+    // 静默
+  }
+}
+
+useLiveRefetch(
+  ["har_order"],
+  async () => {
+    await refreshOpenDetail();
+    await load(true);
+  },
+  { busy: liveBusy }
+);
+
 onMounted(async () => {
   load();
   loadMerchantOptions();