From 44df4562409daf8e87b38926aa54e397ce329ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=A1=E5=A4=8D=E4=B9=A0?= <2353956224@qq.com> Date: Fri, 13 Mar 2026 17:48:00 +0800 Subject: [PATCH] =?UTF-8?q?mac=E4=B8=8D=E5=BC=BA=E5=88=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 3 +- src/components/UpdateNotification.vue | 39 +++- src/hooks/useUpdate.js | 206 +++++++++++------- src/pages/UpdateChecker.vue | 32 ++- ....timestamp-1773392797167-9352ce3e637ed.mjs | 25 +++ 5 files changed, 218 insertions(+), 87 deletions(-) create mode 100644 vite.config.js.timestamp-1773392797167-9352ce3e637ed.mjs diff --git a/src/App.vue b/src/App.vue index 12491b4..d2ac8ac 100644 --- a/src/App.vue +++ b/src/App.vue @@ -14,7 +14,8 @@
- + +
diff --git a/src/components/UpdateNotification.vue b/src/components/UpdateNotification.vue index 8ce93ac..f74428f 100644 --- a/src/components/UpdateNotification.vue +++ b/src/components/UpdateNotification.vue @@ -38,10 +38,31 @@

{{ updateInfo.releaseNotes }}

- + + + + + + @@ -107,6 +128,7 @@ const { progress, error, currentVersion, + isMac, checkForUpdates, downloadUpdate, installUpdate, @@ -120,4 +142,13 @@ function formatBytes(bytes) { const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}` } + +// 打开下载页面 +const openDownloadPage = () => { + if (isElectronEnv) { + window.electronAPI.openExternal?.('https://yolozs.com/') + } else { + window.open('https://yolozs.com/', '_blank') + } +} diff --git a/src/hooks/useUpdate.js b/src/hooks/useUpdate.js index 89d3867..d9732d5 100644 --- a/src/hooks/useUpdate.js +++ b/src/hooks/useUpdate.js @@ -1,94 +1,141 @@ -import { ref, onMounted, onUnmounted } from 'vue' +import { ref, onMounted, onUnmounted, computed } from 'vue' import { isElectron } from '../utils/electronBridge' -// NOTE: Since we are using JS, we don't have interfaces, but structure remains same. +// ============== 单例状态 ============== +// 在函数外部定义,确保所有组件共享同一个状态 + +const status = ref('idle') +const updateInfo = ref(null) +const progress = ref(null) +const error = ref(null) +const currentVersion = ref('') +const platform = ref('unknown') +let listenersSetup = false // 标记是否已设置监听器 +let unsubList = [] // 取消订阅列表 + +const isMac = computed(() => platform.value === 'mac') +const isWindows = computed(() => platform.value === 'windows') + +// 平台判断 +const getPlatformInfo = () => { + const navPlatform = navigator.platform || '' + const ua = navigator.userAgent || '' + const appVersion = navigator.appVersion || '' + + console.log('[useUpdate] 平台检测信息:', { + navPlatform, + userAgent: ua, + appVersion + }) + + const p = navPlatform.toLowerCase() + if (p.includes('mac')) return 'mac' + if (p.includes('win')) return 'windows' + if (p.includes('linux')) return 'linux' + + const u = ua.toLowerCase() + if (u.includes('macintosh') || u.includes('mac os') || u.includes('macintel')) return 'mac' + if (u.includes('windows') || u.includes('win32') || u.includes('win64')) return 'windows' + if (u.includes('linux')) return 'linux' + + const av = appVersion.toLowerCase() + if (av.includes('mac')) return 'mac' + if (av.includes('win')) return 'windows' + if (av.includes('linux')) return 'linux' + + return 'unknown' +} + +// 获取当前版本 +const fetchVersion = () => { + if (!isElectron()) { + currentVersion.value = 'web' + return + } + window.electronAPI.getAppVersion().then(v => { + currentVersion.value = v + }).catch(console.error) +} + +// 设置监听器(只执行一次) +const setupListeners = () => { + if (!isElectron() || listenersSetup) return + + listenersSetup = true + const api = window.electronAPI + + // 正在检查 + if (api.onUpdateChecking) { + unsubList.push(api.onUpdateChecking(() => { + status.value = 'checking' + })) + } + + // 发现新版本 + unsubList.push(api.onUpdateAvailable((info) => { + updateInfo.value = info + status.value = 'available' + error.value = null + })) + + // 无可用更新 + if (api.onUpdateNotAvailable) { + unsubList.push(api.onUpdateNotAvailable(() => { + status.value = 'idle' + })) + } + + unsubList.push(api.onUpdateProgress((prog) => { + progress.value = prog + status.value = 'downloading' + })) + + unsubList.push(api.onUpdateDownloaded(() => { + status.value = 'downloaded' + progress.value = null + })) + + unsubList.push(api.onUpdateError((err) => { + error.value = err.message + status.value = 'error' + })) + + // macOS 手动安装提示 + if (api.onUpdateManualInstall) { + unsubList.push(api.onUpdateManualInstall((info) => { + console.log('macOS 需要手动安装更新:', info.path) + alert(`更新已下载完成!\n\n请手动安装更新文件:\n${info.path}\n\n应用将退出。`) + })) + } +} + +// 清理监听器 +const cleanupListeners = () => { + unsubList.forEach(unsub => unsub && unsub()) + unsubList = [] + listenersSetup = false +} /** * 应用更新 Hook (Vue Composable) - * 管理更新状态、进度和操作 + * 单例模式:所有组件共享同一个状态和监听器 * 注意:此 Composable 仅在 Electron 环境中有效 */ export function useUpdate() { - const status = ref('idle') - const updateInfo = ref(null) - const progress = ref(null) - const error = ref(null) - const currentVersion = ref('') - - // 获取当前版本 - const fetchVersion = () => { - if (!isElectron()) { - currentVersion.value = 'web' - return - } - window.electronAPI.getAppVersion().then(v => { - currentVersion.value = v - }).catch(console.error) - } - - let unsubList = [] - - // 监听更新事件 - const setupListeners = () => { - if (!isElectron()) return - - const api = window.electronAPI - - // 正在检查 - if (api.onUpdateChecking) { - unsubList.push(api.onUpdateChecking(() => { - status.value = 'checking' - })) - } - - // 发现新版本 - unsubList.push(api.onUpdateAvailable((info) => { - updateInfo.value = info - status.value = 'available' - error.value = null - })) - - // 无可用更新 - if (api.onUpdateNotAvailable) { - unsubList.push(api.onUpdateNotAvailable(() => { - status.value = 'idle' - })) - } - - unsubList.push(api.onUpdateProgress((prog) => { - progress.value = prog - status.value = 'downloading' - })) - - unsubList.push(api.onUpdateDownloaded(() => { - status.value = 'downloaded' - progress.value = null - })) - - unsubList.push(api.onUpdateError((err) => { - error.value = err.message - status.value = 'error' - })) - - // macOS 手动安装提示 - if (api.onUpdateManualInstall) { - unsubList.push(api.onUpdateManualInstall((info) => { - console.log('macOS 需要手动安装更新:', info.path) - // 显示提示让用户知道需要手动安装 - alert(`更新已下载完成!\n\n请手动安装更新文件:\n${info.path}\n\n应用将退出。`) - })) - } - } - + // 每个组件挂载时的初始化逻辑 onMounted(() => { + // 检测平台(确保只设置一次) + if (isElectron() && platform.value === 'unknown') { + platform.value = getPlatformInfo() + console.log('[useUpdate] 平台检测结果:', platform.value) + } fetchVersion() setupListeners() }) - onUnmounted(() => { - unsubList.forEach(unsub => unsub && unsub()) - unsubList = [] - }) + // 注意:不在这里清理监听器,因为是单例模式 + // 只在最后一个组件卸载时才清理(这里简化处理,不清理) + // 如果需要严格清理,可以使用引用计数 const checkForUpdates = () => { if (!isElectron()) return @@ -132,6 +179,9 @@ export function useUpdate() { progress, error, currentVersion, + platform, + isMac, + isWindows, checkForUpdates, downloadUpdate, installUpdate, diff --git a/src/pages/UpdateChecker.vue b/src/pages/UpdateChecker.vue index 940a2af..e439121 100644 --- a/src/pages/UpdateChecker.vue +++ b/src/pages/UpdateChecker.vue @@ -76,10 +76,24 @@

{{ updateInfo.releaseNotes }}

- + + + + +

必须更新后才能使用程序 @@ -185,6 +199,7 @@ const { progress, error, currentVersion, + isMac, checkForUpdates, downloadUpdate, installUpdate @@ -304,6 +319,15 @@ function formatBytes(bytes) { return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}` } +// 打开下载页面(Mac 用户) +function openDownloadPage() { + if (isElectronEnv) { + window.electronAPI.openExternal?.('https://yolozs.com/') + } else { + window.open('https://yolozs.com/', '_blank') + } +} + onUnmounted(() => { if (timeoutTimer) clearTimeout(timeoutTimer) if (countdownTimer) clearInterval(countdownTimer) diff --git a/vite.config.js.timestamp-1773392797167-9352ce3e637ed.mjs b/vite.config.js.timestamp-1773392797167-9352ce3e637ed.mjs new file mode 100644 index 0000000..0e9836f --- /dev/null +++ b/vite.config.js.timestamp-1773392797167-9352ce3e637ed.mjs @@ -0,0 +1,25 @@ +// vite.config.js +import { defineConfig } from "file:///D:/git/gitea/web-fusion/node_modules/vite/dist/node/index.js"; +import vue from "file:///D:/git/gitea/web-fusion/node_modules/@vitejs/plugin-vue/dist/index.mjs"; +import { resolve } from "path"; +var __vite_injected_original_dirname = "D:\\git\\gitea\\web-fusion"; +var vite_config_default = defineConfig({ + plugins: [vue()], + resolve: { + alias: { + "@": resolve(__vite_injected_original_dirname, "src") + } + }, + server: { + port: 5173, + host: true + }, + build: { + outDir: "dist", + sourcemap: false + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxnaXRcXFxcZ2l0ZWFcXFxcd2ViLWZ1c2lvblwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRDpcXFxcZ2l0XFxcXGdpdGVhXFxcXHdlYi1mdXNpb25cXFxcdml0ZS5jb25maWcuanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0Q6L2dpdC9naXRlYS93ZWItZnVzaW9uL3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHZ1ZSBmcm9tICdAdml0ZWpzL3BsdWdpbi12dWUnXHJcbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tICdwYXRoJ1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICAgIHBsdWdpbnM6IFt2dWUoKV0sXHJcbiAgICByZXNvbHZlOiB7XHJcbiAgICAgICAgYWxpYXM6IHtcclxuICAgICAgICAgICAgJ0AnOiByZXNvbHZlKF9fZGlybmFtZSwgJ3NyYycpXHJcbiAgICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICAgIHBvcnQ6IDUxNzMsXHJcbiAgICAgICAgaG9zdDogdHJ1ZVxyXG4gICAgfSxcclxuICAgIGJ1aWxkOiB7XHJcbiAgICAgICAgb3V0RGlyOiAnZGlzdCcsXHJcbiAgICAgICAgc291cmNlbWFwOiBmYWxzZVxyXG4gICAgfVxyXG59KVxyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQStQLFNBQVMsb0JBQW9CO0FBQzVSLE9BQU8sU0FBUztBQUNoQixTQUFTLGVBQWU7QUFGeEIsSUFBTSxtQ0FBbUM7QUFLekMsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDeEIsU0FBUyxDQUFDLElBQUksQ0FBQztBQUFBLEVBQ2YsU0FBUztBQUFBLElBQ0wsT0FBTztBQUFBLE1BQ0gsS0FBSyxRQUFRLGtDQUFXLEtBQUs7QUFBQSxJQUNqQztBQUFBLEVBQ0o7QUFBQSxFQUNBLFFBQVE7QUFBQSxJQUNKLE1BQU07QUFBQSxJQUNOLE1BQU07QUFBQSxFQUNWO0FBQUEsRUFDQSxPQUFPO0FBQUEsSUFDSCxRQUFRO0FBQUEsSUFDUixXQUFXO0FBQUEsRUFDZjtBQUNKLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==