<template>
<view class="mask flex-center">
<view class="content">
<view class="content-top">
<text class="content-top-text">{{title}}</text>
<image class="content-top-image" mode="widthFix"
src="/uni_modules/uni-upgrade-center-app/static/app/bg_top.png"></image>
</view>
<view class="content-space"></view>
<view class="content-body">
<view class="content-body-title">
<text class="text title">{{subTitle}}</text>
<text class="text version">v{{version}}</text>
</view>
<view class="body">
<scroll-view class="box-des-scroll" scroll-y="true">
<text class="text box-des">
{{contents}}
</text>
</scroll-view>
</view>
<view class="footer flex-center">
<template v-if="isiOS || isHarmony">
<button class="content-button" style="border: none;color: #fff;" type="primary" 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" :percent="downLoadPercent" activeColor="#3DA7FF" :show-info="true"
:stroke-width="10" />
<view style="width:100%;display: flex;justify-content: space-around;flex-direction: row;">
<text class="text" style="font-size: 14px;">{{downLoadingText}}</text>
<text class="text" style="font-size: 14px;">({{downloadedSize}}/{{packageFileSize}}M)</text>
</view>
</view>
<button v-else class="content-button" @click="updateApp">
{{downLoadBtnText}}
</button>
</template>
<button v-else-if="downloadSuccess && !installed" class="content-button" :loading="installing"
:disabled="installing" @click="installPackage">
{{installing ? '正在安装……' : '下载完成,立即安装'}}
</button>
<button v-else-if="installed" class="content-button" @click="installPackage">
安装未完成,点击安装
</button>
</template>
</view>
</view>
<view class="content-bottom">
<image v-if="!is_mandatory" class="close-img" mode="widthFix"
src="/uni_modules/uni-upgrade-center-app/static/app/app_update_close.png" @click="closeUpdate">
</image>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { openSchema as utsOpenSchema } from '@/uni_modules/uts-openSchema'
import { UniUpgradeCenterResult, StoreListItem } from '../../utils/call-check-version'
import { platform_iOS, platform_Android, platform_Harmony } from '../../utils/utils'
// #ifdef APP-ANDROID
import { createNotificationProgress, cancelNotificationProgress, finishNotificationProgress } from '@/uni_modules/uts-progressNotification'
import { type CreateNotificationProgressOptions, type FinishNotificationProgressOptions } from '@/uni_modules/uts-progressNotification/utssdk/interface.uts'
// #endif
const requiredKey = ['version', 'url', 'type']
let downloadTask : DownloadTask | null = null;
let openSchemePromise : Promise<boolean> | null = null;
const openSchema = (url : string) : Promise<boolean> => new Promise<boolean>((resolve, reject) => {
try {
utsOpenSchema(url)
resolve(true)
} catch (e) {
reject(false)
}
})
// 从之前下载安装
const installForBeforeFilePath = ref<string>('')
// 安装
const installed = ref<boolean>(false)
const installing = ref<boolean>(false)
// 下载
const downloadSuccess = ref<boolean>(false)
const downloading = ref<boolean>(false)
const downLoadPercent = ref<number>(0)
const downloadedSize = ref<number>(0)
const packageFileSize = ref<number>(0)
// 要安装的本地包地址
const tempFilePath = ref<string>('')
// 默认安装包信息
const title = ref<string>('更新日志')
const contents = ref<string>('')
const version = ref<string>('')
const is_mandatory = ref<boolean>(false)
const url = ref<string>("")
const platform = ref<string[]>([])
const store_list = ref<StoreListItem[] | null>(null)
// 可自定义属性
const subTitle = ref<string>('发现新版本')
const downLoadBtnTextiOS = ref<string>('立即跳转更新')
const downLoadBtnText = ref<string>('立即下载更新')
const downLoadingText = ref<string>('安装包下载中,请稍后')
const isiOS = computed(() : boolean => platform.value.includes(platform_iOS))
const isHarmony = computed(() : boolean => platform.value.includes(platform_Harmony))
const isAndroid = computed(() : boolean => platform.value.includes(platform_Android))
const needNotificationProgress = computed(() : boolean => isAndroid.value && !is_mandatory.value)
function getCurrentDialogPage() : UniPage | null {
const pages = getCurrentPages()
if (pages.length > 0) {
const dialogPages = pages[pages.length - 1].getDialogPages()
if (dialogPages.length > 0) {
return dialogPages[dialogPages.length - 1]
}
}
return null
}
function closePopup() {
downloadSuccess.value = false
downloading.value = false
downLoadPercent.value = 0
downloadedSize.value = 0
packageFileSize.value = 0
tempFilePath.value = ''
installing.value = false
installed.value = false
uni.closeDialogPage({
dialogPage: getCurrentDialogPage(),
fail(e) {
console.log('e: ', e);
}
})
}
function askAbortDownload() {
uni.showModal({
title: '是否取消下载?',
cancelText: '否',
confirmText: '是',
success: res => {
if (res.confirm) {
if (downloadTask !== null) downloadTask!.abort()
if (needNotificationProgress.value) {
// #ifdef APP-ANDROID
cancelNotificationProgress();
// #endif
}
closePopup()
}
}
});
}
function closeUpdate() {
if (downloading.value && !needNotificationProgress.value) {
askAbortDownload()
return;
}
closePopup()
}
function jumpToAppStore() {
openSchema(url.value)
}
function show(localPackageInfo : UniUpgradeCenterResult | null) {
if (localPackageInfo === null) return;
for (let key in localPackageInfo) {
if (requiredKey.indexOf(key) != -1 && localPackageInfo[key] === null) {
console.error(`参数 ${key} 必填,请检查后重试`)
closePopup()
return;
}
}
title.value = localPackageInfo.title
url.value = localPackageInfo.url
contents.value = localPackageInfo.contents
is_mandatory.value = localPackageInfo.is_mandatory
platform.value = localPackageInfo.platform
version.value = localPackageInfo.version
store_list.value = localPackageInfo.store_list
}
function checkStoreScheme() : Promise<boolean> | null {
if (store_list.value !== null) {
const storeList : StoreListItem[] = store_list.value!.filter((item : StoreListItem) : boolean => item.enable)
if (storeList.length > 0) {
if (openSchemePromise === null) {
openSchemePromise = Promise.reject() as Promise<boolean>
}
storeList
.sort((cur : StoreListItem, next : StoreListItem) : number => next.priority - cur.priority)
.map((item : StoreListItem) : string => item.scheme)
.reduce((promise : Promise<boolean>, cur : string) : Promise<boolean> => {
openSchemePromise = promise.catch<boolean>(() : Promise<boolean> => openSchema(cur))
return openSchemePromise!
}, openSchemePromise!)
return openSchemePromise!
}
}
return null
}
function installPackage() {
installing.value = true;
// #ifdef APP
uni.installApk({
filePath: tempFilePath.value,
success: _ => {
installing.value = false;
installed.value = true;
},
fail: err => {
console.error('installApk fail', err);
// 安装失败需要重新下载安装包
installing.value = false;
installed.value = false;
uni.showModal({
title: '更新失败,请重新下载',
content: `uni.installApk 错误码 ${err.errCode}`,
showCancel: false
});
}
});
// 安装跳出覆盖安装,此处直接返回上一页
if (!is_mandatory.value) {
uni.navigateBack()
}
// #endif
}
function downloadFail() {
const errMsg = '下载失败,请点击重试'
downloadSuccess.value = false;
downloading.value = false;
downLoadPercent.value = 0;
downloadedSize.value = 0;
packageFileSize.value = 0;
downLoadBtnText.value = errMsg
downloadTask = null;
if (needNotificationProgress.value) {
// #ifdef APP-ANDROID
finishNotificationProgress({
title: '升级包下载失败',
content: '请重新检查更新',
onClick() { }
} as FinishNotificationProgressOptions);
// #endif
}
}
function downLoadComplete() {
downloadSuccess.value = true;
downloading.value = false;
downLoadPercent.value = 0
downloadedSize.value = 0
packageFileSize.value = 0
downloadTask = null;
if (needNotificationProgress.value) {
// #ifdef APP-ANDROID
finishNotificationProgress({
title: "安装升级包",
content: "下载完成",
onClick() { }
} as FinishNotificationProgressOptions)
installPackage();
// #endif
return
}
// 强制更新,直接安装
if (is_mandatory.value) {
installPackage();
}
}
function downloadPackage() {
//下载包
downloadTask = uni.downloadFile({
url: url.value,
success: res => {
if (res.statusCode == 200) {
tempFilePath.value = res.tempFilePath
downLoadComplete()
} else {
console.log('downloadFile err: ', res);
downloadFail()
}
},
fail: err => {
console.log('downloadFile err: ', err);
downloadFail()
}
});
if (downloadTask !== null) {
downloading.value = true;
if (needNotificationProgress.value) {
closePopup()
}
downloadTask!.onProgressUpdate(res => {
downLoadPercent.value = parseFloat(res.progress.toFixed(0));
downloadedSize.value = parseFloat((res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2));
packageFileSize.value = parseFloat((res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2));
if (needNotificationProgress.value) {
// #ifdef APP-ANDROID
createNotificationProgress({
title: "升级中心正在下载安装包……",
content: `${downLoadPercent.value}%`,
progress: downLoadPercent.value,
onClick: () => {
if (!downloadSuccess.value) {
askAbortDownload()
}
}
} as CreateNotificationProgressOptions)
// #endif
}
});
}
}
function updateApp() {
const checkStoreSchemeResult = checkStoreScheme()
if (checkStoreSchemeResult !== null) {
checkStoreSchemeResult
.then(_ => { })
.catch(() => { downloadPackage() })
.finally(() => {
openSchemePromise = null
})
} else { downloadPackage() }
}
onUnload(() => {
if (needNotificationProgress.value) {
// #ifdef APP-ANDROID
cancelNotificationProgress()
// #endif
}
})
onLoad((onLoadOptions : OnLoadOptions) => {
const local_storage_key : string | null = onLoadOptions['local_storage_key']
if (local_storage_key == null) {
console.error('local_storage_key为空,请检查后重试')
closePopup()
return;
};
const localPackageInfo = uni.getStorageSync(local_storage_key);
if (localPackageInfo == null) {
console.error('安装包信息为空,请检查后重试')
closePopup()
return;
};
show(JSON.parse<UniUpgradeCenterResult>(JSON.stringify(localPackageInfo)) as UniUpgradeCenterResult)
})
onBackPress((options : OnBackPressOptions) : boolean | null => {
if (is_mandatory.value) return true
if (!needNotificationProgress.value) {
if (downloadTask !== null) {
downloadTask!.abort()
}
}
return false
})
</script>
<style>
.flex-center {
/* #ifndef APP-NVUE | UNI-APP-X */
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, .65);
}
.content {
position: relative;
top: 0;
width: 600rpx;
background-color: transparent;
}
.text {
font-family: Source Han Sans CN;
}
.content-top {
width: 100%;
border-bottom-color: #fff;
border-bottom-width: 15px;
border-bottom-style: solid;
}
.content-space {
width: 100%;
height: 120px;
background-color: #fff;
position: absolute;
top: 30%;
z-index: -1;
}
.content-top-image {
width: 100%;
position: relative;
bottom: -10%;
}
.content-top-text {
font-size: 22px;
font-weight: bold;
color: #F8F8FA;
position: absolute;
width: 65%;
top: 50%;
left: 25px;
z-index: 1;
}
.content-body {
box-sizing: border-box;
padding: 0 25px;
width: 100%;
background-color: #fff;
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}
.content-body-title {
flex-direction: row;
align-items: center;
}
.content-body-title .version {
padding-left: 10px;
color: #fff;
font-size: 10px;
margin-left: 5px;
padding: 2px 4px;
border-radius: 10px;
background: #50aefd;
}
.title {
font-size: 16px;
font-weight: bold;
color: #3DA7FF;
line-height: 38px;
}
.footer {
height: 75px;
display: flex;
align-items: center;
justify-content: space-around;
}
.box-des-scroll {
box-sizing: border-box;
padding: 0 15px;
height: 100px;
}
.box-des {
font-size: 13px;
color: #000000;
line-height: 25px;
}
.progress-box {
width: 100%;
}
.progress {
width: 90%;
height: 20px;
}
.content-bottom {
height: 75px;
}
.close-img {
width: 35px;
height: 35px;
z-index: 1000;
position: relative;
bottom: -30%;
left: 50%;
margin-left: -17px;
}
.content-button {
width: 100%;
height: 40px;
line-height: 40px;
font-size: 15px;
font-weight: 400;
border-radius: 20px;
border: none;
color: #fff;
text-align: center;
background-color: #1785ff;
}
.flex-column {
display: flex;
flex-direction: column;
align-items: center;
}
</style>