update:新增ota

This commit is contained in:
2026-09-21 14:56:43 +08:00
parent b060d8f987
commit 4bd7599cc0
11 changed files with 652 additions and 410 deletions
+181 -27
View File
@@ -5,6 +5,7 @@ import Container from "@/components/Container.vue";
import Header from "@/components/Header.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import ModalDialog from "@/components/ModalDialog.vue";
import OtaModal from "@/components/OtaModal.vue";
import {
getHardwareBoxVersionAPI,
laserAimAPI,
@@ -17,10 +18,12 @@ import {
DEVICE_NAME_STORAGE_KEY,
useDeviceStatus,
} from "./composables/useDeviceStatus";
import { useOtaUpdate } from "./composables/useOtaUpdate";
const store = useStore();
const { updateDevice, clearDevice } = store;
const { user, device, deviceStatus, online } = storeToRefs(store);
const DEVICE_NAME_MAX_LENGTH = 10;
const formatBindingDate = (value) => {
if (!value) return "--";
@@ -56,11 +59,26 @@ const retryScanOnShow = ref(false);
const calibration = ref(false);
const showDeviceId = ref(false);
const latestVersionDialogVisible = ref(false);
const firmwareConfirmVisible = ref(false);
const wifiRequiredVisible = ref(false);
const otaNeedUpdate = ref(false);
const firmwareActionPending = ref(false);
const pendingOtaInfo = ref(null);
let otaCheckPromise = null;
let otaCheckRequestVersion = 0;
const {
updating: otaUpdating,
progress: otaProgress,
phase: otaPhase,
resultVisible: otaResultVisible,
resultStatus: otaResultStatus,
resultTitle: otaResultTitle,
resultContent: otaResultContent,
startUpdate: startOtaUpdate,
closeResult: closeOtaResult,
} = useOtaUpdate();
const {
batteryText,
deviceDetails,
@@ -94,8 +112,8 @@ const bindingDateText = computed(() =>
);
const deviceIdText = computed(() =>
showDeviceId.value && device.value.deviceId
? device.value.deviceId
showDeviceId.value && device.value.deviceName
? device.value.deviceName
: maskedDeviceId.value
);
@@ -107,9 +125,11 @@ const designDeviceStats = computed(() => [
},
{
label: "剩余电量",
value: batteryText.value === "暂无数据" || !isDeviceOnline.value
? "--"
: batteryText.value,
value: !isDeviceOnline.value
? "设备离线"
: batteryText.value === "暂无数据"
? "--"
: batteryText.value,
},
{
label: "累计使用",
@@ -150,6 +170,14 @@ const closeNameEditor = () => {
nameEditorVisible.value = false;
};
const editingNameLength = computed(() =>
Array.from(String(editingName.value || "")).length
);
const isEditingNameTooLong = computed(
() => editingNameLength.value > DEVICE_NAME_MAX_LENGTH
);
const toggleDeviceId = () => {
if (device.value.deviceId) showDeviceId.value = !showDeviceId.value;
};
@@ -162,8 +190,15 @@ const confirmName = async () => {
uni.showToast({ title: "请输入设备名", icon: "none" });
return;
}
if (name.length > 10 || !/^[\u4e00-\u9fa5A-Za-z0-9_-]+$/.test(name)) {
uni.showToast({ title: "设备名格式不正确", icon: "none" });
if (Array.from(name).length > DEVICE_NAME_MAX_LENGTH) {
uni.showToast({ title: "设备名最多支持10个字符", icon: "none" });
return;
}
if (!/^[\u4e00-\u9fa5A-Za-z0-9_-]+$/.test(name)) {
uni.showToast({
title: "仅支持中文、英文、数字、下划线和减号",
icon: "none",
});
return;
}
@@ -206,7 +241,7 @@ const toDeviceIntroPage = () => {
const joinWifi = () => {
if (!isDeviceOnline.value) {
uni.showToast({ title: "请先开启智能弓", icon: "none" });
uni.showToast({ title: "请先开启智能弓", icon: "none" });
return;
}
uni.navigateTo({ url: "/pages/device/ota-wifi" });
@@ -277,6 +312,52 @@ const closeLatestVersionDialog = () => {
latestVersionDialogVisible.value = false;
};
const closeFirmwareConfirm = () => {
firmwareConfirmVisible.value = false;
};
const runFirmwareUpdate = () => {
const versionInfo = pendingOtaInfo.value;
if (!versionInfo) return;
void startOtaUpdate({
versionNumber: versionInfo.versionNumber,
resourceUrl: versionInfo.resourceUrl,
onSuccess: () => {
otaNeedUpdate.value = false;
},
});
};
const confirmFirmwareUpdate = () => {
firmwareConfirmVisible.value = false;
if (!isDeviceOnline.value) {
uni.showToast({ title: "请先开启智能弓", icon: "none" });
return;
}
if (String(networkType.value || "").toLowerCase() === "wifi") {
runFirmwareUpdate();
return;
}
wifiRequiredVisible.value = true;
};
const goWifiForFirmwareUpdate = () => {
const versionInfo = pendingOtaInfo.value;
if (!versionInfo) return;
wifiRequiredVisible.value = false;
const query = [
"source=firmware-update",
`versionNumber=${encodeURIComponent(versionInfo.versionNumber)}`,
`resourceUrl=${encodeURIComponent(versionInfo.resourceUrl)}`,
].join("&");
uni.navigateTo({ url: `/pages/device/ota-wifi?${query}` });
};
const handleOtaResultClose = () => {
closeOtaResult();
void refreshOtaUpdateState();
};
const goFirmwareUpdate = async () => {
if (firmwareActionPending.value) return;
if (!isDeviceOnline.value) {
@@ -293,11 +374,8 @@ const goFirmwareUpdate = async () => {
return;
}
const query = [
`versionNumber=${encodeURIComponent(versionInfo.versionNumber)}`,
`resourceUrl=${encodeURIComponent(versionInfo.resourceUrl)}`,
].join("&");
uni.navigateTo({ url: `/pages/device/ota-wifi?${query}` });
pendingOtaInfo.value = versionInfo;
firmwareConfirmVisible.value = true;
} catch (error) {
uni.showToast({ title: "获取更新版本失败,请重试", icon: "none" });
} finally {
@@ -307,7 +385,7 @@ const goFirmwareUpdate = async () => {
const goCalibration = async () => {
if (!isDeviceOnline.value) {
uni.showToast({ title: "请先开启智能弓", icon: "none" });
uni.showToast({ title: "请先开启智能弓", icon: "none" });
return;
}
try {
@@ -546,10 +624,16 @@ onShow(async () => {
</view>
</view>
<ScreenHint mode="square" :show="showTip" :onClose="closeTip">
<ModalDialog
:show="showTip"
:showCancel="false"
:showConfirm="false"
:showClose="true"
:onClose="closeTip"
>
<view class="scan-tips">
<text class="scan-tips-title">扫码绑定射灵弓箭</text>
<text class="scan-tips-subtitle">设备底部二维码样例</text>
<text class="scan-tips-subtitle">配套令牌样例</text>
<image
class="scan-tips-qr"
src="../../static/device-assets/my-device-unbound-qr-sample.png"
@@ -561,7 +645,7 @@ onShow(async () => {
<button hover-class="none" @click="copyEmail">shelingxingqiu@163.com</button>
</view>
</view>
</ScreenHint>
</ModalDialog>
<ScreenHint :show="confirmBindTip" :onClose="closeConfirmBindTip">
<view class="confirm-bind">
@@ -598,6 +682,47 @@ onShow(async () => {
:onConfirm="closeLatestVersionDialog"
></ModalDialog>
<ModalDialog
:show="firmwareConfirmVisible"
title="固件更新"
content="是否现在进行固件更新?"
cancelText="取消"
confirmText="确定"
:onCancel="closeFirmwareConfirm"
:onConfirm="confirmFirmwareUpdate"
></ModalDialog>
<ModalDialog
:show="wifiRequiredVisible"
title="固件更新"
content="请在WiFi网络下更新"
confirmText="连接WiFi"
:showCancel="false"
:onConfirm="goWifiForFirmwareUpdate"
></ModalDialog>
<OtaModal
:visible="otaUpdating"
state="update_progress"
:progress="otaProgress"
:phase="otaPhase"
/>
<OtaModal
:visible="otaResultVisible && otaResultStatus === 'success'"
state="update_success"
@done="handleOtaResultClose"
/>
<ModalDialog
:show="otaResultVisible && otaResultStatus === 'failed'"
:title="otaResultTitle"
:content="otaResultContent"
confirmText="关闭"
:showCancel="false"
:onConfirm="handleOtaResultClose"
></ModalDialog>
<view v-if="nameEditorVisible" class="name-mask" @click="closeNameEditor">
<view class="name-panel" @click.stop>
<image
@@ -613,9 +738,9 @@ onShow(async () => {
focus
:cursor-spacing="24"
:disabled="renaming"
maxlength="10"
:maxlength="-1"
confirm-type="done"
placeholder="请输入设备名(最长10个汉字)"
placeholder="请输入设备名"
placeholder-class="name-placeholder"
@confirm="confirmName"
/>
@@ -631,7 +756,13 @@ onShow(async () => {
/>
</view>
</view>
<text class="name-helper">仅支持中文英文数字下划线减号</text>
<view class="name-meta">
<text class="name-helper">仅支持中文英文数字下划线减号</text>
<text
class="name-count"
:class="{ 'name-count--exceeded': isEditingNameTooLong }"
>{{ editingNameLength }}/{{ DEVICE_NAME_MAX_LENGTH }}</text>
</view>
</view>
</view>
</view>
@@ -661,7 +792,7 @@ onShow(async () => {
.device-nav .device-status {
position: absolute;
top: calc(100% + 28rpx);
top: calc(100% + 0);
left: 50%;
white-space: nowrap;
transform: translateX(-50%);
@@ -851,7 +982,7 @@ onShow(async () => {
display: flex;
align-items: center;
color: rgba(255, 255, 255, 0.9);
font-size: 24rpx;
font-size: 18rpx;
line-height: 30rpx;
}
@@ -1120,12 +1251,13 @@ onShow(async () => {
.scan-tips {
display: flex;
width: 90%;
width: 100%;
box-sizing: border-box;
flex-direction: column;
margin-top: 20%;
align-items: flex-start;
color: #ffffff;
font-size: 24rpx;
text-align: left;
}
.scan-tips-title {
@@ -1276,12 +1408,34 @@ onShow(async () => {
opacity: 0.6;
}
.name-helper {
display: block;
.name-meta {
display: flex;
width: 100%;
max-width: 620rpx;
align-items: flex-start;
justify-content: space-between;
margin-top: 22rpx;
margin-right: auto;
margin-left: auto;
gap: 20rpx;
}
.name-helper,
.name-count {
color: rgba(255, 255, 255, 0.62);
font-size: 24rpx;
line-height: 34rpx;
text-align: center;
}
.name-helper {
flex: 1;
}
.name-count {
flex-shrink: 0;
}
.name-count--exceeded {
color: #fed847;
}
</style>