mac不强制更新

This commit is contained in:
2026-03-13 17:48:00 +08:00
parent 1dd64988ba
commit 44df456240
5 changed files with 218 additions and 87 deletions

View File

@@ -14,7 +14,8 @@
<!-- 配置页面 - 使用 v-show 保持状态 --> <!-- 配置页面 - 使用 v-show 保持状态 -->
<div class="h-full w-full animate-fadeIn" v-show="currentPage === 'config'"> <div class="h-full w-full animate-fadeIn" v-show="currentPage === 'config'">
<ConfigPage @go-to-browser="handleGoToBrowser" @logout="handleLogout" /> <ConfigPage @go-to-browser="handleGoToBrowser" @logout="handleLogout" />
<UpdateNotification /> <!-- 更新通知组件启动时已在 UpdateChecker 检查此处暂不显示 -->
<!-- <UpdateNotification /> -->
</div> </div>
<!-- 浏览器页面 --> <!-- 浏览器页面 -->

View File

@@ -38,10 +38,31 @@
<p v-if="updateInfo.releaseNotes" class="text-gray-500 text-xs bg-gray-50 p-2 rounded-lg line-clamp-3"> <p v-if="updateInfo.releaseNotes" class="text-gray-500 text-xs bg-gray-50 p-2 rounded-lg line-clamp-3">
{{ updateInfo.releaseNotes }} {{ updateInfo.releaseNotes }}
</p> </p>
<button @click="downloadUpdate"
class="w-full py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg font-medium hover:from-blue-600 hover:to-blue-700 transition-all shadow-md hover:shadow-lg"> <!-- Mac 用户跳转下载页面 -->
下载更新 <template v-if="isMac">
</button> <p class="text-amber-600 text-sm bg-amber-50 p-2 rounded-lg">
Mac 版本请前往官网下载最新安装包
</p>
<div class="flex gap-2">
<button @click="openDownloadPage"
class="flex-1 py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg font-medium hover:from-blue-600 hover:to-blue-700 transition-all shadow-md hover:shadow-lg">
前往下载
</button>
<button @click="dismissUpdate"
class="px-4 py-2 bg-gray-100 text-gray-600 rounded-lg font-medium hover:bg-gray-200 transition-all">
稍后提醒
</button>
</div>
</template>
<!-- Windows 用户正常下载更新 -->
<template v-else>
<button @click="downloadUpdate"
class="w-full py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg font-medium hover:from-blue-600 hover:to-blue-700 transition-all shadow-md hover:shadow-lg">
下载更新
</button>
</template>
</div> </div>
<!-- 下载中 --> <!-- 下载中 -->
@@ -107,6 +128,7 @@ const {
progress, progress,
error, error,
currentVersion, currentVersion,
isMac,
checkForUpdates, checkForUpdates,
downloadUpdate, downloadUpdate,
installUpdate, installUpdate,
@@ -120,4 +142,13 @@ function formatBytes(bytes) {
const i = Math.floor(Math.log(bytes) / Math.log(k)) const i = Math.floor(Math.log(bytes) / Math.log(k))
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}` 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')
}
}
</script> </script>

View File

@@ -1,94 +1,141 @@
import { ref, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted, computed } from 'vue'
import { isElectron } from '../utils/electronBridge' 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) * 应用更新 Hook (Vue Composable)
* 管理更新状态、进度和操作 * 单例模式:所有组件共享同一个状态和监听器
* 注意:此 Composable 仅在 Electron 环境中有效 * 注意:此 Composable 仅在 Electron 环境中有效
*/ */
export function useUpdate() { 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(() => { onMounted(() => {
// 检测平台(确保只设置一次)
if (isElectron() && platform.value === 'unknown') {
platform.value = getPlatformInfo()
console.log('[useUpdate] 平台检测结果:', platform.value)
}
fetchVersion() fetchVersion()
setupListeners() setupListeners()
}) })
onUnmounted(() => { // 注意:不在这里清理监听器,因为是单例模式
unsubList.forEach(unsub => unsub && unsub()) // 只在最后一个组件卸载时才清理(这里简化处理,不清理)
unsubList = [] // 如果需要严格清理,可以使用引用计数
})
const checkForUpdates = () => { const checkForUpdates = () => {
if (!isElectron()) return if (!isElectron()) return
@@ -132,6 +179,9 @@ export function useUpdate() {
progress, progress,
error, error,
currentVersion, currentVersion,
platform,
isMac,
isWindows,
checkForUpdates, checkForUpdates,
downloadUpdate, downloadUpdate,
installUpdate, installUpdate,

View File

@@ -76,10 +76,24 @@
<p class="text-gray-600 text-sm whitespace-pre-wrap">{{ updateInfo.releaseNotes }}</p> <p class="text-gray-600 text-sm whitespace-pre-wrap">{{ updateInfo.releaseNotes }}</p>
</div> </div>
<button @click="downloadUpdate" <!-- Mac 用户跳转下载页面 -->
class="w-full py-3 bg-gradient-to-r from-blue-600 to-blue-500 text-white rounded-lg font-medium hover:from-blue-700 hover:to-blue-600 transition-all shadow-sm"> <template v-if="isMac">
立即下载更新 <p class="text-amber-600 text-sm bg-amber-50 p-3 rounded-lg text-center">
</button> Mac 版本请前往官网下载最新安装包
</p>
<button @click="openDownloadPage"
class="w-full py-3 bg-gradient-to-r from-blue-600 to-blue-500 text-white rounded-lg font-medium hover:from-blue-700 hover:to-blue-600 transition-all shadow-sm">
前往下载
</button>
</template>
<!-- Windows 用户正常下载更新 -->
<template v-else>
<button @click="downloadUpdate"
class="w-full py-3 bg-gradient-to-r from-blue-600 to-blue-500 text-white rounded-lg font-medium hover:from-blue-700 hover:to-blue-600 transition-all shadow-sm">
立即下载更新
</button>
</template>
<p class="text-center text-gray-400 text-xs"> <p class="text-center text-gray-400 text-xs">
必须更新后才能使用程序 必须更新后才能使用程序
@@ -185,6 +199,7 @@ const {
progress, progress,
error, error,
currentVersion, currentVersion,
isMac,
checkForUpdates, checkForUpdates,
downloadUpdate, downloadUpdate,
installUpdate installUpdate
@@ -304,6 +319,15 @@ function formatBytes(bytes) {
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}` 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(() => { onUnmounted(() => {
if (timeoutTimer) clearTimeout(timeoutTimer) if (timeoutTimer) clearTimeout(timeoutTimer)
if (countdownTimer) clearInterval(countdownTimer) if (countdownTimer) clearInterval(countdownTimer)

View File

@@ -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==