From 7955b37ec567d6279d205b0f6c298097903a39a6 Mon Sep 17 00:00:00 2001 From: chenlimao Date: Tue, 18 Aug 2026 11:48:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8D=A1=E5=9C=A8=E6=9B=B4=E6=96=B0=E4=B8=AD?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/index.vue | 109 ++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 35 deletions(-) diff --git a/src/pages/index.vue b/src/pages/index.vue index 58bc1a0..5023377 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -57,17 +57,36 @@ 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 = 10; +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); @@ -157,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 () => { - try { - const taskStatus = await getHardwareBoxTaskStatusAPI(taskId); - const status = Number(taskStatus?.status); - if (status === 2) { - completeHomeOtaUpdate(); - return; - } - if (status === 3) { - failHomeOtaUpdate(); - return; - } - if (status === 0 || status === 1) { - pollHomeOtaTaskStatus(taskId); - return; - } - failHomeOtaUpdate(); - } catch (err) { - failHomeOtaUpdate(); +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(runId); + return; } - }, 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 更新。 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({ @@ -220,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); } }; @@ -272,6 +310,7 @@ const handleOtaDone = () => { // 处理 OTA 更新失败后的重试按钮,重新走立即更新判断流程。 const handleOtaRetry = () => { + invalidateOtaUpdateRun(); handleOtaUpdate(); }; @@ -383,7 +422,7 @@ onMounted(async () => { }); onUnmounted(() => { - clearOtaUpdateTimers(); + invalidateOtaUpdateRun(); }); onShareAppMessage(() => {