From 41fd2b2ecabb43dae5a5e8b67826e72cb245f393 Mon Sep 17 00:00:00 2001 From: digua Date: Wed, 17 Dec 2025 20:56:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/main/ipc/window.ts | 13 +++++ electron/main/update.ts | 73 ++++++++++++++++++++++---- electron/preload/index.d.ts | 5 ++ electron/preload/index.ts | 23 +++++++- src/components/common/SettingModal.vue | 64 ++++++++++++++++++---- 5 files changed, 155 insertions(+), 23 deletions(-) diff --git a/electron/main/ipc/window.ts b/electron/main/ipc/window.ts index ce19520..801172d 100644 --- a/electron/main/ipc/window.ts +++ b/electron/main/ipc/window.ts @@ -6,6 +6,7 @@ import { ipcMain, app, dialog, clipboard, shell } from 'electron' import { autoUpdater } from 'electron-updater' import * as fs from 'fs/promises' import type { IpcContext } from './types' +import { simulateUpdateDialog } from '../update' /** * 注册窗口和文件系统操作 IPC 处理器 @@ -53,11 +54,23 @@ export function registerWindowHandlers(ctx: IpcContext): void { win.webContents.openDevTools() }) + // ==================== 应用信息 ==================== + ipcMain.handle('app:getVersion', () => { + return app.getVersion() + }) + // ==================== 更新检查 ==================== ipcMain.on('check-update', () => { autoUpdater.checkForUpdates() }) + // 模拟更新弹窗(仅开发模式使用) + ipcMain.on('simulate-update', () => { + if (!app.isPackaged) { + simulateUpdateDialog(win) + } + }) + // ==================== 通用工具 ==================== ipcMain.handle('show-message', (event, args) => { event.sender.send('show-message', args) diff --git a/electron/main/update.ts b/electron/main/update.ts index 50266f5..0532c8d 100644 --- a/electron/main/update.ts +++ b/electron/main/update.ts @@ -7,26 +7,46 @@ const checkUpdate = (win) => { autoUpdater.autoDownload = false // 自动下载 autoUpdater.autoInstallOnAppQuit = true // 应用退出后自动安装 - // 绕过开发模式更新检测,模拟线上更新(Skip checkForUpdates because application is not packed and dev update config is not forced) - // Object.defineProperty(app, 'isPackaged', { - // get() { - // return true - // }, - // }) + // 开发模式下模拟更新检测(需要创建 dev-app-update.yml 文件) + // 取消下面的注释来启用开发模式更新测试 + // if (!app.isPackaged) { + // Object.defineProperty(app, 'isPackaged', { + // get() { + // return true + // }, + // }) + // } let showUpdateMessageBox = false autoUpdater.on('update-available', (info) => { // win.webContents.send('show-message', 'electron:发现新版本') if (showUpdateMessageBox) return showUpdateMessageBox = true + + // 解析更新日志 + let releaseNotes = '' + if (info.releaseNotes) { + if (typeof info.releaseNotes === 'string') { + releaseNotes = info.releaseNotes + } else if (Array.isArray(info.releaseNotes)) { + releaseNotes = info.releaseNotes.map((note) => note.note || note).join('\n') + } + // 简单清理 HTML 标签 + releaseNotes = releaseNotes.replace(/<[^>]*>/g, '').trim() + } + + const detail = releaseNotes + ? `更新内容:\n${releaseNotes}\n\n是否立即下载并安装新版本?` + : '是否立即下载并安装新版本?' + dialog .showMessageBox({ title: '发现新版本 v' + info.version, message: '发现新版本 v' + info.version, - detail: '是否立即下载并安装新版本?', + detail, buttons: ['立即下载', '取消'], - defaultId: 1, - cancelId: 2, + defaultId: 0, + cancelId: 1, type: 'question', noLink: true, }) @@ -98,8 +118,39 @@ const checkUpdate = (win) => { // 等待 3 秒再检查更新,确保窗口准备完成,用户进入系统 setTimeout(() => { - // autoUpdater.checkForUpdatesAndNotify().catch() + autoUpdater.checkForUpdates().catch((err) => { + console.log('[Update] 检查更新失败:', err) + }) }, 3000) } -export { checkUpdate } +/** + * 模拟更新弹窗(仅用于开发测试) + * 控制台通过:window.api.app.simulateUpdate() 测试 + */ +const simulateUpdateDialog = (win) => { + const mockInfo = { + version: '9.9.9', + releaseNotes: `## 更新内容\n\n- 🎉 新增聊天记录查看器\n- 🔧 修复已知问题\n- ⚡️ 性能优化`, + } + + // 解析更新日志 + let releaseNotes = mockInfo.releaseNotes.replace(/<[^>]*>/g, '').trim() + + const detail = releaseNotes + ? `更新内容:\n${releaseNotes}\n\n是否立即下载并安装新版本?` + : '是否立即下载并安装新版本?' + + dialog.showMessageBox({ + title: '发现新版本 v' + mockInfo.version, + message: '发现新版本 v' + mockInfo.version, + detail, + buttons: ['立即下载', '取消'], + defaultId: 0, + cancelId: 1, + type: 'question', + noLink: true, + }) +} + +export { checkUpdate, simulateUpdateDialog } diff --git a/electron/preload/index.d.ts b/electron/preload/index.d.ts index 9c7fc97..96bf97d 100644 --- a/electron/preload/index.d.ts +++ b/electron/preload/index.d.ts @@ -84,6 +84,11 @@ interface Api { clipboard: { copyImage: (dataUrl: string) => Promise<{ success: boolean; error?: string }> } + app: { + getVersion: () => Promise + checkUpdate: () => void + simulateUpdate: () => void + } } interface MergeApi { diff --git a/electron/preload/index.ts b/electron/preload/index.ts index fd385aa..3d25444 100644 --- a/electron/preload/index.ts +++ b/electron/preload/index.ts @@ -34,6 +34,7 @@ const api = { const validChannels = [ 'show-message', 'check-update', + 'simulate-update', 'get-gpu-acceleration', 'set-gpu-acceleration', 'save-gpu-acceleration', @@ -958,7 +959,7 @@ const cacheApi = { }, } -// 扩展 api,添加 dialog 和 clipboard 功能 +// 扩展 api,添加 dialog、clipboard 和应用功能 const extendedApi = { ...api, dialog: { @@ -975,6 +976,26 @@ const extendedApi = { return ipcRenderer.invoke('copyImage', dataUrl) }, }, + app: { + /** + * 获取应用版本号 + */ + getVersion: (): Promise => { + return ipcRenderer.invoke('app:getVersion') + }, + /** + * 检查更新 + */ + checkUpdate: (): void => { + ipcRenderer.send('check-update') + }, + /** + * 模拟更新弹窗(仅开发模式) + */ + simulateUpdate: (): void => { + ipcRenderer.send('simulate-update') + }, + }, } // Use `contextBridge` APIs to expose Electron APIs to diff --git a/src/components/common/SettingModal.vue b/src/components/common/SettingModal.vue index 7dfcece..59c3d6a 100644 --- a/src/components/common/SettingModal.vue +++ b/src/components/common/SettingModal.vue @@ -1,5 +1,5 @@