|
|
@@ -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>
|
|
|
|