Files
zgty-mas-m/pages/setting/upgrade-popup.vue
houjunxiang 386f1e7466 1
2025-10-09 18:19:55 +08:00

508 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="mask flex-center">
<view class="content botton-radius">
<view class="content-top">
<text class="content-top-text">{{ title }}</text>
<image
class="content-top"
style="top: 0"
width="100%"
height="100%"
src="../../static/images/app_update_bg_top.png"
>
</image>
</view>
<view class="content-header"></view>
<view class="content-body">
<view class="title">
<text>{{ subTitle }}</text>
<!-- <text style="padding-left:20rpx;font-size: 0.5em;color: #666;">v.{{version}}</text> -->
</view>
<view class="body">
<scroll-view class="box-des-scroll" scroll-y="true">
<text class="box-des">
{{ contents }}
</text>
</scroll-view>
</view>
<view class="footer flex-center">
<template v-if="isiOS">
<button class="content-button" style="border: none; color: #fff" plain @click="jumpToAppStore">
{{ downLoadBtnTextiOS }}
</button>
</template>
<template v-else>
<template v-if="!downloadSuccess">
<view class="progress-box flex-column" v-if="downloading">
<progress
class="progress"
border-radius="35"
:percent="downLoadPercent"
activeColor="#3DA7FF"
show-info
stroke-width="10"
/>
<view style="width: 100%; font-size: 14px; display: flex; justify-content: space-around">
<text>{{ downLoadingText }}</text>
<text>({{ downloadedSize }}/{{ packageFileSize }}M)</text>
</view>
</view>
<button v-else class="content-button" style="border: none; color: #fff" plain @click="downloadPackage">
{{ downLoadBtnText }}
</button>
</template>
<button
v-else-if="downloadSuccess"
class="content-button"
style="border: none; color: #fff"
plain
:loading="installing"
:disabled="installing"
@click="installPackage"
>
{{ installing ? '正在安装……' : '下载完成,立即安装' }}
</button>
<button
v-if="installed && isWGT"
class="content-button"
style="border: none; color: #fff"
plain
@click="restart"
>
安装完毕点击重启
</button>
</template>
</view>
</view>
<image
v-if="!is_mandatory"
class="close-img"
src="../../static/images/app_update_close.png"
@click.stop="closeUpdate"
></image>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onLoad, onBackPress } from '@dcloudio/uni-app'
const localFilePathKey = '__localFilePath__'
const platform_iOS = 'iOS'
let downloadTask = null
// 工具函数保持原样
function compare(v1 = '0', v2 = '0') {
v1 = String(v1).split('.')
v2 = String(v2).split('.')
const minVersionLens = Math.min(v1.length, v2.length)
let result = 0
for (let i = 0; i < minVersionLens; i++) {
const curV1 = Number(v1[i])
const curV2 = Number(v2[i])
if (curV1 > curV2) {
result = 1
break
} else if (curV1 < curV2) {
result = -1
break
}
}
if (result === 0 && v1.length !== v2.length) {
const v1BiggerThenv2 = v1.length > v2.length
const maxLensVersion = v1BiggerThenv2 ? v1 : v2
for (let i = minVersionLens; i < maxLensVersion.length; i++) {
const curVersion = Number(maxLensVersion[i])
if (curVersion > 0) {
v1BiggerThenv2 ? (result = 1) : (result = -1)
break
}
}
}
return result
}
// 响应式状态
const installForBeforeFilePath = ref('')
const installed = ref(false)
const installing = ref(false)
const downloadSuccess = ref(false)
const downloading = ref(false)
const downLoadPercent = ref(0)
const downloadedSize = ref(0)
const packageFileSize = ref(0)
const tempFilePath = ref('')
const title = ref('更新日志')
const contents = ref('')
const is_mandatory = ref(false)
const subTitle = ref('发现新版本')
const downLoadBtnTextiOS = ref('立即跳转更新')
const downLoadBtnText = ref('立即下载更新')
const downLoadingText = ref('安装包下载中,请稍后')
const version = ref('')
const url = ref('')
const type = ref('')
const platform = ref('')
// 计算属性
const isWGT = computed(() => type.value === 'wgt')
const isiOS = computed(() => !isWGT.value && platform.value.includes(platform_iOS))
// 方法
function checkLocalStoragePackage() {
const localFilePathRecord = uni.getStorageSync(localFilePathKey)
if (localFilePathRecord) {
const { version: recordVersion, savedFilePath, installed: recordInstalled } = localFilePathRecord
if (!recordInstalled && compare(recordVersion, version.value) === 0) {
downloadSuccess.value = true
installForBeforeFilePath.value = savedFilePath
tempFilePath.value = savedFilePath
} else {
deleteSavedFile(savedFilePath)
}
}
}
async function closeUpdate() {
if (downloading.value) {
if (is_mandatory.value) {
uni.showToast({ title: '下载中,请稍后……', icon: 'none', duration: 500 })
return
}
uni.showModal({
title: '是否取消下载?',
cancelText: '否',
confirmText: '是',
success: res => {
if (res.confirm) {
downloadTask?.abort()
uni.navigateBack()
}
}
})
return
}
if (downloadSuccess.value && tempFilePath.value) {
await saveFile(tempFilePath.value, version.value)
uni.navigateBack()
return
}
uni.navigateBack()
}
function downloadPackage() {
downloading.value = true
downloadTask = uni.downloadFile({
url: url.value,
success: res => {
if (res.statusCode === 200) {
downloadSuccess.value = true
tempFilePath.value = res.tempFilePath
// 强制更新
if (is_mandatory.value) {
installPackage()
}
}
},
complete: () => {
downloading.value = false
downLoadPercent.value = 0
downloadedSize.value = 0
packageFileSize.value = 0
downloadTask = null
}
})
downloadTask.onProgressUpdate(res => {
downLoadPercent.value = res.progress
downloadedSize.value = (res.totalBytesWritten / 1024 ** 2).toFixed(2)
packageFileSize.value = (res.totalBytesExpectedToWrite / 1024 ** 2).toFixed(2)
})
}
function installPackage() {
// #ifdef APP-PLUS
if (isWGT.value) {
installing.value = true
}
plus.runtime.install(
tempFilePath.value,
{
force: false
},
async () => {
installing.value = false
installed.value = true
if (isWGT.value) {
if (is_mandatory.value) {
uni.showLoading({ icon: 'none', title: '安装成功,正在重启……' })
setTimeout(() => {
uni.hideLoading()
restart()
}, 1000)
}
} else {
const localFilePathRecord = uni.getStorageSync(localFilePathKey)
uni.setStorageSync(localFilePathKey, {
...localFilePathRecord,
installed: true
})
}
},
async err => {
if (installForBeforeFilePath.value) {
await deleteSavedFile(installForBeforeFilePath.value)
installForBeforeFilePath.value = ''
}
installing.value = false
installed.value = false
uni.showModal({
title: `更新失败${isWGT.value ? '' : 'APK文件不存在'},请重新下载`,
content: err.message,
showCancel: false
})
}
)
if (!isWGT.value) {
uni.navigateBack()
}
// #endif
}
function restart() {
installed.value = false
// #ifdef APP-PLUS
plus.runtime.restart()
// #endif
}
async function saveFile(tempFilePath, version) {
try {
const res = await uni.saveFile({ tempFilePath })
uni.setStorageSync(localFilePathKey, {
version,
savedFilePath: res.savedFilePath
})
} catch (err) {
console.error('保存文件失败:', err)
}
}
async function deleteSavedFile(filePath) {
try {
await uni.removeSavedFile({ filePath })
uni.removeStorageSync(localFilePathKey)
} catch (err) {
console.error('删除文件失败:', err)
}
}
function jumpToAppStore() {
plus.runtime.openURL(url.value)
}
// 生命周期
onLoad(params => {
const { local_storage_key } = params
if (!local_storage_key) {
console.error('local_storage_key为空')
// uni.navigateBack()
return
}
const localPackageInfo = uni.getStorageSync(local_storage_key)
if (!localPackageInfo) {
console.error('安装包信息为空')
uni.navigateBack()
return
}
const requiredKeys = ['version', 'url', 'type']
for (const key of requiredKeys) {
if (!localPackageInfo[key]) {
console.error(`参数 ${key} 必填`)
uni.navigateBack()
return
}
}
// 更新响应式状态
version.value = localPackageInfo.version
url.value = localPackageInfo.url
type.value = localPackageInfo.type
platform.value = localPackageInfo.platform || ''
title.value = localPackageInfo.title || '更新日志'
contents.value = localPackageInfo.contents || ''
is_mandatory.value = localPackageInfo.is_mandatory || false
subTitle.value = localPackageInfo.subTitle || '发现新版本'
downLoadBtnTextiOS.value = localPackageInfo.downLoadBtnTextiOS || '立即跳转更新'
downLoadBtnText.value = localPackageInfo.downLoadBtnText || '立即下载更新'
downLoadingText.value = localPackageInfo.downLoadingText || '安装包下载中,请稍后'
checkLocalStoragePackage()
})
onBackPress(() => {
if (is_mandatory.value) return true
if (downloadTask) downloadTask.abort()
return false
})
</script>
<style>
page {
background: transparent;
}
.flex-center {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
}
.mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.65);
}
.botton-radius {
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}
.content {
position: relative;
top: 0;
width: 400px;
background-color: #fff;
box-sizing: border-box;
padding: 0 30px;
font-family: Source Han Sans CN;
max-height: 80vh;
overflow: hidden;
border-radius: 20px;
}
.content-top {
position: absolute;
top: -55px;
left: 0;
width: 400px;
height: 180px;
}
.content-top-text {
font-size: 24px;
font-weight: bold;
color: #f8f8fa;
position: absolute;
top: 80px;
left: 30px;
z-index: 1;
}
.content-header {
padding-top: 80px;
height: 40px;
}
.content-body {
padding-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
color: #3da7ff;
line-height: 1.5;
margin-bottom: 15px;
}
.footer {
height: 90px;
display: flex;
align-items: center;
justify-content: space-around;
margin-top: 15px;
}
.box-des-scroll {
box-sizing: border-box;
padding: 0 20px;
height: 50px;
text-align: left;
margin-bottom: 15px;
}
.box-des {
font-size: 16px;
color: #000000;
line-height: 1.5;
}
.progress-box {
width: 100%;
}
.progress {
width: 90%;
height: 20px;
border-radius: 10px;
margin-bottom: 10px;
}
.close-img {
width: 40px;
height: 40px;
z-index: 1000;
position: absolute;
bottom: -70px;
left: calc(50% - 20px);
}
.content-button {
text-align: center;
flex: 1;
font-size: 18px;
font-weight: 400;
color: #ffffff;
border-radius: 20px;
margin: 0 10px;
height: 50px;
line-height: 50px;
background: linear-gradient(to right, #1785ff, #3da7ff);
min-width: 160px;
}
.flex-column {
display: flex;
flex-direction: column;
align-items: center;
}
</style>