| <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> |