Merge branch 'test' into feat-scene
This commit is contained in:
+95
-24
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import {onMounted, onUnmounted, ref} from "vue";
|
||||
import {onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import {onShareAppMessage, onShareTimeline, onShow} from "@dcloudio/uni-app";
|
||||
import Container from "@/components/Container.vue";
|
||||
import AppFooter from "@/components/AppFooter.vue";
|
||||
@@ -52,20 +52,41 @@ const otaInfo = ref({
|
||||
forceUpdate: false,
|
||||
});
|
||||
const isStartingOta = ref(false);
|
||||
let isCheckingOta = false;
|
||||
let otaCheckQueuedForOnline = false;
|
||||
let otaProgressTimer = null;
|
||||
let otaStatusTimer = null;
|
||||
let otaTimeoutTimer = null;
|
||||
let otaResultTimer = null;
|
||||
let otaStatusPollCount = 0;
|
||||
let otaUpdateRunId = 0;
|
||||
// 首页 OTA 轮询采用请求次数和总时长双重兜底,避免更新中状态长期卡住。
|
||||
const OTA_TASK_STATUS_POLL_INTERVAL = 2000;
|
||||
const OTA_TASK_STATUS_MAX_POLL_COUNT = 15;
|
||||
const OTA_TASK_STATUS_TIMEOUT = 30000;
|
||||
|
||||
// 清理首页 OTA 更新定时器,避免弹窗关闭或页面卸载后继续轮询。
|
||||
const clearOtaUpdateTimers = () => {
|
||||
clearInterval(otaProgressTimer);
|
||||
clearTimeout(otaStatusTimer);
|
||||
clearTimeout(otaTimeoutTimer);
|
||||
clearTimeout(otaResultTimer);
|
||||
otaProgressTimer = null;
|
||||
otaStatusTimer = null;
|
||||
otaTimeoutTimer = null;
|
||||
otaResultTimer = null;
|
||||
otaStatusPollCount = 0;
|
||||
};
|
||||
|
||||
// 使当前 OTA 运行失效,确保旧请求返回后不会继续轮询或覆盖新状态。
|
||||
const invalidateOtaUpdateRun = () => {
|
||||
otaUpdateRunId += 1;
|
||||
clearOtaUpdateTimers();
|
||||
};
|
||||
|
||||
const isOtaUpdateRunActive = (runId) =>
|
||||
runId === otaUpdateRunId && otaState.value === "update_progress";
|
||||
|
||||
// 启动首页 OTA 本地进度动画,最终成功失败以后端任务状态为准。
|
||||
const startOtaProgressAnimation = () => {
|
||||
clearInterval(otaProgressTimer);
|
||||
@@ -91,13 +112,25 @@ const applyOtaVersionInfo = (versionInfo) => {
|
||||
|
||||
// 检查当前设备盒子是否存在可升级版本。
|
||||
const checkOtaUpdate = async () => {
|
||||
if (isCheckingOta || otaVisible.value) return;
|
||||
isCheckingOta = true;
|
||||
|
||||
try {
|
||||
let deviceStatus;
|
||||
try {
|
||||
deviceStatus = await getDeviceBatteryAPI();
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
if (deviceStatus?.online !== true || otaVisible.value) return;
|
||||
|
||||
let versionInfo;
|
||||
try {
|
||||
versionInfo = await getHardwareBoxVersionAPI();
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
if (!versionInfo?.needUpdate) return;
|
||||
if (!versionInfo?.needUpdate || otaVisible.value) return;
|
||||
applyOtaVersionInfo(versionInfo);
|
||||
|
||||
const dismissedAt = uni.getStorageSync("ota_dismissed_at");
|
||||
@@ -105,8 +138,26 @@ const checkOtaUpdate = async () => {
|
||||
if (!otaInfo.value.forceUpdate && dismissedAt && now - dismissedAt < 24 * 60 * 60 * 1000) return;
|
||||
otaState.value = "new_version";
|
||||
otaVisible.value = true;
|
||||
} finally {
|
||||
isCheckingOta = false;
|
||||
const shouldRecheckForOnline = otaCheckQueuedForOnline;
|
||||
otaCheckQueuedForOnline = false;
|
||||
if (shouldRecheckForOnline && !otaVisible.value) {
|
||||
void checkOtaUpdate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 设备由 WS 通知上线时补查 OTA;已显示任何 OTA 弹窗时保留当前流程和结果。
|
||||
watch(online, (nextOnline, previousOnline) => {
|
||||
if (previousOnline !== false || nextOnline !== true || otaVisible.value) return;
|
||||
if (isCheckingOta) {
|
||||
otaCheckQueuedForOnline = true;
|
||||
return;
|
||||
}
|
||||
void checkOtaUpdate();
|
||||
});
|
||||
|
||||
// 拼接 OTA WiFi 页参数,让未连 WiFi 的设备继续使用同一份版本信息。
|
||||
const getOtaWifiUrl = () => {
|
||||
const { versionNumber, resourceUrl } = otaInfo.value;
|
||||
@@ -125,61 +176,78 @@ const handleOtaDismiss = () => {
|
||||
};
|
||||
|
||||
// 将首页 OTA 直连更新流程标记为失败。
|
||||
const failHomeOtaUpdate = () => {
|
||||
clearOtaUpdateTimers();
|
||||
const failHomeOtaUpdate = (runId) => {
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
invalidateOtaUpdateRun();
|
||||
isStartingOta.value = false;
|
||||
otaState.value = "update_failure";
|
||||
otaVisible.value = true;
|
||||
};
|
||||
|
||||
// 将首页 OTA 直连更新流程标记为成功。
|
||||
const completeHomeOtaUpdate = () => {
|
||||
clearOtaUpdateTimers();
|
||||
const completeHomeOtaUpdate = (runId) => {
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
invalidateOtaUpdateRun();
|
||||
isStartingOta.value = false;
|
||||
otaProgress.value = 100;
|
||||
setTimeout(() => {
|
||||
const completedRunId = otaUpdateRunId;
|
||||
otaResultTimer = setTimeout(() => {
|
||||
otaResultTimer = null;
|
||||
if (completedRunId !== otaUpdateRunId) return;
|
||||
otaState.value = "update_success";
|
||||
otaVisible.value = true;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// 轮询首页直接发起的 OTA 更新任务状态。
|
||||
const pollHomeOtaTaskStatus = (taskId) => {
|
||||
clearTimeout(otaStatusTimer);
|
||||
otaStatusTimer = setTimeout(async () => {
|
||||
const pollHomeOtaTaskStatus = async (taskId, runId) => {
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
|
||||
otaStatusPollCount += 1;
|
||||
try {
|
||||
const taskStatus = await getHardwareBoxTaskStatusAPI(taskId);
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
|
||||
const status = Number(taskStatus?.status);
|
||||
if (status === 2) {
|
||||
completeHomeOtaUpdate();
|
||||
completeHomeOtaUpdate(runId);
|
||||
return;
|
||||
}
|
||||
if (status === 3) {
|
||||
failHomeOtaUpdate();
|
||||
failHomeOtaUpdate(runId);
|
||||
return;
|
||||
}
|
||||
if (status === 0 || status === 1) {
|
||||
pollHomeOtaTaskStatus(taskId);
|
||||
if (otaStatusPollCount >= OTA_TASK_STATUS_MAX_POLL_COUNT) {
|
||||
failHomeOtaUpdate(runId);
|
||||
return;
|
||||
}
|
||||
failHomeOtaUpdate();
|
||||
otaStatusTimer = setTimeout(() => {
|
||||
otaStatusTimer = null;
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
void pollHomeOtaTaskStatus(taskId, runId);
|
||||
}, OTA_TASK_STATUS_POLL_INTERVAL);
|
||||
return;
|
||||
}
|
||||
failHomeOtaUpdate(runId);
|
||||
} catch (err) {
|
||||
failHomeOtaUpdate();
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
failHomeOtaUpdate(runId);
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// 设备盒子已连 WiFi 时,从首页直接传空 WiFi 信息发起 OTA 更新。
|
||||
const startHomeOtaUpdate = async () => {
|
||||
invalidateOtaUpdateRun();
|
||||
const runId = otaUpdateRunId;
|
||||
otaState.value = "update_progress";
|
||||
otaVisible.value = true;
|
||||
otaProgress.value = 0;
|
||||
startOtaProgressAnimation();
|
||||
otaTimeoutTimer = setTimeout(() => {
|
||||
if (otaState.value === "update_progress") {
|
||||
failHomeOtaUpdate();
|
||||
}
|
||||
}, 5 * 60 * 1000);
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
failHomeOtaUpdate(runId);
|
||||
}, OTA_TASK_STATUS_TIMEOUT);
|
||||
|
||||
try {
|
||||
const updateResult = await sendHardwareBoxUpdateAPI({
|
||||
@@ -188,13 +256,15 @@ const startHomeOtaUpdate = async () => {
|
||||
wifiPassword: "",
|
||||
resourceUrl: otaInfo.value.resourceUrl,
|
||||
});
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
if (!updateResult?.taskId) {
|
||||
failHomeOtaUpdate();
|
||||
failHomeOtaUpdate(runId);
|
||||
return;
|
||||
}
|
||||
pollHomeOtaTaskStatus(updateResult.taskId);
|
||||
void pollHomeOtaTaskStatus(updateResult.taskId, runId);
|
||||
} catch (err) {
|
||||
failHomeOtaUpdate();
|
||||
if (!isOtaUpdateRunActive(runId)) return;
|
||||
failHomeOtaUpdate(runId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,6 +310,7 @@ const handleOtaDone = () => {
|
||||
|
||||
// 处理 OTA 更新失败后的重试按钮,重新走立即更新判断流程。
|
||||
const handleOtaRetry = () => {
|
||||
invalidateOtaUpdateRun();
|
||||
handleOtaUpdate();
|
||||
};
|
||||
|
||||
@@ -351,7 +422,7 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearOtaUpdateTimers();
|
||||
invalidateOtaUpdateRun();
|
||||
});
|
||||
|
||||
onShareAppMessage(() => {
|
||||
|
||||
Reference in New Issue
Block a user