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