فهرست منبع

fix: 充电页定时器统一托管(onHide 暂停/onShow 恢复/onUnload 清理),合并两份停止轮询实现并移除死函数 start()

skyline 3 هفته پیش
والد
کامیت
38d0d765a4
2فایلهای تغییر یافته به همراه121 افزوده شده و 227 حذف شده
  1. 121 207
      charge-front/src/pages-charge/ordering/ordering.vue
  2. 0 20
      charge-front/src/pages-charge/utils/date.ts

+ 121 - 207
charge-front/src/pages-charge/ordering/ordering.vue

@@ -88,16 +88,20 @@
 </template>
 
 <script setup lang="ts">
-import { onHide, onLoad, onShow } from "@dcloudio/uni-app";
-import { cancelCharge, fetchChargeStatus, startCharge } from "../../api/charge";
+import { onHide, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
+import { cancelCharge, fetchChargeStatus } from "../../api/charge";
 import { ref } from "vue";
-import { format } from "../utils/date";
+import { format, formatDuration, parseDate } from "@/utils/date";
+import { fenToYuan } from "@/utils/money";
 import { to, reLaunch } from "@/utils/navigate";
 import { requireAuth } from "@/utils/auth";
 
 let timer: any;
 let statusTimer: any;
 let stopCountdownTimer: any;
+let stoppedTimer: any; // 等待充电桩停止的轮询
+let fetchStatusTimer: any; // 查询充电状态失败重试
+let stopWaitMode: "cancel" | "finish" | null = null; // 当前等待停止的流程类型
 const step = ref(0);
 const options = ref<any>();
 const status = ref({
@@ -136,6 +140,7 @@ const startStartTimer = () => {
   }, 1000);
 };
 const startStatusTimer = () => {
+  statusTimer && clearTimeout(statusTimer);
   statusTimer = setTimeout(() => {
     if (status.value.cancel) {
       return;
@@ -155,27 +160,6 @@ const startStatusTimer = () => {
       });
   }, 30000);
 };
-const start = () => {
-  step.value = 1;
-  startStartTimer();
-  startCharge(options.value && options.value.connectorId ? options.value.connectorId : "")
-    .then(() => {
-      fetchStatus();
-    })
-    .catch((err) => {
-      setTimeout(() => {
-        status.value.error = true;
-        timer && clearTimeout(timer);
-        uni.showModal({
-          content: `${err.errMsg}`,
-          showCancel: false,
-          success: () => {
-            uni.navigateBack();
-          },
-        });
-      }, 300);
-    });
-};
 
 const cancel = () => {
   uni.showModal({
@@ -203,7 +187,7 @@ const cancel = () => {
             )
               .then(() => {
                 // 停止充电后,轮询等待充电桩真正停止
-                waitForChargeStopped();
+                waitForChargeStopped("cancel");
               })
               .catch(() => {
                 clearStopCountdown();
@@ -242,108 +226,94 @@ const clearStopCountdown = () => {
 };
 
 /**
- * 轮询等待充电桩真正停止
- * 最多等待15秒,每2秒检查一次充电状态
+ * 轮询等待充电桩真正停止(最多重试 8 次,每 2 秒检查一次,约 15 秒)
+ * @param mode cancel: 手动停止充电,成功/异常提示后返回上一页,超时弹窗提示;
+ *             finish: 正常结束充电,成功/超时/异常均进入结束界面
  */
-const waitForChargeStopped = () => {
-  const maxRetry = 8; // 最多重试8次,每2秒检查一次,约15秒
+const waitForChargeStopped = (mode: "cancel" | "finish") => {
+  const maxRetry = 8;
   let retryCount = 0;
-  
+  stopWaitMode = mode;
+  const markStopped = () => {
+    clearStopCountdown();
+    getApp<any>().globalData.manualStop = true;
+  };
+  const finishAsStopped = () => {
+    markStopped();
+    if (mode === "finish") {
+      step.value = 3;
+      return;
+    }
+    uni.showToast({
+      title: "已停止充电",
+      icon: "success",
+    });
+    setTimeout(() => {
+      uni.navigateBack();
+    }, 1500);
+  };
+  const finishAsTimeout = () => {
+    markStopped();
+    if (mode === "finish") {
+      step.value = 3;
+      return;
+    }
+    uni.showModal({
+      title: "温馨提示",
+      content: "充电桩响应较慢,已发送停止指令,请稍后确认充电状态",
+      showCancel: false,
+      confirmText: "知道了",
+      success: () => {
+        uni.navigateBack();
+      },
+    });
+  };
+  const finishAsError = () => {
+    markStopped();
+    if (mode === "finish") {
+      step.value = 3;
+      return;
+    }
+    uni.showToast({
+      title: "已停止充电",
+      icon: "none",
+    });
+    setTimeout(() => {
+      uni.navigateBack();
+    }, 1500);
+  };
   const checkStopped = () => {
     fetchChargeStatus()
       .then((res) => {
         // 充电状态:0:预约中 1:启动中 2:充电中 3:停止中 4:已结束 5:未知
-        // 如果状态为 4(已结束)或 5(未知)或没有返回数据,认为已停止
+        // 状态为 4(已结束)/5(未知)或没有返回数据时,认为已停止
         if (!res || [4, 5].includes(res.chargeStatus)) {
-          clearStopCountdown();
-          // 设置手工停止充电标识
-          getApp<any>().globalData.manualStop = true;
-          uni.showToast({
-            title: "已停止充电",
-            icon: "success",
-          });
-          setTimeout(() => {
-            uni.navigateBack();
-          }, 1500);
-          return;
-        }
-        
-        // 如果状态为 3(停止中),继续等待
-        if (res.chargeStatus === 3) {
-          retryCount++;
-          if (retryCount >= maxRetry) {
-            clearStopCountdown();
-            // 设置手工停止充电标识
-            getApp<any>().globalData.manualStop = true;
-            uni.showModal({
-              title: "温馨提示",
-              content: "充电桩响应较慢,已发送停止指令,请稍后确认充电状态",
-              showCancel: false,
-              confirmText: "知道了",
-              success: () => {
-                uni.navigateBack();
-              },
-            });
-            return;
-          }
-          // 继续轮询
-          setTimeout(checkStopped, 2000);
+          finishAsStopped();
           return;
         }
-        
-        // 如果状态仍为 1 或 2(充电中/启动中),继续等待
-        if ([1, 2].includes(res.chargeStatus)) {
+        // 停止中/启动中/充电中:继续等待
+        if ([1, 2, 3].includes(res.chargeStatus)) {
           retryCount++;
           if (retryCount >= maxRetry) {
-            clearStopCountdown();
-            // 设置手工停止充电标识
-            getApp<any>().globalData.manualStop = true;
-            uni.showModal({
-              title: "温馨提示",
-              content: "充电桩响应较慢,已发送停止指令,请稍后确认充电状态",
-              showCancel: false,
-              confirmText: "知道了",
-              success: () => {
-                uni.navigateBack();
-              },
-            });
+            finishAsTimeout();
             return;
           }
-          // 继续轮询
-          setTimeout(checkStopped, 2000);
+          stoppedTimer = setTimeout(checkStopped, 2000);
           return;
         }
-        
         // 其他状态,认为已停止
-        clearStopCountdown();
-        getApp<any>().globalData.manualStop = true;
-        uni.showToast({
-          title: "已停止充电",
-          icon: "success",
-        });
-        setTimeout(() => {
-          uni.navigateBack();
-        }, 1500);
+        finishAsStopped();
       })
       .catch(() => {
         retryCount++;
         if (retryCount >= maxRetry) {
-          clearStopCountdown();
-          getApp<any>().globalData.manualStop = true;
-          uni.showToast({
-            title: "已停止充电",
-            icon: "none",
-          });
-          setTimeout(() => {
-            uni.navigateBack();
-          }, 1500);
+          finishAsError();
           return;
         }
         // 出错后继续轮询
-        setTimeout(checkStopped, 2000);
+        stoppedTimer = setTimeout(checkStopped, 2000);
       });
   };
-  
   checkStopped();
 };
 const finish = () => {
@@ -355,20 +325,11 @@ const finish = () => {
     .then((res) => {
       if (res.isStarted) {
         // 当前充电中
-        const start = res.startTime
-          ? new Date(res.startTime.replace(/-/g, "/"))
-          : new Date();
+        const start = res.startTime ? parseDate(res.startTime) : new Date();
         const end = new Date();
-        const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
-        const min = parseInt(`${diff / 60}`);
-        const time =
-          min >= 60
-            ? `${parseInt(`${min / 60}`)}小时${parseInt(
-                `${min - parseInt(`${min / 60}`) * 60}`
-              )}分钟`
-            : `${parseInt(`${diff / 60}`)}分钟`;
+        const time = formatDuration(start, end);
         const endFormat = format("y-M-d h:m:s");
-        const totalMoney = (res.totalMoney / 100).toFixed(2);
+        const totalMoney = fenToYuan(res.totalMoney);
         const _chargeInfo = [
           {
             label: "累计充电量",
@@ -417,10 +378,9 @@ const finish = () => {
               return;
             }
             // 停止充电后,轮询等待充电桩真正停止
-            waitForChargeStoppedAndShowResult();
+            waitForChargeStopped("finish");
           })
           .catch((err) => {
-            console.log(err);
             clearStopCountdown();
             uni.showToast({
               title: "停止充电失败,请重试",
@@ -430,7 +390,6 @@ const finish = () => {
       }
     })
     .catch((err) => {
-      console.log(err);
       clearStopCountdown();
       uni.showModal({
         title: "温馨提示",
@@ -445,94 +404,11 @@ const finish = () => {
     });
 };
 
-/**
- * 轮询等待充电桩真正停止,然后显示充电结束界面
- * 用于 finish 函数调用
- */
-const waitForChargeStoppedAndShowResult = () => {
-  const maxRetry = 8; // 最多重试8次,每2秒检查一次,约15秒
-  let retryCount = 0;
-  
-  const checkStopped = () => {
-    fetchChargeStatus()
-      .then((res) => {
-        // 充电状态:0:预约中 1:启动中 2:充电中 3:停止中 4:已结束 5:未知
-        // 如果状态为 4(已结束)或 5(未知)或没有返回数据,认为已停止
-        if (!res || [4, 5].includes(res.chargeStatus)) {
-          clearStopCountdown();
-          // 设置手工停止充电标识
-          getApp<any>().globalData.manualStop = true;
-          // 显示充电结束界面
-          step.value = 3;
-          return;
-        }
-        
-        // 如果状态为 3(停止中),继续等待
-        if (res.chargeStatus === 3) {
-          retryCount++;
-          if (retryCount >= maxRetry) {
-            clearStopCountdown();
-            // 设置手工停止充电标识
-            getApp<any>().globalData.manualStop = true;
-            // 超时也显示结束界面
-            step.value = 3;
-            return;
-          }
-          // 继续轮询
-          setTimeout(checkStopped, 2000);
-          return;
-        }
-        
-        // 如果状态仍为 1 或 2(充电中/启动中),继续等待
-        if ([1, 2].includes(res.chargeStatus)) {
-          retryCount++;
-          if (retryCount >= maxRetry) {
-            clearStopCountdown();
-            // 设置手工停止充电标识
-            getApp<any>().globalData.manualStop = true;
-            // 超时也显示结束界面
-            step.value = 3;
-            return;
-          }
-          // 继续轮询
-          setTimeout(checkStopped, 2000);
-          return;
-        }
-        
-        // 其他状态,认为已停止
-        clearStopCountdown();
-        getApp<any>().globalData.manualStop = true;
-        step.value = 3;
-      })
-      .catch(() => {
-        retryCount++;
-        if (retryCount >= maxRetry) {
-          clearStopCountdown();
-          getApp<any>().globalData.manualStop = true;
-          step.value = 3;
-          return;
-        }
-        // 出错后继续轮询
-        setTimeout(checkStopped, 2000);
-      });
-  };
-  
-  checkStopped();
-};
 const setChargeData = (res: any) => {
-  const start = res.startTime
-          ? new Date(res.startTime.replace(/-/g, "/"))
-          : new Date();
+  const start = res.startTime ? parseDate(res.startTime) : new Date();
   const end = new Date();
-  const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
-  const min = parseInt(`${diff / 60}`);
-  const time =
-    min >= 60
-      ? `${parseInt(`${min / 60}`)}小时${parseInt(
-          `${min - parseInt(`${min / 60}`) * 60}`
-        )}分钟`
-      : `${parseInt(`${diff / 60}`)}分钟`;
-  const totalMoney = (res.totalMoney / 100).toFixed(2);
+  const time = formatDuration(start, end);
+  const totalMoney = fenToYuan(res.totalMoney);
   const _chargeInfo = [
     {
       label: "累计充电量",
@@ -559,6 +435,8 @@ const setChargeData = (res: any) => {
   }
   chargeInfo.value = _chargeInfo;
 };
+const FETCH_STATUS_MAX_RETRY = 10;
+let fetchStatusRetry = 0;
 const fetchStatus = (data?: any) => {
   if (status.value.cancel) {
     return;
@@ -566,6 +444,7 @@ const fetchStatus = (data?: any) => {
   const promise = data ? Promise.resolve(data) : fetchChargeStatus();
   promise
     .then((res) => {
+      fetchStatusRetry = 0;
       if (res.isStarted) {
         // 当前充电中
         status.value.start = true;
@@ -575,8 +454,20 @@ const fetchStatus = (data?: any) => {
       }
     })
     .catch((err) => {
-      console.log(err);
-      setTimeout(() => {
+      if (++fetchStatusRetry >= FETCH_STATUS_MAX_RETRY) {
+        uni.showModal({
+          title: "温馨提示",
+          content: "查询充电状态异常,请返回重试",
+          showCancel: false,
+          confirmText: "知道了",
+          confirmColor: "#2d9e95",
+          success: () => {
+            reLaunch();
+          },
+        });
+        return;
+      }
+      fetchStatusTimer = setTimeout(() => {
         fetchStatus(data);
       }, 1000);
     });
@@ -600,7 +491,6 @@ const onImgLoad = () => {
       }
     })
     .catch((err) => {
-      console.log(err);
       uni.showModal({
         title: "温馨提示",
         content: "查询充电状态异常,请返回重试",
@@ -620,12 +510,36 @@ onLoad((_options: any) => {
   }
   options.value = _options;
 });
-onHide(() => {
+/** 清理本页全部定时器(不清 isStopping 等业务状态,供 onShow 恢复判断) */
+const clearAllTimers = () => {
   timer && clearTimeout(timer);
   statusTimer && clearTimeout(statusTimer);
+  stoppedTimer && clearTimeout(stoppedTimer);
+  fetchStatusTimer && clearTimeout(fetchStatusTimer);
+  if (stopCountdownTimer) {
+    clearInterval(stopCountdownTimer);
+    stopCountdownTimer = null;
+  }
+};
+
+onHide(() => {
+  clearAllTimers();
 });
 onShow(() => {
   startStartTimer();
+  // 恢复充电中状态轮询(onHide 时被暂停)
+  if (step.value === 2 && status.value.start && !status.value.cancel) {
+    fetchStatus();
+  }
+  // 恢复等待充电桩停止的轮询与倒计时遮罩
+  if (stoppingStatus.value.isStopping && stopWaitMode) {
+    startStopCountdown();
+    waitForChargeStopped(stopWaitMode);
+  }
+});
+onUnload(() => {
+  clearAllTimers();
+  stopWaitMode = null;
 });
 </script>
 

+ 0 - 20
charge-front/src/pages-charge/utils/date.ts

@@ -1,20 +0,0 @@
-export function format(format: string, time?: number) {
-  const now = time ? new Date(time) : new Date();
-  const map: Record<string, string | number> = {
-    y: now.getFullYear(),
-    M: now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`,
-    d: now.getDate() > 9 ? now.getDate() : `0${now.getDate()}`,
-    h: now.getHours() > 9 ? now.getHours() : `0${now.getHours()}`,
-    m: now.getMinutes() > 9 ? now.getMinutes() : `0${now.getMinutes()}`,
-    s: now.getSeconds() > 9 ? now.getSeconds() : `0${now.getSeconds()}`,
-  };
-  const res = [];
-  for (let i = 0; i < format.length; i++) {
-    if (map[format.charAt(i)]) {
-      res.push(map[format.charAt(i)]);
-    } else {
-      res.push(format.charAt(i));
-    }
-  }
-  return res.join("");
-}