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