fix:OTA弹窗+wifi页面结构样式修改
1
.gitignore
vendored
@@ -10,6 +10,7 @@ lerna-debug.log*
|
|||||||
node_modules
|
node_modules
|
||||||
.history
|
.history
|
||||||
.github
|
.github
|
||||||
|
.claude
|
||||||
openspec
|
openspec
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
docs
|
docs
|
||||||
|
|||||||
350
src/components/OtaModal.vue
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
type: String,
|
||||||
|
default: "new_version", // new_version | update_success | update_failure
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 副标题:如“新版本将优化智能弓体验”
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 详细说明:如“升级前请确保:...”
|
||||||
|
changelog: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
forceUpdate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update", "skip", "close", "done", "retry"]);
|
||||||
|
|
||||||
|
const isNewVersion = computed(() => props.state === "new_version");
|
||||||
|
const isSuccess = computed(() => props.state === "update_success");
|
||||||
|
const isFailure = computed(() => props.state === "update_failure");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view v-if="visible" class="ota-mask">
|
||||||
|
<!-- 图标 + 弹窗卡片 容器 -->
|
||||||
|
<view
|
||||||
|
class="ota-outer"
|
||||||
|
:class="isNewVersion ? 'outer-new' : 'outer-result'"
|
||||||
|
>
|
||||||
|
<!-- 悬浮图标(溢出卡片顶部) -->
|
||||||
|
<image
|
||||||
|
v-if="isNewVersion"
|
||||||
|
src="../static/ota/ota-mascot.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
class="float-icon float-mascot"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
v-else-if="isSuccess"
|
||||||
|
src="../static/ota/check-char.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
class="float-icon float-check"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
v-else-if="isFailure"
|
||||||
|
src="../static/ota/close-char.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
class="float-icon float-close"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 弹窗卡片:overflow:visible 允许按钮溢出底部,背景图通过 ota-bg-clip 独立裁剪保持圆角 -->
|
||||||
|
<view class="ota-dialog">
|
||||||
|
<view class="ota-bg-clip">
|
||||||
|
<image src="../static/ota/ota-bg.png" mode="aspectFill" class="ota-bg" />
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="ota-content"
|
||||||
|
:class="{ 'content-new': isNewVersion, 'content-result': isSuccess || isFailure }"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- 发现新版本:new-ver.png 已包含标题图,不再重复文字;版本号使用 ota-ver.png 胶囊背景 -->
|
||||||
|
<block v-if="isNewVersion">
|
||||||
|
<image src="../static/ota/new-ver.png" mode="aspectFit" class="new-ver-img" />
|
||||||
|
<view v-if="version" class="version-tag-wrap">
|
||||||
|
<image src="../static/ota/ota-ver.png" mode="aspectFill" class="version-tag-bg-img" />
|
||||||
|
<text class="version-tag">{{ version }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 副标题:如“新版本将优化智能弓体验”,离下方详情 12rpx -->
|
||||||
|
<text v-if="description" class="desc-text">{{ description }}</text>
|
||||||
|
<!-- 详细说明:如“升级前请确保:...” -->
|
||||||
|
<text v-if="changelog" class="changelog-text">{{ changelog }}</text>
|
||||||
|
<view class="btn-group">
|
||||||
|
<view class="primary-btn" @click="emit('update')">
|
||||||
|
<text class="primary-btn-text">立即更新</text>
|
||||||
|
</view>
|
||||||
|
<text v-if="!forceUpdate" class="skip-text" @click="emit('skip')">暂不更新</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
<!-- 更新成功:图片左边距 34rpx,文案左边距 44rpx,按钮浮动底部居中 -->
|
||||||
|
<block v-else-if="isSuccess">
|
||||||
|
<image src="../static/ota/update-ok.png" mode="aspectFit" class="result-title-img" style="width: 220rpx; height: 62rpx;" />
|
||||||
|
<text class="dialog-desc">请关机并重启智能弓</text>
|
||||||
|
<view class="btn-group-result">
|
||||||
|
<view class="primary-btn" @click="emit('done')">
|
||||||
|
<text class="primary-btn-text">完成</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
<!-- 更新失败:图片左边距 34rpx,文案左对齐 44rpx,按钮浮动底部居中 -->
|
||||||
|
<block v-else-if="isFailure">
|
||||||
|
<image src="../static/ota/update-fail.png" mode="aspectFit" class="result-title-img" style="width: 222rpx; height: 62rpx;" />
|
||||||
|
<text class="dialog-desc">请确保:</text>
|
||||||
|
<text class="dialog-desc">1、智能弓已开启</text>
|
||||||
|
<text class="dialog-desc">2、网路连接稳定</text>
|
||||||
|
<view class="btn-group-result">
|
||||||
|
<view class="primary-btn" @click="emit('retry')">
|
||||||
|
<text class="primary-btn-text">重试</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 关闭按钮(仅新版本状态,非强制更新时,位于弹窗下方) -->
|
||||||
|
<view
|
||||||
|
v-if="isNewVersion && !forceUpdate"
|
||||||
|
class="ota-close-below"
|
||||||
|
@click="emit('close')"
|
||||||
|
>
|
||||||
|
<image src="../static/sicon/close.png" mode="aspectFit" style="width: 56rpx; height: 56rpx;" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ota-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 外层容器:相对定位,为浮动图标创造溢出空间 */
|
||||||
|
.ota-outer {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
/* 设计图:吉祥物向上突出弹窗顶部 40px(375px基准)× 2 = 80rpx */
|
||||||
|
.outer-new {
|
||||||
|
padding-top: 80rpx;
|
||||||
|
}
|
||||||
|
.outer-result {
|
||||||
|
padding-top: 80rpx;
|
||||||
|
padding-bottom: 66rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮动图标(绝对定位,位于卡片顶部上方) */
|
||||||
|
.float-icon {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
/* 吉祥物尺寸:设计图 149×109px(375px基准)× 2 = 298×218rpx */
|
||||||
|
.float-mascot {
|
||||||
|
width: 298rpx;
|
||||||
|
height: 218rpx;
|
||||||
|
top: -5px;
|
||||||
|
right: -74rpx;
|
||||||
|
}
|
||||||
|
.float-check {
|
||||||
|
width: 194rpx;
|
||||||
|
height: 166rpx;
|
||||||
|
top: 20px;
|
||||||
|
right: 30rpx;
|
||||||
|
}
|
||||||
|
.float-close {
|
||||||
|
width: 194rpx;
|
||||||
|
height: 164rpx;
|
||||||
|
top: 20px;
|
||||||
|
right: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗卡片:overflow:visible 允许按钮溢出底部,背景通过 ota-bg-clip 独立裁剪 */
|
||||||
|
.ota-dialog {
|
||||||
|
position: relative;
|
||||||
|
width: 482rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
border: 2px solid #F9D5A1;
|
||||||
|
overflow: visible;
|
||||||
|
background-color: #392F1D;
|
||||||
|
}
|
||||||
|
/* 背景图裁剪层:独立 overflow:hidden + border-radius 保持圆角效果 */
|
||||||
|
.ota-bg-clip {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.ota-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ota-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
/* 按钮以外内容均左对齐 */
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.content-new {
|
||||||
|
padding: 30rpx 0 40rpx 0;
|
||||||
|
}
|
||||||
|
.content-result {
|
||||||
|
padding: 30rpx 0 66rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 发现新版本内容 */
|
||||||
|
.new-ver-img {
|
||||||
|
width: 274rpx;
|
||||||
|
height: 62rpx;
|
||||||
|
/* 左边距 34rpx,去掉 margin-bottom */
|
||||||
|
margin-left: 34rpx;
|
||||||
|
}
|
||||||
|
/* 版本号胶囊容器:相对定位,使 ota-ver.png 作为背景衬底 */
|
||||||
|
.version-tag-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
/* 离标题图 -10rpx,左边距 50rpx,离下方副标题 22rpx */
|
||||||
|
margin-top: -10rpx;
|
||||||
|
margin-left: 50rpx;
|
||||||
|
margin-bottom: 22rpx;
|
||||||
|
}
|
||||||
|
/* ota-ver.png 胶囊背景图 */
|
||||||
|
.version-tag-bg-img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
/* 版本号文字:浮于背景图之上 */
|
||||||
|
.version-tag {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
color: rgba(254, 222, 100, 1);
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 8rpx 22rpx 4rpx 24rpx;
|
||||||
|
}
|
||||||
|
/* 副标题(如"新版本将优化智能弓体验"):左边距 44rpx,离下方文案 12rpx */
|
||||||
|
.desc-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 36rpx;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 44rpx;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
/* 详细说明文案(如“升级前请确保:...”):左边距 44rpx */
|
||||||
|
.changelog-text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 40rpx;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 44rpx;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮组(新版本状态):离上方文案 30rpx,内部按钮间距 24rpx */
|
||||||
|
.btn-group {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 30rpx;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
/* 按钮组(结果状态):绝对定位,溢出卡片底边 -35rpx 悬浮在底边中间 */
|
||||||
|
.btn-group-result {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -35rpx;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
/* 主按钮:按照设计规范 width: 232rpx, height: 70rpx */
|
||||||
|
.primary-btn {
|
||||||
|
width: 232rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
background-color: #FED847;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.primary-btn-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 36rpx;
|
||||||
|
}
|
||||||
|
/* 暂不更新:设计规范颜色 #5FADFF 蓝色 */
|
||||||
|
.skip-text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #5FADFF;
|
||||||
|
line-height: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 更新结果内容:图片左边距 34rpx,下边距 16rpx */
|
||||||
|
.result-title-img {
|
||||||
|
margin-left: 34rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
/* 结果页文案:左对齐,左边距 44rpx,与 new_version 保持一致 */
|
||||||
|
.dialog-desc {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 40rpx;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 44rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 关闭按钮(位于弹窗下方) */
|
||||||
|
.ota-close-below {
|
||||||
|
margin-top: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -119,6 +119,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/mine-bow-data"
|
"path": "pages/mine-bow-data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/ota-wifi",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import AppFooter from "@/components/AppFooter.vue";
|
|||||||
import UserHeader from "@/components/UserHeader.vue";
|
import UserHeader from "@/components/UserHeader.vue";
|
||||||
import Signin from "@/components/Signin.vue";
|
import Signin from "@/components/Signin.vue";
|
||||||
import BubbleTip from "@/components/BubbleTip.vue";
|
import BubbleTip from "@/components/BubbleTip.vue";
|
||||||
|
import OtaModal from "@/components/OtaModal.vue";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
checkUserBindAPI,
|
checkUserBindAPI,
|
||||||
@@ -36,6 +37,44 @@ const showModal = ref(false);
|
|||||||
const showGuide = ref(false);
|
const showGuide = ref(false);
|
||||||
const scoreRankList = ref([]);
|
const scoreRankList = ref([]);
|
||||||
|
|
||||||
|
// OTA 相关
|
||||||
|
const otaVisible = ref(false);
|
||||||
|
const otaState = ref("new_version");
|
||||||
|
const OTA_MOCK = {
|
||||||
|
hasUpdate: true,
|
||||||
|
version: "V8.7.0",
|
||||||
|
description: "新版本将优化智能弓体验",
|
||||||
|
details: "升级前请确保:\n1、智能弓已开启,且电量充足\n2、所处稳定的 Wi-Fi 环境中。",
|
||||||
|
forceUpdate: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkOtaUpdate = () => {
|
||||||
|
if (!OTA_MOCK.hasUpdate) return;
|
||||||
|
const dismissedAt = uni.getStorageSync("ota_dismissed_at");
|
||||||
|
const now = Date.now();
|
||||||
|
if (dismissedAt && now - dismissedAt < 24 * 60 * 60 * 1000) return;
|
||||||
|
otaState.value = "new_version";
|
||||||
|
otaVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOtaDismiss = () => {
|
||||||
|
uni.setStorageSync("ota_dismissed_at", Date.now());
|
||||||
|
otaVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOtaUpdate = () => {
|
||||||
|
otaVisible.value = false;
|
||||||
|
uni.navigateTo({ url: "/pages/ota-wifi" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOtaDone = () => {
|
||||||
|
otaVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOtaRetry = () => {
|
||||||
|
otaVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
// 提取积分榜接口返回的榜单数组,兼容数组和对象两种返回格式。
|
// 提取积分榜接口返回的榜单数组,兼容数组和对象两种返回格式。
|
||||||
const getScoreRankData = (result) => {
|
const getScoreRankData = (result) => {
|
||||||
if (Array.isArray(result)) return result;
|
if (Array.isArray(result)) return result;
|
||||||
@@ -63,7 +102,15 @@ const toRankListPage = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onShow(async () => {
|
onShow(async (options) => {
|
||||||
|
// 检查是否从 OTA 更新页面返回
|
||||||
|
if (options && options.updateResult) {
|
||||||
|
otaState.value = options.updateResult;
|
||||||
|
otaVisible.value = true;
|
||||||
|
} else {
|
||||||
|
checkOtaUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
const env = uni.getAccountInfoSync().miniProgram.envVersion;
|
const env = uni.getAccountInfoSync().miniProgram.envVersion;
|
||||||
const token = uni.getStorageSync(`${env}_token`);
|
const token = uni.getStorageSync(`${env}_token`);
|
||||||
|
|
||||||
@@ -158,6 +205,20 @@ onShareTimeline(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Container :isHome="true" :showBackToGame="true">
|
<Container :isHome="true" :showBackToGame="true">
|
||||||
|
<!-- OTA 升级弹窗:使用 visible 控制显隐,description 为副标题,changelog 为详细说明 -->
|
||||||
|
<OtaModal
|
||||||
|
:visible="otaVisible"
|
||||||
|
:state="otaState"
|
||||||
|
:version="OTA_MOCK.version"
|
||||||
|
:description="OTA_MOCK.description"
|
||||||
|
:changelog="OTA_MOCK.details"
|
||||||
|
:forceUpdate="OTA_MOCK.forceUpdate"
|
||||||
|
@update="handleOtaUpdate"
|
||||||
|
@skip="handleOtaDismiss"
|
||||||
|
@close="handleOtaDismiss"
|
||||||
|
@done="handleOtaDone"
|
||||||
|
@retry="handleOtaRetry"
|
||||||
|
/>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<view class="top-theme">
|
<view class="top-theme">
|
||||||
<!-- <image
|
<!-- <image
|
||||||
|
|||||||
720
src/pages/ota-wifi.vue
Normal file
@@ -0,0 +1,720 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||||
|
import Container from "@/components/Container.vue";
|
||||||
|
|
||||||
|
const STATES = {
|
||||||
|
SCANNING: "SCANNING",
|
||||||
|
LIST: "LIST",
|
||||||
|
CONNECTING: "CONNECTING",
|
||||||
|
CONNECTED: "CONNECTED",
|
||||||
|
UPDATING: "UPDATING",
|
||||||
|
DONE: "DONE",
|
||||||
|
FAILED: "FAILED",
|
||||||
|
};
|
||||||
|
|
||||||
|
const isIOS = uni.getDeviceInfo().osName === "ios";
|
||||||
|
const currentState = ref(STATES.SCANNING);
|
||||||
|
const connectedWifi = ref(null);
|
||||||
|
const wifiList = ref([]);
|
||||||
|
const showWifiBanner = ref(false);
|
||||||
|
|
||||||
|
const connectingWifi = ref(null);
|
||||||
|
const connectInput = ref({ ssid: "", password: "" });
|
||||||
|
const connectMode = ref("secure"); // secure | open | manual
|
||||||
|
const connectError = ref("");
|
||||||
|
|
||||||
|
const progress = ref(0);
|
||||||
|
let progressTimer = null;
|
||||||
|
let timeoutTimer = null;
|
||||||
|
|
||||||
|
const MOCK_WIFI_LIST = [
|
||||||
|
{ SSID: "shelingxingqiu", secure: true, signalStrength: -50 },
|
||||||
|
{ SSID: "tplink3435_02", secure: true, signalStrength: -65 },
|
||||||
|
{ SSID: "tplink2025_007", secure: true, signalStrength: -70 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const startScanning = () => {
|
||||||
|
currentState.value = STATES.SCANNING;
|
||||||
|
wifiList.value = [];
|
||||||
|
showWifiBanner.value = false;
|
||||||
|
connectError.value = "";
|
||||||
|
|
||||||
|
wx.startWifi({
|
||||||
|
success() {
|
||||||
|
wx.getWifiList({
|
||||||
|
fail(err) {
|
||||||
|
if (err.errCode === 12005) showWifiBanner.value = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail(err) {
|
||||||
|
if (err.errCode === 12005) showWifiBanner.value = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
wx.onGetWifiList((res) => {
|
||||||
|
wifiList.value = res.wifiList || [];
|
||||||
|
currentState.value = STATES.LIST;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (currentState.value === STATES.SCANNING) {
|
||||||
|
wifiList.value = MOCK_WIFI_LIST;
|
||||||
|
currentState.value = STATES.LIST;
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectWifi = (wifi) => {
|
||||||
|
connectingWifi.value = wifi;
|
||||||
|
connectInput.value = { ssid: wifi.SSID, password: "" };
|
||||||
|
connectMode.value = wifi.secure ? "secure" : "open";
|
||||||
|
connectError.value = "";
|
||||||
|
currentState.value = STATES.CONNECTING;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectOther = () => {
|
||||||
|
connectingWifi.value = null;
|
||||||
|
connectInput.value = { ssid: "", password: "" };
|
||||||
|
connectMode.value = "manual";
|
||||||
|
connectError.value = "";
|
||||||
|
currentState.value = STATES.CONNECTING;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeConnectSheet = () => {
|
||||||
|
connectError.value = "";
|
||||||
|
currentState.value = connectedWifi.value ? STATES.CONNECTED : STATES.LIST;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasChinese = (str) => /[\u4e00-\u9fa5]/.test(str);
|
||||||
|
|
||||||
|
const ssidWarning = computed(() => {
|
||||||
|
if (connectMode.value === "manual" && hasChinese(connectInput.value.ssid)) {
|
||||||
|
return "网络名称仅支持英文字符及数字,请连接英文名网络或把网络改为英文";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinDisabled = computed(() => {
|
||||||
|
if (connectMode.value === "secure") return !connectInput.value.password;
|
||||||
|
if (connectMode.value === "manual") return !connectInput.value.ssid;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinNetwork = () => {
|
||||||
|
if (joinDisabled.value) return;
|
||||||
|
connectError.value = "";
|
||||||
|
const ssid = connectInput.value.ssid;
|
||||||
|
const password = connectInput.value.password;
|
||||||
|
|
||||||
|
wx.connectWifi({
|
||||||
|
SSID: ssid,
|
||||||
|
password,
|
||||||
|
success() {
|
||||||
|
connectedWifi.value = { SSID: ssid, secure: !!password };
|
||||||
|
currentState.value = STATES.CONNECTED;
|
||||||
|
},
|
||||||
|
fail() {
|
||||||
|
connectError.value = `无法查找到网络"${ssid}",请检查网络名称与设置!`;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (currentState.value === STATES.CONNECTING && !connectError.value) {
|
||||||
|
connectedWifi.value = { SSID: ssid, secure: !!password };
|
||||||
|
currentState.value = STATES.CONNECTED;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startUpdate = () => {
|
||||||
|
currentState.value = STATES.UPDATING;
|
||||||
|
progress.value = 0;
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
progressTimer = setInterval(() => {
|
||||||
|
if (progress.value >= 90) {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const increment = Math.max(0.5, 2 - progress.value / 60);
|
||||||
|
progress.value = Math.min(90, progress.value + increment);
|
||||||
|
}, 500);
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
timeoutTimer = setTimeout(() => {
|
||||||
|
if (currentState.value === STATES.UPDATING) {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
currentState.value = STATES.FAILED;
|
||||||
|
}
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWsDone = () => {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
progress.value = 100;
|
||||||
|
setTimeout(() => {
|
||||||
|
currentState.value = STATES.DONE;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWsFail = () => {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
currentState.value = STATES.FAILED;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDone = () => {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 1,
|
||||||
|
success() {
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
const prevPage = pages[pages.length - 2];
|
||||||
|
if (prevPage) {
|
||||||
|
prevPage.$vm.otaState = "update_success";
|
||||||
|
prevPage.$vm.otaVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRetry = () => {
|
||||||
|
if (connectedWifi.value) {
|
||||||
|
currentState.value = STATES.CONNECTED;
|
||||||
|
} else {
|
||||||
|
startScanning();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
startScanning();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
wx.offGetWifiList && wx.offGetWifiList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Container title="连接无线网络">
|
||||||
|
<!-- WiFi 不可用 Banner -->
|
||||||
|
<view v-if="showWifiBanner" class="wifi-banner">
|
||||||
|
<text class="wifi-banner-text">请先开启 WiFi 后刷新重试</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- SCANNING / LIST / CONNECTED -->
|
||||||
|
<view
|
||||||
|
v-if="currentState === 'SCANNING' || currentState === 'LIST' || currentState === 'CONNECTED'"
|
||||||
|
class="wifi-page"
|
||||||
|
>
|
||||||
|
<!-- Hero: wifi1.png 单图(已内含吉祥物) -->
|
||||||
|
<view class="hero-area">
|
||||||
|
<image src="../static/ota/wifi1.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<text class="page-title">{{ currentState === 'CONNECTED' ? '连接成功' : '连接无线网络' }}</text>
|
||||||
|
<text v-if="currentState !== 'CONNECTED'" class="page-subtitle">网络名称仅支持英文字符及数字</text>
|
||||||
|
|
||||||
|
<view v-if="isIOS && currentState === 'SCANNING'" class="ios-guide">
|
||||||
|
<text class="ios-guide-text">请在设备 WiFi 设置中完成扫描后返回</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 已连接网络卡片 -->
|
||||||
|
<view v-if="currentState === 'CONNECTED'" class="section-label-row">
|
||||||
|
<text class="section-label">连接网络</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="currentState === 'CONNECTED'" class="wifi-list-card connected-wifi-card">
|
||||||
|
<view class="wifi-item connected-wifi-item">
|
||||||
|
<image class="check-icon" src="../static/sicon/check.png" mode="aspectFit" />
|
||||||
|
<text class="wifi-ssid">{{ connectedWifi?.SSID }}</text>
|
||||||
|
<view class="wifi-icons">
|
||||||
|
<image
|
||||||
|
v-if="connectedWifi?.secure"
|
||||||
|
class="security-icon"
|
||||||
|
src="../static/sicon/pwd.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
/>
|
||||||
|
<image class="signal-icon" src="../static/sicon/wifi.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 网络列表标题 -->
|
||||||
|
<view class="section-label-row">
|
||||||
|
<text class="section-label">网络</text>
|
||||||
|
<image
|
||||||
|
v-if="currentState === 'SCANNING'"
|
||||||
|
src="../static/sicon/refresh.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
style="width: 34rpx; height: 34rpx; margin-left: 8rpx; opacity: 0.7;"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 网络列表卡片 -->
|
||||||
|
<view class="wifi-list-card">
|
||||||
|
<block v-if="currentState === 'SCANNING'">
|
||||||
|
<view class="wifi-item" @click="selectOther">
|
||||||
|
<text class="wifi-ssid other-text">其他...</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
<block v-else>
|
||||||
|
<view
|
||||||
|
v-for="(wifi, idx) in wifiList"
|
||||||
|
:key="wifi.SSID"
|
||||||
|
class="wifi-item"
|
||||||
|
@click="selectWifi(wifi)"
|
||||||
|
>
|
||||||
|
<text class="wifi-ssid">{{ wifi.SSID }}</text>
|
||||||
|
<view class="wifi-icons">
|
||||||
|
<image
|
||||||
|
v-if="wifi.secure"
|
||||||
|
class="security-icon"
|
||||||
|
src="../static/sicon/pwd.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
/>
|
||||||
|
<image class="signal-icon" src="../static/sicon/wifi.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="wifi-item" @click="selectOther">
|
||||||
|
<text class="wifi-ssid other-text">其他...</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- CONNECTED:开始更新按钮 -->
|
||||||
|
<view v-if="currentState === 'CONNECTED'" class="bottom-btn-area">
|
||||||
|
<view class="primary-btn" @click="startUpdate">
|
||||||
|
<text class="primary-btn-text">开始更新</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- UPDATING -->
|
||||||
|
<view v-else-if="currentState === 'UPDATING'" class="center-page">
|
||||||
|
<image src="../static/ota/target-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
||||||
|
<text class="page-title" style="margin-top: 24rpx;">更新中,请稍等片刻...</text>
|
||||||
|
<view class="progress-wrap">
|
||||||
|
<view class="progress-track">
|
||||||
|
<view class="progress-fill" :style="{ width: progress + '%' }"></view>
|
||||||
|
</view>
|
||||||
|
<text class="progress-pct">{{ Math.floor(progress) }}%</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- DONE -->
|
||||||
|
<view v-else-if="currentState === 'DONE'" class="center-page">
|
||||||
|
<image src="../static/ota/check-char.png" mode="aspectFit" style="width: 194rpx; height: 166rpx;" />
|
||||||
|
<text class="page-title" style="margin-top: 24rpx;">更新完成</text>
|
||||||
|
<text class="page-desc-white">请关机并重启智能弓</text>
|
||||||
|
<view class="done-info-box"></view>
|
||||||
|
<view class="primary-btn done-btn" @click="handleDone">
|
||||||
|
<text class="primary-btn-text">完成</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- FAILED -->
|
||||||
|
<view v-else-if="currentState === 'FAILED'" class="center-page">
|
||||||
|
<image src="../static/ota/close-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
||||||
|
<text class="page-title fail-title" style="margin-top: 24rpx;">更新失败</text>
|
||||||
|
<text class="page-desc-white">请确保:</text>
|
||||||
|
<text class="page-desc-white">1、智能弓已开启</text>
|
||||||
|
<text class="page-desc-white">2、网路连接稳定</text>
|
||||||
|
<view class="primary-btn done-btn" style="margin-top: 40rpx;" @click="handleRetry">
|
||||||
|
<text class="primary-btn-text">重试</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- CONNECTING 底部弹窗 -->
|
||||||
|
<view v-if="currentState === 'CONNECTING'" class="sheet-mask" @click="closeConnectSheet">
|
||||||
|
<view class="sheet" @click.stop="">
|
||||||
|
<image src="../static/ota/ota-bg.png" mode="aspectFill" class="sheet-bg" />
|
||||||
|
<view class="sheet-inner">
|
||||||
|
|
||||||
|
<!-- 有密码网络 -->
|
||||||
|
<block v-if="connectMode === 'secure'">
|
||||||
|
<view class="sheet-header">
|
||||||
|
<view class="sheet-nav-btn" @click="closeConnectSheet">
|
||||||
|
<image src="../static/sicon/arrow-left.png" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
|
||||||
|
</view>
|
||||||
|
<text class="sheet-title">加入"{{ connectInput.ssid }}"</text>
|
||||||
|
<view
|
||||||
|
class="sheet-nav-btn"
|
||||||
|
:class="{ 'nav-disabled': joinDisabled }"
|
||||||
|
@click="joinNetwork"
|
||||||
|
>
|
||||||
|
<image src="../static/sicon/check.png" mode="aspectFit" style="width: 28rpx; height: 24rpx;" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="input-row-card">
|
||||||
|
<text class="input-label">密码</text>
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
password
|
||||||
|
v-model="connectInput.password"
|
||||||
|
placeholder="输入网络密码"
|
||||||
|
placeholder-class="input-placeholder"
|
||||||
|
confirm-type="done"
|
||||||
|
@confirm="joinNetwork"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<text v-if="connectError" class="connect-error">{{ connectError }}</text>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
<!-- 无密码网络 -->
|
||||||
|
<block v-else-if="connectMode === 'open'">
|
||||||
|
<view class="sheet-header">
|
||||||
|
<view class="sheet-nav-btn" @click="closeConnectSheet">
|
||||||
|
<image src="../static/sicon/arrow-left.png" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
|
||||||
|
</view>
|
||||||
|
<text class="sheet-title">加入"{{ connectInput.ssid }}"</text>
|
||||||
|
<view class="sheet-nav-btn" @click="joinNetwork">
|
||||||
|
<image src="../static/sicon/check.png" mode="aspectFit" style="width: 28rpx; height: 24rpx;" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="sheet-hint">该网络为开放网络,点击 ✓ 加入</text>
|
||||||
|
<text v-if="connectError" class="connect-error">{{ connectError }}</text>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
<!-- 手动输入 -->
|
||||||
|
<block v-else-if="connectMode === 'manual'">
|
||||||
|
<view class="sheet-header">
|
||||||
|
<view class="sheet-nav-btn" @click="closeConnectSheet">
|
||||||
|
<image src="../static/sicon/arrow-left.png" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
|
||||||
|
</view>
|
||||||
|
<text class="sheet-title">加入无线网络</text>
|
||||||
|
<view
|
||||||
|
class="sheet-nav-btn"
|
||||||
|
:class="{ 'nav-disabled': joinDisabled }"
|
||||||
|
@click="joinNetwork"
|
||||||
|
>
|
||||||
|
<image src="../static/sicon/check.png" mode="aspectFit" style="width: 28rpx; height: 24rpx;" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="input-row-card">
|
||||||
|
<text class="input-label">名称</text>
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
v-model="connectInput.ssid"
|
||||||
|
placeholder="输入网络名称"
|
||||||
|
placeholder-class="input-placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="input-row-card">
|
||||||
|
<text class="input-label">密码</text>
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
password
|
||||||
|
v-model="connectInput.password"
|
||||||
|
placeholder="输入网络密码"
|
||||||
|
placeholder-class="input-placeholder"
|
||||||
|
confirm-type="done"
|
||||||
|
@confirm="joinNetwork"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<text v-if="ssidWarning" class="connect-error">{{ ssidWarning }}</text>
|
||||||
|
<text v-else-if="connectError" class="connect-error">{{ connectError }}</text>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.wifi-banner {
|
||||||
|
background-color: rgba(255, 200, 0, 0.12);
|
||||||
|
border: 1rpx solid rgba(254, 216, 71, 0.5);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
margin: 16rpx 24rpx 0;
|
||||||
|
}
|
||||||
|
.wifi-banner-text {
|
||||||
|
color: rgba(254, 216, 71, 1);
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-page {
|
||||||
|
padding: 0 67rpx 40rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-area {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40rpx 0 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
color: rgba(91, 196, 255, 1);
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
.page-subtitle {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
.fail-title {
|
||||||
|
color: rgba(255, 100, 100, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ios-guide {
|
||||||
|
background-color: rgba(95, 173, 255, 0.12);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
.ios-guide-text {
|
||||||
|
color: rgba(95, 173, 255, 1);
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-label-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
padding: 0 22rpx;
|
||||||
|
}
|
||||||
|
.section-label {
|
||||||
|
color: rgba(255, 255, 255, 0.56);
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-list-card {
|
||||||
|
background-color: rgba(30, 35, 50, 0.96);
|
||||||
|
border-radius: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
.connected-wifi-card {
|
||||||
|
margin-bottom: 44rpx;
|
||||||
|
}
|
||||||
|
.wifi-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 92rpx;
|
||||||
|
padding: 0 26rpx 0 66rpx;
|
||||||
|
}
|
||||||
|
.wifi-item:not(:last-child)::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 66rpx;
|
||||||
|
right: 26rpx;
|
||||||
|
bottom: 0;
|
||||||
|
height: 1rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
.connected-wifi-item {
|
||||||
|
padding-left: 24rpx;
|
||||||
|
}
|
||||||
|
.wifi-ssid {
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 36rpx;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.other-text {
|
||||||
|
color: rgba(255, 255, 255, 0.86);
|
||||||
|
}
|
||||||
|
.wifi-icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.check-icon {
|
||||||
|
width: 28rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.security-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.signal-icon {
|
||||||
|
width: 36rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-btn-area {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-btn {
|
||||||
|
background-color: rgba(254, 216, 71, 1);
|
||||||
|
border-radius: 50rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.primary-btn-text {
|
||||||
|
color: rgba(0, 0, 0, 1);
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 80rpx 32rpx 40rpx;
|
||||||
|
}
|
||||||
|
.page-desc-white {
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 52rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.done-info-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 240rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
border-radius: 24rpx;
|
||||||
|
margin: 24rpx 0;
|
||||||
|
}
|
||||||
|
.done-btn {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-wrap {
|
||||||
|
width: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
}
|
||||||
|
.progress-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 10rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 5rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(91, 196, 255, 1);
|
||||||
|
border-radius: 5rpx;
|
||||||
|
transition: width 0.4s ease;
|
||||||
|
}
|
||||||
|
.progress-pct {
|
||||||
|
color: rgba(91, 196, 255, 1);
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.sheet {
|
||||||
|
position: relative;
|
||||||
|
background-color: rgba(57, 47, 29, 1);
|
||||||
|
border-top-left-radius: 28rpx;
|
||||||
|
border-top-right-radius: 28rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 320rpx;
|
||||||
|
border: 1rpx solid rgba(249, 213, 161, 0.4);
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.sheet-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.sheet-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 32rpx 32rpx 112rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
.sheet-nav-btn {
|
||||||
|
width: 64rpx;
|
||||||
|
height: 64rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.nav-disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
.sheet-title {
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.sheet-hint {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-size: 26rpx;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
.connect-error {
|
||||||
|
color: rgba(254, 216, 71, 1);
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-row-card {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
margin-left: 48rpx;
|
||||||
|
margin-right: 48rpx;
|
||||||
|
}
|
||||||
|
.input-row-card + .input-row-card {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
.input-label {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-size: 28rpx;
|
||||||
|
width: 80rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.input-field {
|
||||||
|
flex: 1;
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
font-size: 28rpx;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0 8rpx;
|
||||||
|
}
|
||||||
|
.input-placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
src/static/ota/check-char.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
src/static/ota/close-char.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
src/static/ota/new-ver.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src/static/ota/ota-bg.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/static/ota/ota-mascot.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/static/ota/ota-ver.png
Normal file
|
After Width: | Height: | Size: 237 B |
BIN
src/static/ota/target-char.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
src/static/ota/update-fail.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/static/ota/update-ok.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/static/ota/wifi1.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
src/static/ota/wifi2.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/static/sicon/arrow-left.png
Normal file
|
After Width: | Height: | Size: 277 B |
BIN
src/static/sicon/cancel.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/static/sicon/check.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
src/static/sicon/close.png
Normal file
|
After Width: | Height: | Size: 603 B |
BIN
src/static/sicon/pwd.png
Normal file
|
After Width: | Height: | Size: 407 B |
BIN
src/static/sicon/refresh.png
Normal file
|
After Width: | Height: | Size: 494 B |
BIN
src/static/sicon/target_icon.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src/static/sicon/tick.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/static/sicon/wifi.png
Normal file
|
After Width: | Height: | Size: 548 B |