feat: 添加应用版本更新功能;实现版本检查、更新提示及国际化支持

This commit is contained in:
2025-12-30 18:07:14 +07:00
parent 7ce60b860c
commit e7e2b1bd85
6 changed files with 484 additions and 70 deletions

View File

@@ -1,3 +1,33 @@
import type { VersionInfo } from "@/api/types";
import { App } from "@capacitor/app";
import { alertController, modalController } from "@ionic/vue";
import { client } from "@/api";
import { i18n } from "@/locales";
/**
* 版本比较函数
* @param version1 版本号1如 "1.2.3"
* @param version2 版本号2如 "1.2.4"
* @returns 如果 version1 < version2 返回 -1相等返回 0大于返回 1
*/
function compareVersion(version1: string, version2: string): number {
const v1Parts = version1.split(".").map(Number);
const v2Parts = version2.split(".").map(Number);
const maxLength = Math.max(v1Parts.length, v2Parts.length);
for (let i = 0; i < maxLength; i++) {
const v1Part = v1Parts[i] || 0;
const v2Part = v2Parts[i] || 0;
if (v1Part < v2Part)
return -1;
if (v1Part > v2Part)
return 1;
}
return 0;
}
/**
* 应用更新检查组合式函数
*/
@@ -5,7 +35,31 @@ export function useAppUpdate() {
const isChecking = ref(false);
const hasUpdate = ref(false);
const latestVersion = ref("");
const currentVersion = ref("1.0.0"); // 从 package.json 或环境变量读取
const currentVersion = ref("");
const forceUpdate = ref(false);
const updateMessage = ref("");
const updateUrl = ref("");
const platform = usePlatform();
/**
* 获取当前应用版本
*/
async function getCurrentVersion(): Promise<string> {
try {
// 在原生平台使用 Capacitor App API
if (platform !== "browser") {
const appInfo = await App.getInfo();
return appInfo.version;
}
// Web 平台从环境变量或 package.json 获取
return import.meta.env.VITE_APP_VERSION || "0.0.1";
}
catch (error) {
console.error("获取当前版本失败:", error);
return "0.0.1";
}
}
/**
* 检查是否有新版本
@@ -14,44 +68,83 @@ export function useAppUpdate() {
hasUpdate: boolean;
currentVersion: string;
latestVersion?: string;
forceUpdate?: boolean;
updateMessage?: string;
updateUrl?: string;
}> {
isChecking.value = true;
try {
// 方案1: 从服务器检查版本(需要后端 API
// const response = await fetch('/api/version');
// const { version } = await response.json();
// latestVersion.value = version;
// 1. 获取当前版本
const current = await getCurrentVersion();
currentVersion.value = current;
// 方案2: 检查 Service Worker 更新PWA 应用)
if ("serviceWorker" in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.update();
const hasNewWorker = registration.waiting !== null || registration.installing !== null;
hasUpdate.value = hasNewWorker;
// 2. 从服务器检查最新版本
// TODO: 后端接口实现后替换为真实 API 调用
// const response = await client.api.app.version.get({
// query: {
// platform: platform === 'ios' ? 'ios' : platform === 'android' ? 'android' : 'web',
// currentVersion: current,
// },
// })
if (hasNewWorker) {
return {
hasUpdate: true,
currentVersion: currentVersion.value,
latestVersion: "新版本可用",
};
}
// 模拟后端返回数据(开发阶段)
await new Promise(resolve => setTimeout(resolve, 500));
const versionInfo: VersionInfo = {
version: "0.0.2", // 模拟有新版本
forceUpdate: false,
updateMessage: "修复了一些问题并优化了性能",
updateUrl: platform === "ios"
? "https://apps.apple.com/app/id123456789"
: platform === "android"
? "https://play.google.com/store/apps/details?id=riwa.ionic.app"
: "",
minSupportVersion: "0.0.1",
};
// 真实 API 调用后的逻辑
// if (response.error) {
// console.error('检查更新失败:', response.error)
// return {
// hasUpdate: false,
// currentVersion: current,
// }
// }
// const versionInfo = response.data as VersionInfo
// 3. 版本比较
const isNewVersion = compareVersion(current, versionInfo.version) < 0;
// 更新状态
latestVersion.value = versionInfo.version;
hasUpdate.value = isNewVersion;
forceUpdate.value = versionInfo.forceUpdate || false;
updateMessage.value = versionInfo.updateMessage || "";
updateUrl.value = versionInfo.updateUrl || "";
// 4. 检查是否低于最低支持版本(强制更新)
if (versionInfo.minSupportVersion) {
const isBelowMinVersion = compareVersion(current, versionInfo.minSupportVersion) < 0;
if (isBelowMinVersion) {
forceUpdate.value = true;
}
}
// 模拟检查(实际应用中需要替换为真实的 API 调用)
await new Promise(resolve => setTimeout(resolve, 1000));
// 暂时返回无更新
return {
hasUpdate: false,
currentVersion: currentVersion.value,
hasUpdate: isNewVersion,
currentVersion: current,
latestVersion: versionInfo.version,
forceUpdate: forceUpdate.value,
updateMessage: updateMessage.value,
updateUrl: updateUrl.value,
};
}
catch (error) {
console.error("检查更新失败:", error);
throw error;
const current = currentVersion.value || await getCurrentVersion();
return {
hasUpdate: false,
currentVersion: current,
};
}
finally {
isChecking.value = false;
@@ -59,9 +152,71 @@ export function useAppUpdate() {
}
/**
* 应用更新(重新加载应用)
* 打开应用商店更新页面
*/
async function openStoreUpdate(): Promise<void> {
if (!updateUrl.value) {
console.warn("没有提供更新链接");
return;
}
// 在原生平台打开外部链接
if (platform !== "browser") {
window.open(updateUrl.value, "_system");
}
else {
// Web 平台直接打开链接
window.open(updateUrl.value, "_blank");
}
}
/**
* 显示更新提示对话框
*/
async function showUpdateDialog(): Promise<void> {
const alert = await alertController.create({
header: i18n.global.t("app.update.title"),
message: updateMessage.value || i18n.global.t("app.update.message"),
backdropDismiss: !forceUpdate.value, // 强制更新时不允许关闭
buttons: forceUpdate.value
? [
{
text: i18n.global.t("app.update.now"),
role: "confirm",
handler: () => {
openStoreUpdate();
},
},
]
: [
{
text: i18n.global.t("app.update.later"),
role: "cancel",
},
{
text: i18n.global.t("app.update.now"),
role: "confirm",
handler: () => {
openStoreUpdate();
},
},
],
});
await alert.present();
}
/**
* 应用更新(重新加载应用或打开商店)
*/
async function applyUpdate(): Promise<void> {
// 如果是原生应用且有更新链接,打开应用商店
if (platform !== "browser" && updateUrl.value) {
await openStoreUpdate();
return;
}
// Web 应用检查 Service Worker 更新
if ("serviceWorker" in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration?.waiting) {
@@ -91,13 +246,31 @@ export function useAppUpdate() {
window.location.reload();
}
/**
* 自动检查更新并显示提示
*/
async function checkAndPromptUpdate(): Promise<void> {
const result = await checkForUpdate();
if (result.hasUpdate) {
await showUpdateDialog();
}
}
return {
isChecking,
hasUpdate,
currentVersion,
latestVersion,
forceUpdate,
updateMessage,
updateUrl,
checkForUpdate,
applyUpdate,
forceReload,
openStoreUpdate,
showUpdateDialog,
checkAndPromptUpdate,
getCurrentVersion,
};
}