New file |
| | |
| | | <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="../images/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: 28rpx;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 && !installed" 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="../images/app_update_close.png" |
| | | @click.stop="closeUpdate"></image> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | const localFilePathKey = '__localFilePath__' |
| | | const platform_iOS = 'iOS'; |
| | | let downloadTask = null; |
| | | |
| | | /** |
| | | * 对比版本号,如需要,请自行修改判断规则 |
| | | * 支持比对 ("3.0.0.0.0.1.0.1", "3.0.0.0.0.1") ("3.0.0.1", "3.0") ("3.1.1", "3.1.1.1") 之类的 |
| | | * @param {Object} v1 |
| | | * @param {Object} v2 |
| | | * v1 > v2 return 1 |
| | | * v1 < v2 return -1 |
| | | * v1 == v2 return 0 |
| | | */ |
| | | 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; |
| | | } |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | // 从之前下载安装 |
| | | installForBeforeFilePath: '', |
| | | |
| | | // 安装 |
| | | installed: false, |
| | | installing: false, |
| | | |
| | | // 下载 |
| | | downloadSuccess: false, |
| | | downloading: false, |
| | | |
| | | downLoadPercent: 0, |
| | | downloadedSize: 0, |
| | | packageFileSize: 0, |
| | | |
| | | tempFilePath: '', // 要安装的本地包地址 |
| | | |
| | | // 默认安装包信息 |
| | | title: '更新日志', |
| | | contents: '', |
| | | is_mandatory: false, |
| | | |
| | | // 可自定义属性 |
| | | subTitle: '发现新版本', |
| | | downLoadBtnTextiOS: '立即跳转更新', |
| | | downLoadBtnText: '立即下载更新', |
| | | downLoadingText: '安装包下载中,请稍后' |
| | | } |
| | | }, |
| | | onLoad({ |
| | | local_storage_key |
| | | }) { |
| | | 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 requiredKey = ['version', 'url', 'type'] |
| | | for (let key in localPackageInfo) { |
| | | if (requiredKey.indexOf(key) !== -1 && !localPackageInfo[key]) { |
| | | console.error(`参数 ${key} 必填,请检查后重试`) |
| | | uni.navigateBack() |
| | | return; |
| | | } |
| | | } |
| | | |
| | | Object.assign(this, localPackageInfo) |
| | | this.checkLocalStoragePackage() |
| | | }, |
| | | onBackPress() { |
| | | // 强制更新不允许返回 |
| | | if (this.is_mandatory) { |
| | | return true |
| | | } |
| | | |
| | | downloadTask && downloadTask.abort() |
| | | }, |
| | | computed: { |
| | | isWGT() { |
| | | return this.type === 'wgt' |
| | | }, |
| | | isiOS() { |
| | | return !this.isWGT ? this.platform.includes(platform_iOS) : false; |
| | | } |
| | | }, |
| | | methods: { |
| | | checkLocalStoragePackage() { |
| | | // 如果已经有下载好的包,则直接提示安装 |
| | | const localFilePathRecord = uni.getStorageSync(localFilePathKey) |
| | | if (localFilePathRecord) { |
| | | const { |
| | | version, |
| | | savedFilePath, |
| | | installed |
| | | } = localFilePathRecord |
| | | |
| | | // 比对版本 |
| | | if (!installed && compare(version, this.version) === 0) { |
| | | this.downloadSuccess = true; |
| | | this.installForBeforeFilePath = savedFilePath; |
| | | this.tempFilePath = savedFilePath |
| | | } else { |
| | | // 如果保存的包版本小 或 已安装过,则直接删除 |
| | | this.deleteSavedFile(savedFilePath) |
| | | } |
| | | } |
| | | }, |
| | | async closeUpdate() { |
| | | if (this.downloading) { |
| | | if (this.is_mandatory) { |
| | | return uni.showToast({ |
| | | title: '下载中,请稍后……', |
| | | icon: 'none', |
| | | duration: 500 |
| | | }) |
| | | } |
| | | uni.showModal({ |
| | | title: '是否取消下载?', |
| | | cancelText: '否', |
| | | confirmText: '是', |
| | | success: res => { |
| | | if (res.confirm) { |
| | | downloadTask && downloadTask.abort() |
| | | uni.navigateBack() |
| | | } |
| | | } |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | if (this.downloadSuccess && this.tempFilePath) { |
| | | // 包已经下载完毕,稍后安装,将包保存在本地 |
| | | await this.saveFile(this.tempFilePath, this.version) |
| | | uni.navigateBack() |
| | | return; |
| | | } |
| | | |
| | | uni.navigateBack() |
| | | }, |
| | | downloadPackage() { |
| | | this.downloading = true; |
| | | |
| | | //下载包 |
| | | downloadTask = uni.downloadFile({ |
| | | url: this.url, |
| | | success: res => { |
| | | if (res.statusCode == 200) { |
| | | this.downloadSuccess = true; |
| | | this.tempFilePath = res.tempFilePath |
| | | |
| | | // 强制更新,直接安装 |
| | | if (this.is_mandatory) { |
| | | this.installPackage(); |
| | | } |
| | | } |
| | | }, |
| | | complete: () => { |
| | | this.downloading = false; |
| | | |
| | | this.downLoadPercent = 0 |
| | | this.downloadedSize = 0 |
| | | this.packageFileSize = 0 |
| | | |
| | | downloadTask = null; |
| | | } |
| | | }); |
| | | |
| | | downloadTask.onProgressUpdate(res => { |
| | | this.downLoadPercent = res.progress; |
| | | this.downloadedSize = (res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2); |
| | | this.packageFileSize = (res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2); |
| | | }); |
| | | }, |
| | | installPackage() { |
| | | // #ifdef APP-PLUS |
| | | // wgt资源包安装 |
| | | if (this.isWGT) { |
| | | this.installing = true; |
| | | } |
| | | |
| | | plus.runtime.install(this.tempFilePath, { |
| | | force: false |
| | | }, async res => { |
| | | this.installing = false; |
| | | this.installed = true; |
| | | |
| | | // wgt包,安装后会提示 安装成功,是否重启 |
| | | if (this.isWGT) { |
| | | // 强制更新安装完成重启 |
| | | if (this.is_mandatory) { |
| | | uni.showLoading({ |
| | | icon: 'none', |
| | | title: '安装成功,正在重启……' |
| | | }) |
| | | |
| | | setTimeout(() => { |
| | | uni.hideLoading() |
| | | this.restart(); |
| | | }, 1000) |
| | | } |
| | | } else { |
| | | const localFilePathRecord = uni.getStorageSync(localFilePathKey) |
| | | uni.setStorageSync(localFilePathKey, { |
| | | ...localFilePathRecord, |
| | | installed: true |
| | | }) |
| | | } |
| | | }, async err => { |
| | | // 如果是安装之前的包,安装失败后删除之前的包 |
| | | if (this.installForBeforeFilePath) { |
| | | await this.deleteSavedFile(this.installForBeforeFilePath) |
| | | this.installForBeforeFilePath = ''; |
| | | } |
| | | |
| | | // 安装失败需要重新下载安装包 |
| | | this.installing = false; |
| | | this.installed = false; |
| | | |
| | | uni.showModal({ |
| | | title: `更新失败${this.isWGT ? '' : ',APK文件不存在'},请重新下载`, |
| | | content: err.message, |
| | | showCancel: false |
| | | }); |
| | | }); |
| | | |
| | | // 非wgt包,安装跳出覆盖安装,此处直接返回上一页 |
| | | if (!this.isWGT) { |
| | | uni.navigateBack() |
| | | } |
| | | // #endif |
| | | }, |
| | | restart() { |
| | | this.installed = false; |
| | | // #ifdef APP-PLUS |
| | | //更新完重启app |
| | | plus.runtime.restart(); |
| | | // #endif |
| | | }, |
| | | async saveFile(tempFilePath, version) { |
| | | const [err, res] = await uni.saveFile({ |
| | | tempFilePath |
| | | }) |
| | | if (err) { |
| | | return; |
| | | } |
| | | uni.setStorageSync(localFilePathKey, { |
| | | version, |
| | | savedFilePath: res.savedFilePath |
| | | }) |
| | | }, |
| | | deleteSavedFile(filePath) { |
| | | uni.removeStorageSync(localFilePathKey) |
| | | return uni.removeSavedFile({ |
| | | filePath |
| | | }) |
| | | }, |
| | | jumpToAppStore() { |
| | | plus.runtime.openURL(this.url); |
| | | } |
| | | } |
| | | } |
| | | </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, .65); |
| | | } |
| | | |
| | | .botton-radius { |
| | | border-bottom-left-radius: 30rpx; |
| | | border-bottom-right-radius: 30rpx; |
| | | } |
| | | |
| | | .content { |
| | | position: relative; |
| | | top: 0; |
| | | width: 600rpx; |
| | | background-color: #fff; |
| | | box-sizing: border-box; |
| | | padding: 0 50rpx; |
| | | font-family: Source Han Sans CN; |
| | | } |
| | | |
| | | .text { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | line-height: 200px; |
| | | text-align: center; |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | .content-top { |
| | | position: absolute; |
| | | top: -195rpx; |
| | | left: 0; |
| | | width: 600rpx; |
| | | height: 270rpx; |
| | | } |
| | | |
| | | .content-top-text { |
| | | font-size: 45rpx; |
| | | font-weight: bold; |
| | | color: #F8F8FA; |
| | | position: absolute; |
| | | top: 120rpx; |
| | | left: 50rpx; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .content-header { |
| | | height: 70rpx; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 33rpx; |
| | | font-weight: bold; |
| | | color: #3DA7FF; |
| | | line-height: 38px; |
| | | } |
| | | |
| | | .footer { |
| | | height: 150rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-around; |
| | | } |
| | | |
| | | .box-des-scroll { |
| | | box-sizing: border-box; |
| | | padding: 0 40rpx; |
| | | height: 200rpx; |
| | | text-align: left; |
| | | } |
| | | |
| | | .box-des { |
| | | font-size: 26rpx; |
| | | color: #000000; |
| | | line-height: 50rpx; |
| | | } |
| | | |
| | | .progress-box { |
| | | width: 100%; |
| | | } |
| | | |
| | | .progress { |
| | | width: 90%; |
| | | height: 40rpx; |
| | | border-radius: 35px; |
| | | } |
| | | |
| | | .close-img { |
| | | width: 70rpx; |
| | | height: 70rpx; |
| | | z-index: 1000; |
| | | position: absolute; |
| | | bottom: -120rpx; |
| | | left: calc(50% - 70rpx / 2); |
| | | } |
| | | |
| | | .content-button { |
| | | text-align: center; |
| | | flex: 1; |
| | | font-size: 30rpx; |
| | | font-weight: 400; |
| | | color: #FFFFFF; |
| | | border-radius: 40rpx; |
| | | margin: 0 18rpx; |
| | | |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | |
| | | background: linear-gradient(to right, #1785ff, #3DA7FF); |
| | | } |
| | | |
| | | .flex-column { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | </style> |