import config from "@/config"; import { getAllVersion } from "@/api/viewIndex"; import bus from "@/plugins/bus"; function compareVersion(v1, v2) { const s1 = String(v1 || "").replace(/[^\d.]/g, "").split(".").map((n) => Number(n) || 0); const s2 = String(v2 || "").replace(/[^\d.]/g, "").split(".").map((n) => Number(n) || 0); const len = Math.max(s1.length, s2.length); for (let i = 0; i < len; i += 1) { const n1 = s1[i] || 0; const n2 = s2[i] || 0; if (n1 > n2) return 1; if (n1 < n2) return -1; } return 0; } function formatFileSize(fileSize) { const size = Number(fileSize); if (!Number.isFinite(size) || size <= 0) return ""; if (size >= 1024 * 1024 * 1024) return `${(size / (1024 * 1024 * 1024)).toFixed(2)}GB`; if (size >= 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(2)}MB`; if (size >= 1024) return `${(size / 1024).toFixed(2)}KB`; return `${size}B`; } async function getCurrentVersion(logPrefix) { let currentVersion = config?.appInfo?.version || "1.0.0"; console.log(`${logPrefix} 开始获取当前版本,默认值:`, currentVersion); // #ifdef MP-WEIXIN try { const accountInfo = uni.getAccountInfoSync(); if (accountInfo?.miniProgram?.version) { currentVersion = accountInfo.miniProgram.version; console.log(`${logPrefix} 当前环境=MP-WEIXIN,版本=`, currentVersion); return currentVersion; } } catch (e) { console.log(`${logPrefix} MP-WEIXIN 获取版本失败:`, e); } // #endif // #ifdef APP-PLUS try { // APP-PLUS 下,plus.runtime.version 不是业务版本号(经常是运行时/SDK版本), // 这里改用 getProperty 取系统层面的 app version。 // @ts-ignore if (plus?.runtime?.getProperty) { // @ts-ignore const appid = plus.runtime.appid; const appInfo = await new Promise((resolve) => { // @ts-ignore plus.runtime.getProperty(appid, (info) => resolve(info || {})); }); const v = appInfo?.version || appInfo?.versionName || appInfo?.appVersion || ""; if (v) { currentVersion = String(v); console.log(`${logPrefix} 当前环境=APP-PLUS,版本=`, currentVersion); return currentVersion; } console.log(`${logPrefix} APP-PLUS 获取到的版本字段为空,将使用配置版本:`, currentVersion); } } catch (e) { console.log(`${logPrefix} APP-PLUS 获取版本失败:`, e); } // #endif console.log(`${logPrefix} 未读取到运行时版本,使用配置版本:`, currentVersion); return currentVersion; } function installPackage(tempFilePath, logPrefix) { console.log(`${logPrefix} 开始安装更新包,临时路径:`, tempFilePath); // #ifdef APP-PLUS // @ts-ignore plus.runtime.install( tempFilePath, {}, () => { console.log(`${logPrefix} 安装成功,等待用户确认是否重启`); uni.showModal({ title: "更新完成", content: "安装包下载成功,是否立即重启应用生效?", success: (res) => { if (res.confirm) { // @ts-ignore plus.runtime.restart(); } }, }); }, (err) => { console.log(`${logPrefix} 安装失败:`, err); uni.showToast({ title: err?.message || "安装更新包失败", icon: "none" }); } ); // #endif } function buildFullDownloadUrl(rawUrl) { const u = String(rawUrl || "").trim(); if (!u) return ""; // 已经是绝对地址,直接返回 if (/^https?:\/\//i.test(u)) return u; const base = String(config?.fileUrl || config?.baseUrl || "").replace(/\/+$/, ""); const path = u.startsWith("/") ? u : `/${u}`; return `${base}${path}`; } function emitDownloadProgress(payload, logPrefix) { try { bus.$emit("versionUpgrade:downloadProgress", payload); } catch (e) { console.log(`${logPrefix} downloadProgress 事件派发失败:`, e); } } function downloadVersionPackage(url, logPrefix, fileSize) { const fullUrl = buildFullDownloadUrl(url); console.log(`${logPrefix} 开始下载更新包:`, { rawUrl: url, fullUrl }); if (!fullUrl) { uni.showToast({ title: "更新附件地址无效", icon: "none" }); return; } const totalSize = Number(fileSize); const hasTotalSize = Number.isFinite(totalSize) && totalSize > 0; console.log(`${logPrefix} 下载大小信息:`, { fileSize, totalSize, hasTotalSize }); emitDownloadProgress({ show: true, progress: 0, title: "更新包下载中..." }, logPrefix); const task = uni.downloadFile({ url: fullUrl, success: (res) => { console.log(`${logPrefix} 下载结果:`, res); emitDownloadProgress({ show: false }, logPrefix); if (res.statusCode !== 200 || !res.tempFilePath) { console.log(`${logPrefix} 下载失败,状态码或临时路径异常`); uni.showToast({ title: "下载失败,请稍后重试", icon: "none" }); return; } const lowerUrl = String(fullUrl || "").toLowerCase(); const isInstallPkg = lowerUrl.endsWith(".wgt") || lowerUrl.endsWith(".wgtu") || lowerUrl.endsWith(".apk"); console.log(`${logPrefix} 文件类型判断:`, { lowerUrl, isInstallPkg }); if (isInstallPkg) { installPackage(res.tempFilePath, logPrefix); return; } uni.openDocument({ filePath: res.tempFilePath, showMenu: true, fail: () => { console.log(`${logPrefix} 非安装包,openDocument 失败,提示用户手动查看`); uni.showToast({ title: "下载成功,请在文件管理中查看", icon: "none" }); }, }); }, fail: (err) => { console.log(`${logPrefix} 下载请求失败:`, err); emitDownloadProgress({ show: false }, logPrefix); uni.showToast({ title: "下载失败,请检查网络", icon: "none" }); }, }); // 下载进度:部分端(APP/小程序)支持 onProgressUpdate try { if (task && typeof task.onProgressUpdate === "function") { task.onProgressUpdate((p) => { const downloadedSize = Number(p?.totalBytesWritten ?? p?.writtenBytes ?? p?.downloadedSize); const expectedSize = Number(p?.totalBytesExpectedToWrite ?? p?.totalBytesExpected ?? p?.totalSize); const denom = hasTotalSize ? totalSize : (Number.isFinite(expectedSize) && expectedSize > 0 ? expectedSize : 0); let progress = Number(p?.progress); if (!(Number.isFinite(progress) && progress >= 0 && progress <= 100) && denom > 0 && Number.isFinite(downloadedSize) && downloadedSize >= 0) { progress = (downloadedSize / denom) * 100; } if (!Number.isFinite(progress)) return; emitDownloadProgress({ show: true, progress, title: "更新包下载中..." }, logPrefix); }); } } catch (e) { console.log(`${logPrefix} onProgressUpdate 绑定失败:`, e); } } async function checkAppVersionUpgrade(logPrefix, currentVersion) { try { console.log(`${logPrefix} 开始检查版本升级`); const params = { records: "", total: 1, size: 1, current: 1, orders: "", optimizeCountSql: true, searchCount: true, optimizeJoinOfCountSql: true, maxLimit: 1, countId: "", id: 1, name: "", version: "", createTime: "2026-04-09 10:10:33", updateTime: "2026-04-09 10:10:33", createUser: 1, updateUser: 1, tenantId: 1, }; console.log(`${logPrefix} 查询参数:`, params); const res = await getAllVersion(params); console.log(`${logPrefix} 接口返回:`, res); const first = Array.isArray(res?.rows) ? res.rows[0] : Array.isArray(res?.data?.records) ? res.data.records[0] : Array.isArray(res?.data) ? res.data[0] : res?.data; if (!first) { console.log(`${logPrefix} 未获取到第一条版本数据,结束检查`); return; } console.log(`${logPrefix} 第一条版本数据:`, first); const latestVersion = String(first?.version || ""); if (!latestVersion) { console.log(`${logPrefix} 第一条数据无版本号,结束检查`); return; } const compareResult = compareVersion(latestVersion, currentVersion); console.log(`${logPrefix} 版本比较:`, { currentVersion, latestVersion, compareResult }); if (compareResult <= 0) { console.log(`${logPrefix} 当前已是最新版本,无需更新`); return; } const firstFile = Array.isArray(first?.commonFileList) ? first.commonFileList[0] : null; const downloadUrl = firstFile?.url || firstFile?.downloadUrl || ""; const fileSizeText = formatFileSize(firstFile?.fileSize); console.log(`${logPrefix} 更新附件信息:`, { downloadUrl, fileSize: firstFile?.fileSize, fileSizeText, firstFile, }); const desc = fileSizeText ? `,更新包大小约 ${fileSizeText}` : ""; uni.showModal({ title: "发现新版本", content: `当前版本 ${currentVersion},最新版本 ${latestVersion}${desc},是否立即下载?`, confirmText: "立即下载", success: (modalRes) => { console.log(`${logPrefix} 更新弹窗操作:`, modalRes); if (!modalRes.confirm) return; if (!downloadUrl) { console.log(`${logPrefix} 用户确认更新,但附件地址为空`); uni.showToast({ title: "未找到更新附件地址", icon: "none" }); return; } downloadVersionPackage(downloadUrl, logPrefix, firstFile?.fileSize); }, }); } catch (e) { console.log(`${logPrefix} 版本检查失败:`, e); } } export function createVersionUpgradeChecker(options = {}) { const throttleMs = Number(options.throttleMs) > 0 ? Number(options.throttleMs) : 3000; const logPrefix = options.logPrefix || "[version]"; let lastVersionCheckAt = 0; const triggerVersionCheck = async (from = "unknown") => { const now = Date.now(); if (now - lastVersionCheckAt < throttleMs) { console.log(`${logPrefix} 跳过重复检查,来源=${from}`); return; } lastVersionCheckAt = now; console.log(`${logPrefix} 触发版本检查,来源=${from}`); const currentVersion = await getCurrentVersion(logPrefix); await checkAppVersionUpgrade(logPrefix, currentVersion); }; return { triggerVersionCheck }; }