| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 | <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="../static/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="../static/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>
 |