3 Commits
Author SHA1 Message Date
chenlimao 75cfcd66ee fix: OTA轮询次数改为15次 2026-08-18 13:57:49 +08:00
chenlimao 7955b37ec5 fix: 修复首页更新卡在更新中弹窗问题 2026-08-18 11:48:20 +08:00
chenlimao 9e6ad4cf50 feat: OTA 首页弹窗优化 2026-08-18 10:30:16 +08:00
+119 -48
View File
@@ -1,5 +1,5 @@
<script setup> <script setup>
import {onMounted, onUnmounted, ref} from "vue"; import {onMounted, onUnmounted, ref, watch} from "vue";
import {onShareAppMessage, onShareTimeline, onShow} from "@dcloudio/uni-app"; import {onShareAppMessage, onShareTimeline, onShow} from "@dcloudio/uni-app";
import Container from "@/components/Container.vue"; import Container from "@/components/Container.vue";
import AppFooter from "@/components/AppFooter.vue"; import AppFooter from "@/components/AppFooter.vue";
@@ -52,20 +52,41 @@ const otaInfo = ref({
forceUpdate: false, forceUpdate: false,
}); });
const isStartingOta = ref(false); const isStartingOta = ref(false);
let isCheckingOta = false;
let otaCheckQueuedForOnline = false;
let otaProgressTimer = null; let otaProgressTimer = null;
let otaStatusTimer = null; let otaStatusTimer = null;
let otaTimeoutTimer = 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 更新定时器,避免弹窗关闭或页面卸载后继续轮询。 // 清理首页 OTA 更新定时器,避免弹窗关闭或页面卸载后继续轮询。
const clearOtaUpdateTimers = () => { const clearOtaUpdateTimers = () => {
clearInterval(otaProgressTimer); clearInterval(otaProgressTimer);
clearTimeout(otaStatusTimer); clearTimeout(otaStatusTimer);
clearTimeout(otaTimeoutTimer); clearTimeout(otaTimeoutTimer);
clearTimeout(otaResultTimer);
otaProgressTimer = null; otaProgressTimer = null;
otaStatusTimer = null; otaStatusTimer = null;
otaTimeoutTimer = null; otaTimeoutTimer = null;
otaResultTimer = null;
otaStatusPollCount = 0;
}; };
// 使当前 OTA 运行失效,确保旧请求返回后不会继续轮询或覆盖新状态。
const invalidateOtaUpdateRun = () => {
otaUpdateRunId += 1;
clearOtaUpdateTimers();
};
const isOtaUpdateRunActive = (runId) =>
runId === otaUpdateRunId && otaState.value === "update_progress";
// 启动首页 OTA 本地进度动画,最终成功失败以后端任务状态为准。 // 启动首页 OTA 本地进度动画,最终成功失败以后端任务状态为准。
const startOtaProgressAnimation = () => { const startOtaProgressAnimation = () => {
clearInterval(otaProgressTimer); clearInterval(otaProgressTimer);
@@ -91,21 +112,51 @@ const applyOtaVersionInfo = (versionInfo) => {
// 检查当前设备盒子是否存在可升级版本。 // 检查当前设备盒子是否存在可升级版本。
const checkOtaUpdate = async () => { const checkOtaUpdate = async () => {
let versionInfo; if (isCheckingOta || otaVisible.value) return;
isCheckingOta = true;
try { try {
versionInfo = await getHardwareBoxVersionAPI(); let deviceStatus;
} catch (err) { 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 || otaVisible.value) return;
applyOtaVersionInfo(versionInfo);
const dismissedAt = uni.getStorageSync("ota_dismissed_at");
const now = Date.now();
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; return;
} }
if (!versionInfo?.needUpdate) return; void checkOtaUpdate();
applyOtaVersionInfo(versionInfo); });
const dismissedAt = uni.getStorageSync("ota_dismissed_at");
const now = Date.now();
if (!otaInfo.value.forceUpdate && dismissedAt && now - dismissedAt < 24 * 60 * 60 * 1000) return;
otaState.value = "new_version";
otaVisible.value = true;
};
// 拼接 OTA WiFi 页参数,让未连 WiFi 的设备继续使用同一份版本信息。 // 拼接 OTA WiFi 页参数,让未连 WiFi 的设备继续使用同一份版本信息。
const getOtaWifiUrl = () => { const getOtaWifiUrl = () => {
@@ -125,61 +176,78 @@ const handleOtaDismiss = () => {
}; };
// 将首页 OTA 直连更新流程标记为失败。 // 将首页 OTA 直连更新流程标记为失败。
const failHomeOtaUpdate = () => { const failHomeOtaUpdate = (runId) => {
clearOtaUpdateTimers(); if (!isOtaUpdateRunActive(runId)) return;
invalidateOtaUpdateRun();
isStartingOta.value = false; isStartingOta.value = false;
otaState.value = "update_failure"; otaState.value = "update_failure";
otaVisible.value = true; otaVisible.value = true;
}; };
// 将首页 OTA 直连更新流程标记为成功。 // 将首页 OTA 直连更新流程标记为成功。
const completeHomeOtaUpdate = () => { const completeHomeOtaUpdate = (runId) => {
clearOtaUpdateTimers(); if (!isOtaUpdateRunActive(runId)) return;
invalidateOtaUpdateRun();
isStartingOta.value = false; isStartingOta.value = false;
otaProgress.value = 100; otaProgress.value = 100;
setTimeout(() => { const completedRunId = otaUpdateRunId;
otaResultTimer = setTimeout(() => {
otaResultTimer = null;
if (completedRunId !== otaUpdateRunId) return;
otaState.value = "update_success"; otaState.value = "update_success";
otaVisible.value = true; otaVisible.value = true;
}, 300); }, 300);
}; };
// 轮询首页直接发起的 OTA 更新任务状态。 // 轮询首页直接发起的 OTA 更新任务状态。
const pollHomeOtaTaskStatus = (taskId) => { const pollHomeOtaTaskStatus = async (taskId, runId) => {
clearTimeout(otaStatusTimer); if (!isOtaUpdateRunActive(runId)) return;
otaStatusTimer = setTimeout(async () => {
try { otaStatusPollCount += 1;
const taskStatus = await getHardwareBoxTaskStatusAPI(taskId); try {
const status = Number(taskStatus?.status); const taskStatus = await getHardwareBoxTaskStatusAPI(taskId);
if (status === 2) { if (!isOtaUpdateRunActive(runId)) return;
completeHomeOtaUpdate();
return; const status = Number(taskStatus?.status);
} if (status === 2) {
if (status === 3) { completeHomeOtaUpdate(runId);
failHomeOtaUpdate(); return;
return;
}
if (status === 0 || status === 1) {
pollHomeOtaTaskStatus(taskId);
return;
}
failHomeOtaUpdate();
} catch (err) {
failHomeOtaUpdate();
} }
}, 3000); if (status === 3) {
failHomeOtaUpdate(runId);
return;
}
if (status === 0 || status === 1) {
if (otaStatusPollCount >= OTA_TASK_STATUS_MAX_POLL_COUNT) {
failHomeOtaUpdate(runId);
return;
}
otaStatusTimer = setTimeout(() => {
otaStatusTimer = null;
if (!isOtaUpdateRunActive(runId)) return;
void pollHomeOtaTaskStatus(taskId, runId);
}, OTA_TASK_STATUS_POLL_INTERVAL);
return;
}
failHomeOtaUpdate(runId);
} catch (err) {
if (!isOtaUpdateRunActive(runId)) return;
failHomeOtaUpdate(runId);
}
}; };
// 设备盒子已连 WiFi 时,从首页直接传空 WiFi 信息发起 OTA 更新。 // 设备盒子已连 WiFi 时,从首页直接传空 WiFi 信息发起 OTA 更新。
const startHomeOtaUpdate = async () => { const startHomeOtaUpdate = async () => {
invalidateOtaUpdateRun();
const runId = otaUpdateRunId;
otaState.value = "update_progress"; otaState.value = "update_progress";
otaVisible.value = true; otaVisible.value = true;
otaProgress.value = 0; otaProgress.value = 0;
startOtaProgressAnimation(); startOtaProgressAnimation();
otaTimeoutTimer = setTimeout(() => { otaTimeoutTimer = setTimeout(() => {
if (otaState.value === "update_progress") { if (!isOtaUpdateRunActive(runId)) return;
failHomeOtaUpdate(); failHomeOtaUpdate(runId);
} }, OTA_TASK_STATUS_TIMEOUT);
}, 5 * 60 * 1000);
try { try {
const updateResult = await sendHardwareBoxUpdateAPI({ const updateResult = await sendHardwareBoxUpdateAPI({
@@ -188,13 +256,15 @@ const startHomeOtaUpdate = async () => {
wifiPassword: "", wifiPassword: "",
resourceUrl: otaInfo.value.resourceUrl, resourceUrl: otaInfo.value.resourceUrl,
}); });
if (!isOtaUpdateRunActive(runId)) return;
if (!updateResult?.taskId) { if (!updateResult?.taskId) {
failHomeOtaUpdate(); failHomeOtaUpdate(runId);
return; return;
} }
pollHomeOtaTaskStatus(updateResult.taskId); void pollHomeOtaTaskStatus(updateResult.taskId, runId);
} catch (err) { } catch (err) {
failHomeOtaUpdate(); if (!isOtaUpdateRunActive(runId)) return;
failHomeOtaUpdate(runId);
} }
}; };
@@ -240,6 +310,7 @@ const handleOtaDone = () => {
// 处理 OTA 更新失败后的重试按钮,重新走立即更新判断流程。 // 处理 OTA 更新失败后的重试按钮,重新走立即更新判断流程。
const handleOtaRetry = () => { const handleOtaRetry = () => {
invalidateOtaUpdateRun();
handleOtaUpdate(); handleOtaUpdate();
}; };
@@ -351,7 +422,7 @@ onMounted(async () => {
}); });
onUnmounted(() => { onUnmounted(() => {
clearOtaUpdateTimers(); invalidateOtaUpdateRun();
}); });
onShareAppMessage(() => { onShareAppMessage(() => {