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