diff --git a/desktop-client/src/App.vue b/desktop-client/src/App.vue index 0445e82..69c2f87 100644 --- a/desktop-client/src/App.vue +++ b/desktop-client/src/App.vue @@ -132,6 +132,12 @@ const updateState = reactive({ lastCheckedAt: "", message: "", }); +const AUTO_UPDATE_CHECK_INTERVAL_MS = 30 * 60 * 1000; +const updateRuntime = reactive({ + downloading: false, + promptedVersion: "", + mandatoryInstalledVersion: "", +}); const contextMenu = reactive({ visible: false, x: 0, @@ -147,6 +153,7 @@ const dropState = reactive({ }); let unlistenDragDrop: UnlistenFn | null = null; let syncTimer: ReturnType | null = null; +let updateCheckTimer: ReturnType | null = null; const toast = reactive({ visible: false, @@ -557,6 +564,13 @@ function clearSyncScheduler() { syncState.nextRunAt = ""; } +function clearUpdateScheduler() { + if (updateCheckTimer) { + clearInterval(updateCheckTimer); + updateCheckTimer = null; + } +} + function syncFingerprint(item: LocalSyncFileItem) { return `${Number(item.size || 0)}:${Number(item.modifiedMs || 0)}`; } @@ -600,7 +614,11 @@ async function initClientVersion() { } } -async function checkClientUpdate(showResultToast = true) { +async function checkClientUpdate(showResultToast = true): Promise { + if (updateState.checking) { + return false; + } + updateState.checking = true; updateState.message = ""; const response = await invokeBridge("api_check_client_update", { @@ -626,22 +644,34 @@ async function checkClientUpdate(showResultToast = true) { showToast("当前已是最新版本", "info"); } } - return; + return true; } updateState.available = false; + updateState.downloadUrl = ""; updateState.message = String(response.data?.message || "检查更新失败"); if (showResultToast) { showToast(updateState.message, "error"); } + return false; } -async function installLatestUpdate() { - if (!updateState.downloadUrl) { - showToast("当前没有可用的更新下载地址", "info"); - return; +async function installLatestUpdate(trigger: "manual" | "auto" = "manual"): Promise { + if (updateRuntime.downloading) { + if (trigger === "manual") { + showToast("更新包正在下载,请稍候", "info"); + } + return false; } + if (!updateState.downloadUrl) { + if (trigger === "manual") { + showToast("当前没有可用的更新下载地址", "info"); + } + return false; + } + + updateRuntime.downloading = true; const taskId = `UPD-${Date.now()}`; const installerName = `wanwan-cloud-desktop_v${updateState.latestVersion || updateState.currentVersion}.exe`; prependTransferTask({ @@ -661,33 +691,78 @@ async function installLatestUpdate() { fileName: installerName, }); - if (response.ok && response.data?.success) { + try { + if (response.ok && response.data?.success) { + updateTransferTask(taskId, { + speed: "-", + progress: 100, + status: "done", + note: "更新包已下载,准备启动安装", + }); + const savePath = String(response.data?.savePath || "").trim(); + if (savePath) { + try { + await openPath(savePath); + } catch (error) { + console.error("open installer failed", error); + } + } + showToast("更新包已下载,已尝试启动安装程序", "success"); + return true; + } + + const message = String(response.data?.message || "下载更新包失败"); updateTransferTask(taskId, { speed: "-", - progress: 100, - status: "done", - note: "更新包已下载,准备启动安装", + progress: 0, + status: "failed", + note: message, }); - const savePath = String(response.data?.savePath || "").trim(); - if (savePath) { - try { - await openPath(savePath); - } catch (error) { - console.error("open installer failed", error); - } - } - showToast("更新包已下载,已尝试启动安装程序", "success"); + showToast(message, "error"); + return false; + } finally { + updateRuntime.downloading = false; + } +} + +async function runAutoUpdateCycle(trigger: "startup" | "login" | "timer" = "startup") { + const checked = await checkClientUpdate(false); + if (!checked) return; + + if (!updateState.available || !updateState.downloadUrl) { return; } - const message = String(response.data?.message || "下载更新包失败"); - updateTransferTask(taskId, { - speed: "-", - progress: 0, - status: "failed", - note: message, - }); - showToast(message, "error"); + const latestVersion = String(updateState.latestVersion || "").trim(); + if (!latestVersion) return; + + if (updateState.mandatory) { + if (updateRuntime.mandatoryInstalledVersion === latestVersion) { + return; + } + + showToast(`检测到强制更新 v${latestVersion},正在下载升级包`, "info"); + const installed = await installLatestUpdate("auto"); + if (installed) { + updateRuntime.mandatoryInstalledVersion = latestVersion; + } + return; + } + + if (updateRuntime.promptedVersion === latestVersion) { + return; + } + + updateRuntime.promptedVersion = latestVersion; + if (trigger === "timer") { + showToast(`发现新版本 v${latestVersion},可在“版本更新”中安装`, "info"); + return; + } + + const confirmed = window.confirm(`发现新版本 v${latestVersion},是否立即下载并安装?`); + if (confirmed) { + await installLatestUpdate("auto"); + } } async function chooseSyncDirectory() { @@ -747,6 +822,16 @@ function rebuildSyncScheduler() { }, intervalMinutes * 60 * 1000); } +function rebuildUpdateScheduler() { + clearUpdateScheduler(); + if (!authenticated.value) { + return; + } + updateCheckTimer = setInterval(() => { + void runAutoUpdateCycle("timer"); + }, AUTO_UPDATE_CHECK_INTERVAL_MS); +} + async function clearSyncSnapshot() { if (!syncState.localDir.trim()) { showToast("请先配置本地同步目录", "info"); @@ -1034,9 +1119,10 @@ async function restoreSession() { authenticated.value = true; loadSyncConfig(); rebuildSyncScheduler(); + rebuildUpdateScheduler(); await loadFiles("/"); await loadShares(true); - await checkClientUpdate(false); + await runAutoUpdateCycle("startup"); } function buildItemPath(item: FileItem) { @@ -1101,11 +1187,12 @@ async function handleLogin() { showToast("登录成功,正在同步文件目录", "success"); loadSyncConfig(); rebuildSyncScheduler(); + rebuildUpdateScheduler(); await loadFiles("/"); if (!user.value) { await loadProfile(); } - await checkClientUpdate(false); + await runAutoUpdateCycle("login"); return; } @@ -1117,6 +1204,7 @@ async function handleLogin() { async function handleLogout() { await invokeBridge("api_logout", { baseUrl: appConfig.baseUrl }); clearSyncScheduler(); + clearUpdateScheduler(); authenticated.value = false; user.value = null; files.value = []; @@ -1137,6 +1225,9 @@ async function handleLogout() { syncState.lastRunAt = ""; syncState.lastSummary = ""; syncState.nextRunAt = ""; + updateRuntime.downloading = false; + updateRuntime.promptedVersion = ""; + updateRuntime.mandatoryInstalledVersion = ""; showToast("已退出客户端", "info"); } @@ -1611,6 +1702,7 @@ onBeforeUnmount(() => { window.removeEventListener("click", handleGlobalClick); window.removeEventListener("keydown", handleGlobalKey); clearSyncScheduler(); + clearUpdateScheduler(); if (unlistenDragDrop) { unlistenDragDrop(); unlistenDragDrop = null; @@ -1753,10 +1845,12 @@ onBeforeUnmount(() => { @@ -1959,6 +2053,7 @@ onBeforeUnmount(() => {
+ 自动检查:已开启(每 30 分钟) 上次检查:{{ updateState.lastCheckedAt ? formatDate(updateState.lastCheckedAt) : "-" }} 提示:{{ updateState.message || "可手动点击“检查更新”获取最新信息" }}