update:优化ota

This commit is contained in:
2026-09-24 15:16:14 +08:00
parent f8bf9fdb63
commit f4b37fba00
6 changed files with 200 additions and 303 deletions
+6 -7
View File
@@ -599,14 +599,13 @@ export const getDeviceWifiStatusAPI = async () => {
}); });
}; };
// 获取硬件盒子版本信息,用于判断当前设备是否需要 OTA 升级。
export const getHardwareBoxVersionAPI = async () => {
return request("GET", "/user/hardwareBox/version");
};
// 发送硬件盒子 OTA 更新指令,服务端会返回后续轮询使用的任务 ID。 // 发送硬件盒子 OTA 更新指令,服务端会返回后续轮询使用的任务 ID。
export const sendHardwareBoxUpdateAPI = async (data) => { export const sendHardwareBoxUpdateAPI = async ({versionNumber, wifiSsid, wifiPassword}) => {
return request("POST", "/user/hardwareBox/sendUpdate", data); return request("POST", "/user/hardwareBox/sendUpdate", {
versionNumber,
wifiSsid,
wifiPassword,
});
}; };
export const addNoteAPI = async (id, remark) => { export const addNoteAPI = async (id, remark) => {
+17 -3
View File
@@ -13,6 +13,15 @@ const phaseTextMap = {
installing: "正在安装固件", installing: "正在安装固件",
}; };
// 设备详情中的 latestVersion 是升级目标,version 是当前固件版本。
export const getOtaInfoFromDetail = (detail) => ({
versionNumber: String(detail?.latestVersion ?? "").trim(),
currentVersion: String(detail?.version ?? "").trim(),
versionInfo: String(detail?.versionInfo ?? "").trim(),
needUpdate: detail?.needUpdate === true || Number(detail?.needUpdate) === 1,
forceUpdate: detail?.forceUpdate === true || Number(detail?.forceUpdate) === 1,
});
// OTA 状态由首页、WiFi 设置页和我的设备页共享,页面切换后仍可继续接收进度。 // OTA 状态由首页、WiFi 设置页和我的设备页共享,页面切换后仍可继续接收进度。
const updating = ref(false); const updating = ref(false);
const progress = ref(0); const progress = ref(0);
@@ -115,6 +124,7 @@ function handleSocketMessage(message) {
} else if (!isCurrentVersionMessage(message.data)) { } else if (!isCurrentVersionMessage(message.data)) {
return; return;
} }
if (!targetVersion && messageVersion) targetVersion = messageVersion;
const nextProgress = Math.min( const nextProgress = Math.min(
100, 100,
@@ -138,6 +148,7 @@ function handleSocketMessage(message) {
} else if (!isCurrentVersionMessage(message.data)) { } else if (!isCurrentVersionMessage(message.data)) {
return; return;
} }
if (!targetVersion && messageVersion) targetVersion = messageVersion;
if (message.data?.status === "success") { if (message.data?.status === "success") {
finishUpdate("success"); finishUpdate("success");
@@ -160,16 +171,20 @@ export const useOtaUpdate = () => {
const startUpdate = async ({ const startUpdate = async ({
versionNumber, versionNumber,
resourceUrl,
wifiSsid = "", wifiSsid = "",
wifiPassword = "", wifiPassword = "",
onSuccess, onSuccess,
}) => { }) => {
if (updating.value) return false; if (updating.value) return false;
const nextVersion = String(versionNumber || "").trim();
if (!nextVersion) {
uni.showToast({ title: "缺少固件目标版本", icon: "none" });
return false;
}
updateRunId += 1; updateRunId += 1;
const runId = updateRunId; const runId = updateRunId;
targetVersion = String(versionNumber || ""); targetVersion = nextVersion;
lastFinishedVersion = ""; lastFinishedVersion = "";
successCallback = onSuccess || null; successCallback = onSuccess || null;
resultVisible.value = false; resultVisible.value = false;
@@ -188,7 +203,6 @@ export const useOtaUpdate = () => {
versionNumber: targetVersion, versionNumber: targetVersion,
wifiSsid, wifiSsid,
wifiPassword, wifiPassword,
resourceUrl,
}); });
if (runId !== updateRunId || !updating.value) return false; if (runId !== updateRunId || !updating.value) return false;
if (!updateResult?.taskId) { if (!updateResult?.taskId) {
@@ -99,7 +99,7 @@ export function useDeviceStatus({
const refreshDeviceDetails = async () => { const refreshDeviceDetails = async () => {
const deviceId = device.value.deviceId; const deviceId = device.value.deviceId;
if (!deviceId) return; if (!deviceId) return null;
const requestVersion = ++deviceDetailRequestVersion; const requestVersion = ++deviceDetailRequestVersion;
try { try {
@@ -111,13 +111,17 @@ export function useDeviceStatus({
requestVersion !== deviceDetailRequestVersion || requestVersion !== deviceDetailRequestVersion ||
device.value.deviceId !== deviceId device.value.deviceId !== deviceId
) { ) {
return; return null;
} }
deviceDetails.value = { deviceDetails.value = {
...deviceDetails.value, ...deviceDetails.value,
...detail, ...detail,
deviceModelName: detail.deviceModelName ?? "", deviceModelName: detail.deviceModelName ?? "",
latestVersion: detail.latestVersion ?? "",
needUpdate: detail.needUpdate ?? false,
forceUpdate: detail.forceUpdate ?? false,
version: detail.version ?? "",
bindTime: String( bindTime: String(
detail.bindTime ?? deviceDetails.value.bindTime ?? "" detail.bindTime ?? deviceDetails.value.bindTime ?? ""
).trim(), ).trim(),
@@ -126,9 +130,11 @@ export function useDeviceStatus({
).trim(), ).trim(),
}; };
updateDeviceUsageDuration(detail.onlineDuration, deviceId); updateDeviceUsageDuration(detail.onlineDuration, deviceId);
return detail;
} catch (error) { } catch (error) {
// 实时刷新失败时保留当前页面数据,等待下次通知或页面重新显示。 // 实时刷新失败时保留当前页面数据,等待下次通知或页面重新显示。
console.log("刷新设备详情失败", error); console.log("刷新设备详情失败", error);
return null;
} }
}; };
@@ -143,6 +149,10 @@ export function useDeviceStatus({
let latestDevice = { let latestDevice = {
...currentDevice, ...currentDevice,
deviceModelName: "", deviceModelName: "",
latestVersion: "",
needUpdate: false,
forceUpdate: false,
version: "",
bindTime: "", bindTime: "",
qrCodeUrl: "", qrCodeUrl: "",
}; };
+47 -112
View File
@@ -8,7 +8,6 @@ import ScreenHint from "@/components/ScreenHint.vue";
import ModalDialog from "@/components/ModalDialog.vue"; import ModalDialog from "@/components/ModalDialog.vue";
import OtaModal from "@/components/OtaModal.vue"; import OtaModal from "@/components/OtaModal.vue";
import { import {
getHardwareBoxVersionAPI,
laserAimAPI, laserAimAPI,
updateDeviceAliasAPI, updateDeviceAliasAPI,
} from "@/apis"; } from "@/apis";
@@ -19,7 +18,7 @@ import {
DEVICE_NAME_STORAGE_KEY, DEVICE_NAME_STORAGE_KEY,
useDeviceStatus, useDeviceStatus,
} from "./composables/useDeviceStatus"; } from "./composables/useDeviceStatus";
import { useOtaUpdate } from "@/composables/useOtaUpdate"; import { getOtaInfoFromDetail, useOtaUpdate } from "@/composables/useOtaUpdate";
const store = useStore(); const store = useStore();
const { updateDevice, updateDeviceUsageDuration, clearDevice } = store; const { updateDevice, updateDeviceUsageDuration, clearDevice } = store;
@@ -62,11 +61,8 @@ const showDeviceId = ref(false);
const latestVersionDialogVisible = ref(false); const latestVersionDialogVisible = ref(false);
const firmwareConfirmVisible = ref(false); const firmwareConfirmVisible = ref(false);
const wifiRequiredVisible = ref(false); const wifiRequiredVisible = ref(false);
const otaNeedUpdate = ref(false);
const firmwareActionPending = ref(false); const firmwareActionPending = ref(false);
const pendingOtaInfo = ref(null); const pendingOtaInfo = ref(null);
let otaCheckPromise = null;
let otaCheckRequestVersion = 0;
const { const {
updating: otaUpdating, updating: otaUpdating,
@@ -113,6 +109,12 @@ const wifiStatusText = computed(() => {
const bindingDateText = computed(() => const bindingDateText = computed(() =>
formatBindingDate(deviceDetails.value.bindTime) formatBindingDate(deviceDetails.value.bindTime)
); );
const otaNeedUpdate = computed(() => {
const info = getOtaInfoFromDetail(deviceDetails.value);
return isDeviceOnline.value &&
deviceDetails.value.deviceId === device.value.deviceId &&
info.needUpdate && !!info.versionNumber;
});
// 固件版本统一以大写 V 开头,避免接口返回格式不一致影响弹窗展示。 // 固件版本统一以大写 V 开头,避免接口返回格式不一致影响弹窗展示。
const formatFirmwareVersion = (value) => { const formatFirmwareVersion = (value) => {
@@ -122,18 +124,12 @@ const formatFirmwareVersion = (value) => {
}; };
const firmwareConfirmContent = computed(() => { const firmwareConfirmContent = computed(() => {
const currentVersion = formatFirmwareVersion(deviceStatus.value.version); const currentVersion = formatFirmwareVersion(pendingOtaInfo.value?.currentVersion);
const latestVersion = formatFirmwareVersion( const latestVersion = formatFirmwareVersion(pendingOtaInfo.value?.versionNumber);
pendingOtaInfo.value?.versionNumber
);
if (currentVersion && latestVersion) { if (currentVersion && latestVersion) {
return `当前固件版本为 ${currentVersion},发现新固件版本 ${latestVersion},是否立即更新固件?`; return `当前固件版本为 ${currentVersion},发现新固件版本 ${latestVersion},是否立即更新固件?`;
} }
if (latestVersion) { return latestVersion ? `发现新固件版本 ${latestVersion},是否立即更新固件?` : "是否立即更新固件?";
return `发现新固件版本 ${latestVersion},是否立即更新固件?`;
}
return "发现新固件版本,是否立即更新固件?";
}); });
const deviceIdText = computed(() => const deviceIdText = computed(() =>
@@ -272,84 +268,20 @@ const joinWifi = () => {
uni.navigateTo({ url: "/pages/device/ota-wifi" }); uni.navigateTo({ url: "/pages/device/ota-wifi" });
}; };
const clearOtaState = () => { const closeFirmwareConfirm = () => {
otaCheckRequestVersion += 1; firmwareConfirmVisible.value = false;
otaCheckPromise = null;
otaNeedUpdate.value = false;
};
const loadOtaVersionInfo = async () => {
if (otaCheckPromise) return otaCheckPromise;
const deviceId = device.value.deviceId;
if (!deviceId || !isDeviceOnline.value) return null;
const requestVersion = ++otaCheckRequestVersion;
const request = getHardwareBoxVersionAPI()
.then((versionInfo) => {
if (
requestVersion !== otaCheckRequestVersion ||
device.value.deviceId !== deviceId ||
!isDeviceOnline.value
) {
return null;
}
if (!versionInfo || typeof versionInfo !== "object") {
throw new Error("固件版本信息为空");
}
const needUpdate =
versionInfo.needUpdate === true || Number(versionInfo.needUpdate) === 1;
otaNeedUpdate.value = needUpdate;
return {
versionNumber: versionInfo.versionNumber || "",
resourceUrl: versionInfo.resourceUrl || "",
needUpdate,
};
})
.finally(() => {
if (otaCheckPromise === request) {
otaCheckPromise = null;
}
});
otaCheckPromise = request;
return request;
};
const refreshOtaUpdateState = async () => {
otaNeedUpdate.value = false;
if (!device.value.deviceId || !isDeviceOnline.value) {
clearOtaState();
return;
}
try {
await loadOtaVersionInfo();
} catch (error) {
clearOtaState();
console.log("检查固件更新失败", error);
}
}; };
const closeLatestVersionDialog = () => { const closeLatestVersionDialog = () => {
latestVersionDialogVisible.value = false; latestVersionDialogVisible.value = false;
}; };
const closeFirmwareConfirm = () => {
firmwareConfirmVisible.value = false;
};
const runFirmwareUpdate = () => { const runFirmwareUpdate = () => {
const versionInfo = pendingOtaInfo.value; const otaInfo = pendingOtaInfo.value;
if (!versionInfo) return; if (!otaInfo || otaInfo.deviceId !== device.value.deviceId) return;
void startOtaUpdate({ void startOtaUpdate({
versionNumber: versionInfo.versionNumber, versionNumber: otaInfo.versionNumber,
resourceUrl: versionInfo.resourceUrl, onSuccess: () => void refreshDeviceDetails(),
onSuccess: () => {
otaNeedUpdate.value = false;
},
}); });
}; };
@@ -367,42 +299,43 @@ const confirmFirmwareUpdate = () => {
}; };
const goWifiForFirmwareUpdate = () => { const goWifiForFirmwareUpdate = () => {
const versionInfo = pendingOtaInfo.value; const otaInfo = pendingOtaInfo.value;
if (!versionInfo) return; if (!otaInfo || otaInfo.deviceId !== device.value.deviceId) return;
wifiRequiredVisible.value = false; wifiRequiredVisible.value = false;
const query = [ const versionNumber = encodeURIComponent(otaInfo.versionNumber);
"source=firmware-update", uni.navigateTo({ url: `/pages/device/ota-wifi?source=firmware-update&versionNumber=${versionNumber}` });
`versionNumber=${encodeURIComponent(versionInfo.versionNumber)}`,
`resourceUrl=${encodeURIComponent(versionInfo.resourceUrl)}`,
].join("&");
uni.navigateTo({ url: `/pages/device/ota-wifi?${query}` });
}; };
const handleOtaResultClose = () => { const handleOtaResultClose = () => {
closeOtaResult(); closeOtaResult();
void refreshOtaUpdateState(); void refreshDeviceDetails();
}; };
const goFirmwareUpdate = async () => { const goFirmwareUpdate = async () => {
if (firmwareActionPending.value) return; if (firmwareActionPending.value || otaUpdating.value) return;
if (!isDeviceOnline.value) { if (!isDeviceOnline.value) {
uni.showToast({ title: "请先开启智能弓", icon: "none" }); uni.showToast({ title: "请先开启智能弓", icon: "none" });
return; return;
} }
firmwareActionPending.value = true; firmwareActionPending.value = true;
const deviceId = device.value.deviceId;
try { try {
const versionInfo = await loadOtaVersionInfo(); const detail = await refreshDeviceDetails();
if (!versionInfo) return; if (!detail || device.value.deviceId !== deviceId) {
if (!versionInfo.needUpdate) { uni.showToast({ title: "获取设备详情失败,请重试", icon: "none" });
return;
}
const otaInfo = getOtaInfoFromDetail(detail);
if (!otaInfo.needUpdate) {
latestVersionDialogVisible.value = true; latestVersionDialogVisible.value = true;
return; return;
} }
if (!otaInfo.versionNumber) {
pendingOtaInfo.value = versionInfo; uni.showToast({ title: "缺少固件目标版本,请重试", icon: "none" });
return;
}
pendingOtaInfo.value = { ...otaInfo, deviceId };
firmwareConfirmVisible.value = true; firmwareConfirmVisible.value = true;
} catch (error) {
uni.showToast({ title: "获取更新版本失败,请重试", icon: "none" });
} finally { } finally {
firmwareActionPending.value = false; firmwareActionPending.value = false;
} }
@@ -451,15 +384,20 @@ watch(
(isOnline, wasOnline) => { (isOnline, wasOnline) => {
if (isOnline && !wasOnline) { if (isOnline && !wasOnline) {
void refreshDeviceDetails(); void refreshDeviceDetails();
void refreshOtaUpdateState(); } else if (!isOnline) {
return; firmwareConfirmVisible.value = false;
} wifiRequiredVisible.value = false;
if (!isOnline) {
clearOtaState();
} }
} }
); );
watch(() => device.value.deviceId, () => {
pendingOtaInfo.value = null;
latestVersionDialogVisible.value = false;
firmwareConfirmVisible.value = false;
wifiRequiredVisible.value = false;
});
onLoad((options = {}) => { onLoad((options = {}) => {
retryScanOnShow.value = options.retryScan === "1"; retryScanOnShow.value = options.retryScan === "1";
}); });
@@ -475,7 +413,6 @@ onUnmounted(() => {
onShow(async () => { onShow(async () => {
calibration.value = uni.getStorageSync("calibration"); calibration.value = uni.getStorageSync("calibration");
await syncDeviceBinding(); await syncDeviceBinding();
void refreshOtaUpdateState();
if (retryScanOnShow.value) { if (retryScanOnShow.value) {
retryScanOnShow.value = false; retryScanOnShow.value = false;
handleScan(); handleScan();
@@ -607,10 +544,7 @@ onShow(async () => {
</view> </view>
<view class="action-label-wrap"> <view class="action-label-wrap">
<text>固件更新</text> <text>固件更新</text>
<text <text v-if="otaNeedUpdate" class="action-badge action-badge--new">New</text>
v-if="otaNeedUpdate"
class="action-badge action-badge--new"
>New</text>
</view> </view>
</view> </view>
<view class="action-item" @click="joinWifi"> <view class="action-item" @click="joinWifi">
@@ -724,6 +658,7 @@ onShow(async () => {
:content="firmwareConfirmContent" :content="firmwareConfirmContent"
cancelText="暂不更新" cancelText="暂不更新"
confirmText="立即更新" confirmText="立即更新"
:showCancel="!pendingOtaInfo?.forceUpdate"
:onCancel="closeFirmwareConfirm" :onCancel="closeFirmwareConfirm"
:onConfirm="confirmFirmwareUpdate" :onConfirm="confirmFirmwareUpdate"
></ModalDialog> ></ModalDialog>
+27 -32
View File
@@ -9,10 +9,10 @@ import useStore from "@/store";
import { capsuleHeight } from "@/util"; import { capsuleHeight } from "@/util";
import { import {
connectDeviceWifiAPI, connectDeviceWifiAPI,
getDeviceDetailAPI,
getDeviceWifiStatusAPI, getDeviceWifiStatusAPI,
getHardwareBoxVersionAPI,
} from "@/apis"; } from "@/apis";
import { useOtaUpdate } from "@/composables/useOtaUpdate"; import { getOtaInfoFromDetail, useOtaUpdate } from "@/composables/useOtaUpdate";
const STATES = { const STATES = {
SCANNING: "SCANNING", SCANNING: "SCANNING",
@@ -23,7 +23,7 @@ const STATES = {
}; };
const isIOS = uni.getDeviceInfo().osName === "ios"; const isIOS = uni.getDeviceInfo().osName === "ios";
const { deviceStatus } = storeToRefs(useStore()); const { device, deviceStatus } = storeToRefs(useStore());
const currentState = ref(STATES.SCANNING); const currentState = ref(STATES.SCANNING);
const connectedWifi = ref(null); const connectedWifi = ref(null);
const connectedWifiName = ref(""); const connectedWifiName = ref("");
@@ -45,10 +45,7 @@ const showPassword = ref(false);
// 刷新防抖标志:扫描进行中为 true,禁止重复点击;扫描结束(成功/失败)后重置为 false。 // 刷新防抖标志:扫描进行中为 true,禁止重复点击;扫描结束(成功/失败)后重置为 false。
const isRefreshing = ref(false); const isRefreshing = ref(false);
const fromFirmwareUpdate = ref(false); const fromFirmwareUpdate = ref(false);
const routeOtaInfo = ref({ const routeOtaVersionNumber = ref("");
versionNumber: "",
resourceUrl: "",
});
const countdownVisible = ref(false); const countdownVisible = ref(false);
const countdownSeconds = ref(3); const countdownSeconds = ref(3);
const firmwareMessageVisible = ref(false); const firmwareMessageVisible = ref(false);
@@ -395,37 +392,38 @@ const joinNetwork = () => {
submitDeviceWifiConfig({ ssid, password }); submitDeviceWifiConfig({ ssid, password });
}; };
// 获取 OTA 更新版本信息,正常使用入口传参,缺失时再请求后端兜底。
const getOtaVersionInfo = async () => {
if (routeOtaInfo.value.versionNumber && routeOtaInfo.value.resourceUrl) {
return {
needUpdate: true,
versionNumber: routeOtaInfo.value.versionNumber,
resourceUrl: routeOtaInfo.value.resourceUrl,
};
}
return getHardwareBoxVersionAPI();
};
const startFirmwareUpdate = async () => { const startFirmwareUpdate = async () => {
if (!fromFirmwareUpdate.value || !connectedWifi.value || otaUpdating.value) return; if (!fromFirmwareUpdate.value || !connectedWifi.value || otaUpdating.value) return;
const deviceId = device.value.deviceId;
const wifi = connectedWifi.value;
if (!deviceId) return;
try { try {
const versionInfo = await getOtaVersionInfo(); const response = await getDeviceDetailAPI(deviceId);
if (!versionInfo?.needUpdate) { const detail = response?.detail || response?.data?.detail;
if (!detail || device.value.deviceId !== deviceId) throw new Error("设备详情已失效");
const otaInfo = getOtaInfoFromDetail(detail);
if (!otaInfo.needUpdate) {
firmwareMessage.value = "当前已是最新版本"; firmwareMessage.value = "当前已是最新版本";
firmwareMessageVisible.value = true; firmwareMessageVisible.value = true;
return; return;
} }
await startOtaUpdate({ if (!otaInfo.versionNumber) {
versionNumber: versionInfo.versionNumber, firmwareMessage.value = "缺少固件目标版本,请返回后重试";
wifiSsid: connectedWifi.value.SSID, firmwareMessageVisible.value = true;
wifiPassword: connectedWifi.value.password || "", return;
resourceUrl: versionInfo.resourceUrl, }
}); // WiFi 连接可能耗时较长,以更新前的最新设备详情为准。
routeOtaVersionNumber.value = otaInfo.versionNumber;
} catch (error) { } catch (error) {
firmwareMessage.value = "获取更新版本失败,请重试"; firmwareMessage.value = "获取设备详情失败,请返回后重试";
firmwareMessageVisible.value = true; firmwareMessageVisible.value = true;
return;
} }
await startOtaUpdate({
versionNumber: routeOtaVersionNumber.value,
wifiSsid: wifi.SSID,
wifiPassword: wifi.password || "",
});
}; };
const clearFirmwareCountdown = () => { const clearFirmwareCountdown = () => {
@@ -466,10 +464,7 @@ const togglePasswordVisibility = () => {
// 页面加载时识别普通 WiFi 入口和固件更新入口。 // 页面加载时识别普通 WiFi 入口和固件更新入口。
onLoad((options = {}) => { onLoad((options = {}) => {
fromFirmwareUpdate.value = options.source === "firmware-update"; fromFirmwareUpdate.value = options.source === "firmware-update";
routeOtaInfo.value = { routeOtaVersionNumber.value = decodeURIComponent(options.versionNumber || "");
versionNumber: decodeURIComponent(options.versionNumber || ""),
resourceUrl: decodeURIComponent(options.resourceUrl || ""),
};
}); });
// 页面挂载后启动 WiFi 扫描流程。 // 页面挂载后启动 WiFi 扫描流程。
+91 -147
View File
@@ -13,14 +13,14 @@ import OtaModal from "@/components/OtaModal.vue";
import { import {
checkUserBindAPI, checkUserBindAPI,
getAppConfig, getAppConfig,
getHardwareBoxVersionAPI, getDeviceDetailAPI,
getHomeData, getHomeData,
getMyDevicesAPI, getMyDevicesAPI,
getScoreRankList, getScoreRankList,
silentLoginAPI, silentLoginAPI,
} from "@/apis"; } from "@/apis";
import {topThreeColors} from "@/constants"; import {topThreeColors} from "@/constants";
import {useOtaUpdate} from "@/composables/useOtaUpdate"; import {getOtaInfoFromDetail, useOtaUpdate} from "@/composables/useOtaUpdate";
import useStore from "@/store"; import useStore from "@/store";
import {storeToRefs} from "pinia"; import {storeToRefs} from "pinia";
@@ -74,16 +74,9 @@ const isDeviceCharging = computed(
() => deviceCardState.value === "online" && deviceStatus.value?.charging === true () => deviceCardState.value === "online" && deviceStatus.value?.charging === true
); );
// OTA 相关
const otaVisible = ref(false); const otaVisible = ref(false);
const wifiRequiredVisible = ref(false); const wifiRequiredVisible = ref(false);
const otaInfo = ref({ const otaInfo = ref({ versionNumber: "", versionInfo: "", needUpdate: false, forceUpdate: false });
versionNumber: "",
versionInfo: "",
resourceUrl: "",
forceUpdate: false,
needUpdate: false,
});
const isStartingOta = ref(false); const isStartingOta = ref(false);
let isCheckingOta = false; let isCheckingOta = false;
let otaCheckQueuedForOnline = false; let otaCheckQueuedForOnline = false;
@@ -98,157 +91,122 @@ const {
closeResult: closeOtaResult, closeResult: closeOtaResult,
} = useOtaUpdate(); } = useOtaUpdate();
// 获取并保存后端返回的 OTA 版本信息,供弹窗展示和更新接口使用 // 首页仅用绑定设备详情判断是否需要升级,不再请求旧版本接口
const applyOtaVersionInfo = (versionInfo) => {
otaInfo.value = {
versionNumber: versionInfo?.versionNumber || "",
versionInfo: versionInfo?.versionInfo || "",
resourceUrl: versionInfo?.resourceUrl || "",
forceUpdate: Number(versionInfo?.forceUpdate) === 1,
needUpdate:
versionInfo?.needUpdate === true || Number(versionInfo?.needUpdate) === 1,
};
};
// 检查当前设备盒子是否存在可升级版本。
const checkOtaUpdate = async () => { const checkOtaUpdate = async () => {
if ( if (isCheckingOta || otaVisible.value || otaUpdating.value || otaResultVisible.value) return;
isCheckingOta || const deviceId = String(device.value.deviceId || "");
otaVisible.value || if (!user.value.id || !deviceId || online.value !== true) return;
otaUpdating.value ||
otaResultVisible.value
) return;
isCheckingOta = true; isCheckingOta = true;
try { try {
if ( const response = await getDeviceDetailAPI(deviceId);
online.value !== true || const detail = response?.detail || response?.data?.detail;
otaVisible.value || if (!detail || device.value.deviceId !== deviceId || online.value !== true) return;
otaUpdating.value ||
otaResultVisible.value
) return;
let versionInfo;
try {
versionInfo = await getHardwareBoxVersionAPI();
} catch (err) {
return;
}
if (otaVisible.value || otaUpdating.value || otaResultVisible.value) return; if (otaVisible.value || otaUpdating.value || otaResultVisible.value) return;
applyOtaVersionInfo(versionInfo); otaInfo.value = getOtaInfoFromDetail(detail);
if (!otaInfo.value.needUpdate) return; if (!otaInfo.value.needUpdate || !otaInfo.value.versionNumber) return;
const dismissedAt = uni.getStorageSync("ota_dismissed_at"); const dismissed = uni.getStorageSync("ota_dismissed_at");
const now = Date.now(); const recentlyDismissed = dismissed?.deviceId === deviceId &&
if (!otaInfo.value.forceUpdate && dismissedAt && now - dismissedAt < 24 * 60 * 60 * 1000) return; dismissed?.versionNumber === otaInfo.value.versionNumber &&
Date.now() - Number(dismissed?.at || 0) < 24 * 60 * 60 * 1000;
if (!otaInfo.value.forceUpdate && recentlyDismissed) return;
otaVisible.value = true; otaVisible.value = true;
} catch (error) {
console.log("检查固件更新失败", error);
} finally { } finally {
isCheckingOta = false; isCheckingOta = false;
const shouldRecheckForOnline = otaCheckQueuedForOnline; if (otaCheckQueuedForOnline) {
otaCheckQueuedForOnline = false; otaCheckQueuedForOnline = false;
if (
shouldRecheckForOnline &&
!otaVisible.value &&
!otaUpdating.value &&
!otaResultVisible.value
) {
void checkOtaUpdate(); void checkOtaUpdate();
} }
} }
}; };
// 设备由 WS 通知上线时补查 OTA;已显示任何 OTA 弹窗时保留当前流程和结果。
watch(online, (nextOnline, previousOnline) => { watch(online, (nextOnline, previousOnline) => {
if ( if (!nextOnline) {
previousOnline !== false || otaVisible.value = false;
nextOnline !== true ||
otaVisible.value ||
otaUpdating.value ||
otaResultVisible.value
) return;
if (isCheckingOta) {
otaCheckQueuedForOnline = true;
return; return;
} }
void checkOtaUpdate(); if (previousOnline === false) {
if (isCheckingOta) otaCheckQueuedForOnline = true;
else void checkOtaUpdate();
}
}); });
// 任一页面发起或恢复 OTA 后,关闭首页的版本发现弹窗,避免两层弹窗重叠。 watch(() => device.value.deviceId, () => {
watch([otaUpdating, otaResultVisible], ([updating, resultVisible]) => {
if (!updating && !resultVisible) return;
otaVisible.value = false; otaVisible.value = false;
isStartingOta.value = false; otaInfo.value = { versionNumber: "", versionInfo: "", needUpdate: false, forceUpdate: false };
}); });
// 拼接 OTA WiFi 页参数,让未连 WiFi 的设备继续使用同一份版本信息。 watch([otaUpdating, otaResultVisible], ([updating, resultVisible]) => {
const getOtaWifiUrl = () => { if (updating || resultVisible) otaVisible.value = false;
const { versionNumber, resourceUrl } = otaInfo.value; });
const query = [
"source=firmware-update", const getOtaWifiUrl = () =>
`versionNumber=${encodeURIComponent(versionNumber)}`, `/pages/device/ota-wifi?source=firmware-update&versionNumber=${encodeURIComponent(otaInfo.value.versionNumber)}`;
`resourceUrl=${encodeURIComponent(resourceUrl)}`,
].join("&");
return `/pages/device/ota-wifi?${query}`;
};
// 处理 OTA 弹窗暂不更新,强制更新时不允许关闭。
const handleOtaDismiss = () => { const handleOtaDismiss = () => {
if (otaInfo.value.forceUpdate) return; if (otaInfo.value.forceUpdate) return;
uni.setStorageSync("ota_dismissed_at", Date.now()); uni.setStorageSync("ota_dismissed_at", {
otaVisible.value = false; deviceId: device.value.deviceId,
};
// 设备盒子已连 WiFi 时,通过共享 OTA 状态发起更新。
const startHomeOtaUpdate = async () => {
otaVisible.value = false;
await startOtaUpdate({
versionNumber: otaInfo.value.versionNumber, versionNumber: otaInfo.value.versionNumber,
resourceUrl: otaInfo.value.resourceUrl, at: Date.now(),
onSuccess: () => {
otaInfo.value = {...otaInfo.value, needUpdate: false};
},
}); });
isStartingOta.value = false; otaVisible.value = false;
}; };
// 点击立即更新时先判断设备是否在线并已通过 WiFi 联网,未联网时先展示连接引导 // 点击更新或失败重试时检查设备网络状态,并使用详情中的目标版本号
const handleOtaUpdate = async () => { const handleOtaUpdate = async () => {
if (isStartingOta.value) return; if (isStartingOta.value) return;
isStartingOta.value = true; isStartingOta.value = true;
const currentDeviceStatus = deviceStatus.value; const deviceId = String(device.value.deviceId || "");
try {
if (currentDeviceStatus?.online !== true) { if (!deviceId || deviceStatus.value?.online !== true) {
uni.showToast({ title: "请先开启智能弓", icon: "none" });
return;
}
const response = await getDeviceDetailAPI(deviceId);
const detail = response?.detail || response?.data?.detail;
if (!detail || device.value.deviceId !== deviceId) {
uni.showToast({ title: "获取设备详情失败,请重试", icon: "none" });
return;
}
otaInfo.value = getOtaInfoFromDetail(detail);
if (!otaInfo.value.needUpdate) {
otaVisible.value = false;
uni.showToast({ title: "当前已是最新版本", icon: "none" });
return;
}
if (!otaInfo.value.versionNumber) {
uni.showToast({ title: "缺少固件目标版本", icon: "none" });
return;
}
const currentDeviceStatus = deviceStatus.value;
if (currentDeviceStatus?.online !== true) {
uni.showToast({ title: "请先开启智能弓", icon: "none" });
return;
}
if (Date.now() - Number(currentDeviceStatus?.receivedAt || 0) > DEVICE_STATUS_STALE_TIME) {
uni.showToast({ title: "设备状态同步中,请稍后重试", icon: "none" });
return;
}
otaVisible.value = false;
if (String(currentDeviceStatus?.netType || "").toLowerCase() === "wifi") {
await startOtaUpdate({
versionNumber: otaInfo.value.versionNumber,
onSuccess: () => { otaInfo.value = { ...otaInfo.value, needUpdate: false }; },
});
} else {
wifiRequiredVisible.value = true;
}
} catch (error) {
uni.showToast({ title: "获取设备详情失败,请重试", icon: "none" });
} finally {
isStartingOta.value = false; isStartingOta.value = false;
uni.showToast({
title: "请先开启智能弓",
icon: "none",
});
return;
} }
if (
Date.now() - Number(currentDeviceStatus?.receivedAt || 0) >
DEVICE_STATUS_STALE_TIME
) {
isStartingOta.value = false;
uni.showToast({
title: "设备状态同步中,请稍后重试",
icon: "none",
});
return;
}
if (String(currentDeviceStatus?.netType || "").toLowerCase() === "wifi") {
startHomeOtaUpdate();
return;
}
isStartingOta.value = false;
otaVisible.value = false;
wifiRequiredVisible.value = true;
}; };
// 从首页固件更新提示进入 WiFi 配置页,并继续沿用当前版本信息 // 从首页更新失败提示进入 WiFi 配置页。
const goWifiForOtaUpdate = () => { const goWifiForOtaUpdate = () => {
wifiRequiredVisible.value = false; wifiRequiredVisible.value = false;
uni.navigateTo({ url: getOtaWifiUrl() }); uni.navigateTo({ url: getOtaWifiUrl() });
@@ -257,22 +215,13 @@ const goWifiForOtaUpdate = () => {
// 处理 OTA 更新成功后的完成按钮,关闭共享结果弹窗。 // 处理 OTA 更新成功后的完成按钮,关闭共享结果弹窗。
const handleOtaDone = () => { const handleOtaDone = () => {
closeOtaResult(); closeOtaResult();
otaInfo.value = { ...otaInfo.value, needUpdate: false };
}; };
// 处理共享 OTA 更新失败后的重试,先刷新版本参数再重新判断网络状态 // 处理共享 OTA 更新失败后的重试。
const handleOtaRetry = async () => { const handleOtaRetry = async () => {
closeOtaResult(); closeOtaResult();
try { await handleOtaUpdate();
const versionInfo = await getHardwareBoxVersionAPI();
applyOtaVersionInfo(versionInfo);
if (!otaInfo.value.needUpdate) return;
await handleOtaUpdate();
} catch (error) {
uni.showToast({
title: "获取更新版本失败,请重试",
icon: "none",
});
}
}; };
// 提取积分榜接口返回的榜单数组,兼容数组和对象两种返回格式。 // 提取积分榜接口返回的榜单数组,兼容数组和对象两种返回格式。
@@ -346,10 +295,6 @@ onShow(async () => {
const env = uni.getAccountInfoSync().miniProgram.envVersion; const env = uni.getAccountInfoSync().miniProgram.envVersion;
const token = uni.getStorageSync(`${env}_token`); const token = uni.getStorageSync(`${env}_token`);
if (token || user.value.id) {
await checkOtaUpdate();
}
if (!user.value.id && !token) { if (!user.value.id && !token) {
// showModal.value = true; // showModal.value = true;
// try { // try {
@@ -401,6 +346,7 @@ onShow(async () => {
}, 3000); }, 3000);
} }
await syncHomeDevice(); await syncHomeDevice();
await checkOtaUpdate();
} }
} }
@@ -432,12 +378,10 @@ onShareTimeline(() => {
<template> <template>
<Container :isHome="true" :showBackToGame="true"> <Container :isHome="true" :showBackToGame="true">
<!-- 首页版本发现弹窗仅负责触发更新执行状态由三个页面共享 -->
<OtaModal <OtaModal
:visible="otaVisible && !otaUpdating && !otaResultVisible" :visible="otaVisible && !otaUpdating && !otaResultVisible"
state="new_version" state="new_version"
:version="otaInfo.versionNumber" :version="otaInfo.versionNumber"
:description="''"
:changelog="otaInfo.versionInfo" :changelog="otaInfo.versionInfo"
:forceUpdate="otaInfo.forceUpdate" :forceUpdate="otaInfo.forceUpdate"
@update="handleOtaUpdate" @update="handleOtaUpdate"
@@ -533,9 +477,9 @@ onShareTimeline(() => {
:class="{ 'device-status-badge--charging': isDeviceCharging }" :class="{ 'device-status-badge--charging': isDeviceCharging }"
@click.stop="$clickSound(() => toPage('/pages/device/my-device'))" @click.stop="$clickSound(() => toPage('/pages/device/my-device'))"
> >
<image <image
v-if="otaInfo.needUpdate" v-if="otaInfo.needUpdate && otaInfo.versionNumber"
class="device-status-refresh" class="device-status-refresh"
src="https://static.shelingxingqiu.com/shootmini/static/home-device/device-state-refresh.png" src="https://static.shelingxingqiu.com/shootmini/static/home-device/device-state-refresh.png"
mode="scaleToFill" mode="scaleToFill"
/> />