const compareVersion = (a = "", b = "") => { const aList = String(a) .split(".") .map((item) => Number(item || 0)); const bList = String(b) .split(".") .map((item) => Number(item || 0)); const length = Math.max(aList.length, bList.length); for (let i = 0; i < length; i += 1) { const aNum = aList[i] || 0; const bNum = bList[i] || 0; if (aNum > bNum) return 1; if (aNum < bNum) return -1; } return 0; }; let appUpgradePromise = null; /** * 全局执行 APK 整包更新。 * * App.vue / 页面全局 onShow 都可能触发更新检查,因此这里不能依赖某个页面 * 是否挂载了 upgrade 组件。使用 uni 的全局提示能力后,用户停留在任意页面 * 都能收到更新提示;模块内 Promise 同时避免重复弹窗和重复下载。 */ export const runAppUpgrade = ({ appVersion: name, note: content = "", url, forceUpdate = true, }) => { if (appUpgradePromise) return appUpgradePromise; appUpgradePromise = new Promise((resolve, reject) => { uni.showModal({ title: `发现新版本 V${name}`, content: content || "检测到新的应用版本,是否立即更新?", showCancel: !forceUpdate, confirmText: "立即更新", cancelText: "稍后再说", success: async ({ confirm }) => { if (!confirm) { resolve({ status: "cancelled" }); return; } try { uni.showLoading({ title: "下载更新中", mask: true, }); const fileName = await downloadApp(url, (percent) => { uni.showLoading({ title: `下载中 ${percent}%`, mask: true, }); }); uni.hideLoading(); installApp(fileName); resolve({ status: "install-triggered" }); } catch (error) { uni.hideLoading(); reject(error); } }, fail: reject, }); }).finally(() => { appUpgradePromise = null; }); return appUpgradePromise; }; /** * @description 检查app版本是否需要升级 * @param name:最新版本名称 * @param code:最新版本号 * @param content:更新内容 * @param url:下载链接 * @param forceUpdate:是否强制升级 */ export const checkVersion = async ({ name, //最新版本名称 code, //最新版本号 content, //更新内容 url, //下载链接 forceUpdate, //是否强制升级 }) => { const currentAppVersion = plus.runtime.version; if (compareVersion(code, currentAppVersion) > 0) { let platform = uni.getSystemInfoSync().platform; //手机平台 if (platform === "android") { uni.$emit("upgrade-app", { name, content, url, forceUpdate, }); return true; } else { uni.showModal({ title: "发现新版本 V" + name, content: "请到App store进行升级", showCancel: false, }); } } return false; }; //获取当前页面url const getCurrentPageRoute = () => { let currentRoute; let pages = getCurrentPages(); // 获取栈实例 if (pages && pages.length) { currentRoute = pages[pages.length - 1].route; } return currentRoute; }; /** * @description H5+下载App * @param downloadUrl:App下载链接 * @param progressCallBack:下载进度回调 */ export const downloadApp = (downloadUrl, progressCallBack = () => {}) => { console.log("🚀 ~ downloadApp ~ downloadUrl:", downloadUrl); // 将模拟进度变量移到事件监听外部,确保状态能累积 let mockProgress = 0; // 进度更新节流控制变量 let lastProgressTime = 0; const PROGRESS_INTERVAL = 300; // 限制300ms内最多更新一次进度 return new Promise((resolve, reject) => { // 创建下载任务 const downloadTask = plus.downloader.createDownload( downloadUrl, { method: "GET" }, (task, status) => { console.log("🚀 ~ returnnewPromise ~ task, status:", task, status); if (status == 200) { resolve(task.filename); } else { reject("fail"); uni.showToast({ title: "下载失败", duration: 1500, icon: "none", }); } } ); downloadTask.addEventListener("statechanged", (task, status) => { // 获取当前时间戳用于节流控制 const now = Date.now(); switch (task.state) { case 1: // 开始 mockProgress = 0; // 仅在开始时重置 progressCallBack(0, 0, task.totalSize || 0); break; case 2: // 已连接到服务器 break; case 3: // 已接收到数据 const hasProgress = task.totalSize && task.totalSize > 0; if (hasProgress) { mockProgress = 0; const current = parseInt( (100 * task.downloadedSize) / task.totalSize ); progressCallBack(current, task.downloadedSize, task.totalSize); } else { // 模拟进度:添加节流控制和平滑增长 if ( mockProgress < 95 && now - lastProgressTime > PROGRESS_INTERVAL ) { // 根据时间间隔动态调整增长速度,避免停滞感 const baseIncrement = 1; const maxIncrement = mockProgress < 30 ? 3 : mockProgress < 60 ? 2 : 1; const increment = Math.floor(Math.random() * maxIncrement) + baseIncrement; mockProgress = Math.min(mockProgress + increment, 95); lastProgressTime = now; // 更新最后更新时间 progressCallBack(mockProgress, task.downloadedSize, 0); } } break; case 4: // 下载完成 progressCallBack(100, task.downloadedSize, task.totalSize || 0); break; } }); downloadTask.start(); }); }; /** * @description H5+安装APP * @param fileName:app文件名 * @param callBack:安装成功回调 */ export const installApp = (fileName, callBack = () => {}) => { console.log("🚀 ~ installApp ~ fileName:", fileName); //注册广播监听app安装情况 onInstallListening(callBack); //开始安装 plus.runtime.install( plus.io.convertLocalFileSystemURL(fileName), {}, () => { //成功跳转到安装界面 }, function (error) { console.log("🚀 ~ plus.runtime.install ~ error:", error); uni.showToast({ title: "安装失败", duration: 1500, icon: "none", }); } ); }; /** * @description 注册广播监听APP是否成功 * @param callBack:安装成功回调函数 */ const onInstallListening = (callBack = () => {}) => { let mainActivity = plus.android.runtimeMainActivity(); //获取activity //生成广播接收器 let receiver = plus.android.implements( "io.dcloud.android.content.BroadcastReceiver", { onReceive: (context, intent) => { //接收广播回调 plus.android.importClass(intent); mainActivity.unregisterReceiver(receiver); //取消监听 callBack(); }, } ); let IntentFilter = plus.android.importClass("android.content.IntentFilter"); let Intent = plus.android.importClass("android.content.Intent"); let filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); //监听apk安装 filter.addDataScheme("package"); mainActivity.registerReceiver(receiver, filter); //注册广播 };