diff --git a/build.sh b/build.sh index a9d7293..4aeec71 100755 --- a/build.sh +++ b/build.sh @@ -2,8 +2,8 @@ ### # @Date: 2025-04-25 13:45:35 # @LastEditors: 陈子健 - # @LastEditTime: 2025-05-26 17:14:22 - # @FilePath: /mac-lyric-vue/build.sh + # @LastEditTime: 2025-06-25 14:01:43 + # @FilePath: /lyroc/build.sh ### # lyroc 打包脚本 @@ -25,7 +25,7 @@ command -v python3 >/dev/null 2>&1 || { echo -e "${RED}错误: python3 未安装 # 项目根目录 ROOT_DIR="$(pwd)" FRONTEND_DIR="$ROOT_DIR/frontend" -BACKEND_DIR="$ROOT_DIR/backend" +BACKEND_DIR="$ROOT_DIR/backend_local" ELECTRON_DIR="$ROOT_DIR/electron-app" # 1. 构建前端 diff --git a/electron-app/loading.html b/electron-app/loading.html index 3a84bf4..6d44d1b 100644 --- a/electron-app/loading.html +++ b/electron-app/loading.html @@ -169,7 +169,7 @@ // 设置语言 function setLanguage(lang) { - const t = translations[lang] || translations.en; + const t = translations[lang] || translations.zh; loadingTitle.textContent = t.title; progressLabel.textContent = t.progress; if (currentProgress === 0) { diff --git a/electron-app/modules/window-manager.js b/electron-app/modules/window-manager.js index 1d44410..9c4a07f 100644 --- a/electron-app/modules/window-manager.js +++ b/electron-app/modules/window-manager.js @@ -2,16 +2,19 @@ * 窗口管理模块 * 负责创建、配置和管理主窗口 */ -const { BrowserWindow, screen, Menu, ipcMain, shell } = require('electron'); -const path = require('path'); -const fs = require('fs'); -const i18next = require('./i18n'); +const { BrowserWindow, screen, Menu, ipcMain, shell } = require("electron"); +const path = require("path"); +const fs = require("fs"); +const i18next = require("./i18n"); // 全局变量 let mainWindow = null; let unlockWindow = null; // 解锁窗口 let contextMenu = null; // 存储右键菜单实例 +// 设置默认语言 +i18next.changeLanguage("zh"); + // 窗口配置文件路径 let configPath = null; @@ -20,7 +23,7 @@ let configPath = null; * @param {string} userDataPath 用户数据目录路径 */ function initConfig(userDataPath) { - configPath = path.join(userDataPath, 'window-config.json'); + configPath = path.join(userDataPath, "window-config.json"); } /** @@ -30,20 +33,20 @@ function initConfig(userDataPath) { function loadWindowConfig() { try { if (fs.existsSync(configPath)) { - const data = fs.readFileSync(configPath, 'utf8'); + const data = fs.readFileSync(configPath, "utf8"); const config = JSON.parse(data); - console.log('加载窗口配置成功:', config); - + console.log("加载窗口配置成功:", config); + // 如果配置中有语言设置,更新i18next if (config.language) { - console.log('更新i18next语言:', config.language); + console.log("更新i18next语言:", config.language); i18next.changeLanguage(config.language); } - + return config; } } catch (error) { - console.error('加载窗口配置失败:', error); + console.error("加载窗口配置失败:", error); } return null; } @@ -57,12 +60,12 @@ function saveWindowConfig(bounds) { // 合并窗口位置和语言设置 const config = { ...bounds, - language: i18next.language + language: i18next.language, }; fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); - console.log('保存窗口配置成功:', config); + console.log("保存窗口配置成功:", config); } catch (error) { - console.error('保存窗口配置失败:', error); + console.error("保存窗口配置失败:", error); } } @@ -77,8 +80,8 @@ function fixStaticAssetsPaths(htmlContent) { .replace(/src="\/assets\//g, 'src="./assets/') .replace(/href="\/assets\//g, 'href="./assets/') .replace(/href="\/favicon.svg"/g, 'href="./favicon.svg"'); - - console.log('已修复资源路径引用'); + + console.log("已修复资源路径引用"); return fixedContent; } /** @@ -90,17 +93,17 @@ function fixStaticAssetsPaths(htmlContent) { * @returns {BrowserWindow} 创建的主窗口 */ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { - console.log('开始创建主窗口...'); + console.log("开始创建主窗口..."); // 加载窗口配置 const config = loadWindowConfig(); - console.log('加载窗口配置成功:', config); - + console.log("加载窗口配置成功:", config); + // 提取窗口尺寸和位置 let windowWidth = 500; let windowHeight = 230; let x, y; - + let searchWindow = null; if (config) { windowWidth = config.width || windowWidth; @@ -108,27 +111,27 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { x = config.x; y = config.y; } - + // 检查坐标是否有效 (针对多显示器情况) const displays = screen.getAllDisplays(); console.log(`系统有 ${displays.length} 个显示器`); - + let foundValidDisplay = false; if (x !== undefined && y !== undefined) { // 检查坐标是否在任一显示器范围内 for (const display of displays) { const { bounds } = display; if ( - x >= bounds.x - windowWidth && + x >= bounds.x - windowWidth && x <= bounds.x + bounds.width && - y >= bounds.y - windowHeight && + y >= bounds.y - windowHeight && y <= bounds.y + bounds.height ) { foundValidDisplay = true; break; } } - + // 如果坐标无效,使用主显示器中央 if (!foundValidDisplay) { const primaryDisplay = screen.getPrimaryDisplay(); @@ -141,9 +144,13 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { x = Math.floor((primaryDisplay.workAreaSize.width - windowWidth) / 2); y = Math.floor((primaryDisplay.workAreaSize.height - windowHeight) / 2); } - - console.log(`设置窗口位置: x=${x}, y=${y}, 屏幕尺寸: ${screen.getPrimaryDisplay().workAreaSize.width}x${screen.getPrimaryDisplay().workAreaSize.height}`); - + + console.log( + `设置窗口位置: x=${x}, y=${y}, 屏幕尺寸: ${ + screen.getPrimaryDisplay().workAreaSize.width + }x${screen.getPrimaryDisplay().workAreaSize.height}` + ); + // 创建浏览器窗口 mainWindow = new BrowserWindow({ width: windowWidth, @@ -152,26 +159,30 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { y: y, frame: false, transparent: true, - backgroundColor: '#00000000', + backgroundColor: "#00000000", hasShadow: false, // 禁用窗口阴影以防止透明边缘出现灰色 - titleBarStyle: 'hidden', + titleBarStyle: "hidden", alwaysOnTop: true, show: false, // 初始不显示,等待加载完成后显示 webPreferences: { nodeIntegration: false, contextIsolation: true, - preload: path.join(__dirname, '..', 'preload.js'), - } + preload: path.join(__dirname, "..", "preload.js"), + }, }); // 确保窗口在屏幕上可见,设置居中位置 mainWindow.setPosition(x, y); - console.log(`设置窗口位置: x=${x}, y=${y}, 屏幕尺寸: ${screen.getPrimaryDisplay().workAreaSize.width}x${screen.getPrimaryDisplay().workAreaSize.height}`); - - console.log('窗口创建成功,现在设置窗口事件...'); - + console.log( + `设置窗口位置: x=${x}, y=${y}, 屏幕尺寸: ${ + screen.getPrimaryDisplay().workAreaSize.width + }x${screen.getPrimaryDisplay().workAreaSize.height}` + ); + + console.log("窗口创建成功,现在设置窗口事件..."); + // 确保窗口及其内容完全透明 - mainWindow.webContents.on('did-finish-load', () => { + mainWindow.webContents.on("did-finish-load", () => { // 设置背景色为透明 mainWindow.webContents.insertCSS(` html, body { @@ -180,64 +191,66 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { } `); }); - + // 设置窗口永远在最上层,使用screen-saver级别确保在所有空间可见 - mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); - + mainWindow.setAlwaysOnTop(true, "screen-saver", 1); + // 在macOS上隐藏流量灯按钮 - if (process.platform === 'darwin') { + if (process.platform === "darwin") { // 完全隐藏所有默认窗口控件 mainWindow.setWindowButtonVisibility(false); mainWindow.setMovable(true); // macOS 窗口设置 try { // 部分 Electron 版本可能不支持这个 API - if (typeof mainWindow.setConstraints === 'function') { + if (typeof mainWindow.setConstraints === "function") { mainWindow.setConstraints({ minWidth: null, minHeight: null }); } } catch (e) { - console.log('无法设置窗口约束,使用默认设置'); + console.log("无法设置窗口约束,使用默认设置"); } } - + // 加载前端页面 if (isDev) { // 开发模式: 加载本地开发服务器 frontendUrl = `http://localhost:${frontendPort}/`; // 检查开发服务器是否在运行 - const http = require('http'); + const http = require("http"); const checkDevServer = () => { - http.get(frontendUrl, (res) => { - if (res.statusCode === 200) { - mainWindow.loadURL(frontendUrl); - } else { + http + .get(frontendUrl, (res) => { + if (res.statusCode === 200) { + mainWindow.loadURL(frontendUrl); + } else { + loadBuiltFiles(mainWindow); + } + }) + .on("error", () => { loadBuiltFiles(mainWindow); - } - }).on('error', () => { - loadBuiltFiles(mainWindow); - }); + }); }; - + // 尝试连接开发服务器 checkDevServer(); } else { loadBuiltFiles(mainWindow); } - + // 设置后端URL global.backendUrl = `http://127.0.0.1:${backendPort}`; - + // 创建右键菜单 contextMenu = Menu.buildFromTemplate(getMenuTemplate()); - + // 添加右键菜单事件监听 - mainWindow.webContents.on('context-menu', (e, params) => { + mainWindow.webContents.on("context-menu", (e, params) => { e.preventDefault(); contextMenu.popup({ window: mainWindow }); }); - + // 阻止窗口导航到外部链接 - mainWindow.webContents.on('will-navigate', (event, url) => { + mainWindow.webContents.on("will-navigate", (event, url) => { if (!url.startsWith(frontendUrl)) { event.preventDefault(); shell.openExternal(url); @@ -245,8 +258,8 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { }); // 创建关闭按钮window - createUnlockWindow() - + createUnlockWindow(); + return mainWindow; /** @@ -256,67 +269,75 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { function getMenuTemplate() { return [ { - label: i18next.t('menu.lockWindow'), - click: () => { - console.log('锁定窗口 - 启用点击穿透'); + label: i18next.t("menu.lockWindow"), + click: () => { + console.log("锁定窗口 - 启用点击穿透"); if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.blur() - mainWindow.webContents.send('toggle-lock-window', true); - showUnlockWindow() + mainWindow.blur(); + mainWindow.webContents.send("toggle-lock-window", true); + showUnlockWindow(); } - } + }, }, - { type: 'separator' }, - { - label: i18next.t('menu.refresh'), - click: () => { mainWindow.reload(); } + { type: "separator" }, + { + label: i18next.t("menu.refresh"), + click: () => { + mainWindow.reload(); + }, }, - { - label: i18next.t('menu.openDevTools'), - click: () => { mainWindow.webContents.openDevTools(); } + { + label: i18next.t("menu.openDevTools"), + click: () => { + mainWindow.webContents.openDevTools(); + }, }, - { - label: i18next.t('menu.language'), + { + label: i18next.t("menu.language"), submenu: [ { - label: i18next.t('menu.chinese'), + label: i18next.t("menu.chinese"), click: () => { - i18next.changeLanguage('zh'); - mainWindow.webContents.send('change-language', 'zh'); - searchWindow && searchWindow.webContents.send('change-language', 'zh'); + i18next.changeLanguage("zh"); + mainWindow.webContents.send("change-language", "zh"); + searchWindow && + searchWindow.webContents.send("change-language", "zh"); updateContextMenu(); - } + }, }, { - label: i18next.t('menu.english'), + label: i18next.t("menu.english"), click: () => { - i18next.changeLanguage('en'); - mainWindow.webContents.send('change-language', 'en'); - searchWindow && searchWindow.webContents.send('change-language', 'en'); + i18next.changeLanguage("en"); + mainWindow.webContents.send("change-language", "en"); + searchWindow && + searchWindow.webContents.send("change-language", "en"); updateContextMenu(); - } - } - ] + }, + }, + ], }, - { - label: i18next.t('menu.deleteLyrics'), + { + label: i18next.t("menu.deleteLyrics"), click: () => { - mainWindow.webContents.send('delete-current-lyrics'); - } + mainWindow.webContents.send("delete-current-lyrics"); + }, }, - { - label: i18next.t('menu.searchLyrics'), + { + label: i18next.t("menu.searchLyrics"), click: () => { - console.log('右键菜单:点击搜索歌词'); - const searchWindowManager = require('../windows/search-window'); + console.log("右键菜单:点击搜索歌词"); + const searchWindowManager = require("../windows/search-window"); searchWindow = searchWindowManager.createWindow(loadBuiltFiles); - } + }, + }, + { type: "separator" }, + { + label: i18next.t("menu.quit"), + click: () => { + require("electron").app.quit(); + }, }, - { type: 'separator' }, - { - label: i18next.t('menu.quit'), - click: () => { require('electron').app.quit(); } - } ]; } @@ -328,48 +349,48 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { contextMenu = Menu.buildFromTemplate(getMenuTemplate()); } } - + // 加载构建好的前端文件 function loadBuiltFiles(window, search) { // 修正前端资源路径,匹配package.json中的配置 - const frontendDistPath = isDev - ? path.join(__dirname, '..', '..', 'frontend', 'dist') - : path.join(process.resourcesPath, 'app'); - + const frontendDistPath = isDev + ? path.join(__dirname, "..", "..", "frontend", "dist") + : path.join(process.resourcesPath, "app"); + console.log(`加载已构建的前端文件: ${frontendDistPath}`); - + try { // 检查路径是否存在 if (!fs.existsSync(frontendDistPath)) { console.error(`前端资源路径不存在: ${frontendDistPath}`); // 尝试列出可能的路径 if (!isDev) { - console.log('资源路径内容:'); + console.log("资源路径内容:"); console.log(fs.readdirSync(process.resourcesPath)); } } - + // 读取index.html文件内容 - const indexPath = path.join(frontendDistPath, 'index.html'); + const indexPath = path.join(frontendDistPath, "index.html"); if (!fs.existsSync(indexPath)) { console.error(`HTML文件不存在: ${indexPath}`); return; } - - let htmlContent = fs.readFileSync(indexPath, 'utf8'); - + + let htmlContent = fs.readFileSync(indexPath, "utf8"); + // 修复资源路径 htmlContent = fixStaticAssetsPaths(htmlContent); - + // 将修复后的HTML内容写入临时文件 - const tempIndexPath = path.join(frontendDistPath, '_fixed_index.html'); - fs.writeFileSync(tempIndexPath, htmlContent, 'utf8'); + const tempIndexPath = path.join(frontendDistPath, "_fixed_index.html"); + fs.writeFileSync(tempIndexPath, htmlContent, "utf8"); console.log(`已创建临时HTML文件: ${tempIndexPath}`); - + // 加载修复后的HTML文件 window.loadFile(tempIndexPath, { search }); } catch (err) { - console.error('加载前端文件失败:', err); + console.error("加载前端文件失败:", err); } } } @@ -381,7 +402,7 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { function createUnlockWindow() { // 获取主窗口的位置和尺寸 let x, y; - + if (mainWindow && !mainWindow.isDestroyed()) { const mainBounds = mainWindow.getBounds(); // 将解锁按钮定位在歌词窗口上方 @@ -393,7 +414,7 @@ function createUnlockWindow() { x = Math.floor(width / 2 - 24); y = Math.floor(height / 2 - 24); } - + // 创建解锁窗口 unlockWindow = new BrowserWindow({ width: 48, @@ -405,29 +426,29 @@ function createUnlockWindow() { resizable: false, transparent: true, alwaysOnTop: true, - backgroundColor: '#00000000', + backgroundColor: "#00000000", webPreferences: { nodeIntegration: true, - contextIsolation: false - } + contextIsolation: false, + }, }); - + // 加载HTML文件 - unlockWindow.loadFile(path.join(__dirname, '..', 'unlock.html')); - + unlockWindow.loadFile(path.join(__dirname, "..", "unlock.html")); + // 禁用右键菜单 - unlockWindow.webContents.on('context-menu', e => { + unlockWindow.webContents.on("context-menu", (e) => { e.preventDefault(); }); - + // 设置窗口永远在最上层,使用screen-saver级别确保在所有空间可见 - unlockWindow.setAlwaysOnTop(true, 'screen-saver', 1); - + unlockWindow.setAlwaysOnTop(true, "screen-saver", 1); + // 在macOS上设置可见于所有工作区 - if (process.platform === 'darwin') { + if (process.platform === "darwin") { unlockWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); } - + return unlockWindow; } @@ -439,17 +460,17 @@ function showUnlockWindow() { const x = mainBounds.x + mainBounds.width / 2 - 24; const y = mainBounds.y + 24; unlockWindow.setPosition(x, y); - - unlockWindow.showInactive() + + unlockWindow.showInactive(); mainWindow.setIgnoreMouseEvents(true); - } + } } } function hideUnlockWindow() { if (unlockWindow) { - unlockWindow.hide() + unlockWindow.hide(); } } @@ -460,7 +481,7 @@ function hideUnlockWindow() { function createLoadingWindow() { // 获取主显示器尺寸 const { width, height } = screen.getPrimaryDisplay().workAreaSize; - + // 创建加载窗口 loadingWindow = new BrowserWindow({ width: 320, @@ -473,28 +494,28 @@ function createLoadingWindow() { alwaysOnTop: true, webPreferences: { nodeIntegration: true, - contextIsolation: false - } + contextIsolation: false, + }, }); - + // 加载HTML文件 - loadingWindow.loadFile(path.join(__dirname, '..', 'loading.html')); - + loadingWindow.loadFile(path.join(__dirname, "..", "loading.html")); + // 禁用右键菜单 - loadingWindow.webContents.on('context-menu', e => { + loadingWindow.webContents.on("context-menu", (e) => { e.preventDefault(); }); // 窗口准备好时发送当前语言设置 - loadingWindow.webContents.on('did-finish-load', () => { + loadingWindow.webContents.on("did-finish-load", () => { // 先尝试从配置中读取语言设置 const config = loadWindowConfig(); - console.log('加载窗口配置:', config); + console.log("加载窗口配置:", config); const currentLang = config?.language || i18next.language; - console.log('发送当前语言到加载窗口:', currentLang); - loadingWindow.webContents.send('change-language', currentLang); + console.log("发送当前语言到加载窗口:", currentLang); + loadingWindow.webContents.send("change-language", currentLang); }); - + return loadingWindow; } @@ -508,35 +529,35 @@ function updateLoadingProgress(progress, message) { // 获取当前语言的消息 const messages = { en: { - initializing: 'Initializing application...', - loadingBackend: 'Loading backend service...', - allocatingPorts: 'Allocating ports...', - loadingFrontend: 'Loading frontend resources...', - connecting: 'Connecting to Apple Music...', - ready: 'Ready', - loadingLyrics: 'Loading lyrics...' + initializing: "Initializing application...", + loadingBackend: "Loading backend service...", + allocatingPorts: "Allocating ports...", + loadingFrontend: "Loading frontend resources...", + connecting: "Connecting to Apple Music...", + ready: "Ready", + loadingLyrics: "Loading lyrics...", }, zh: { - initializing: '正在初始化应用...', - loadingBackend: '正在加载后端服务...', - allocatingPorts: '正在分配端口...', - loadingFrontend: '正在加载前端资源...', - connecting: '正在连接 Apple Music...', - ready: '准备就绪', - loadingLyrics: '正在加载歌词...' - } + initializing: "正在初始化应用...", + loadingBackend: "正在加载后端服务...", + allocatingPorts: "正在分配端口...", + loadingFrontend: "正在加载前端资源...", + connecting: "正在连接 Apple Music...", + ready: "准备就绪", + loadingLyrics: "正在加载歌词...", + }, }; // 获取当前语言 const currentLang = i18next.language; - const t = messages[currentLang] || messages.en; + const t = messages[currentLang] || messages.zh; // 如果消息是预定义的键,则使用翻译 const translatedMessage = t[message] || message; - loadingWindow.webContents.send('update-progress', { + loadingWindow.webContents.send("update-progress", { progress, - message: translatedMessage + message: translatedMessage, }); } } @@ -547,11 +568,11 @@ function updateLoadingProgress(progress, message) { function closeLoadingWindow() { if (loadingWindow && !loadingWindow.isDestroyed()) { // 设置最终进度为100% - updateLoadingProgress(100, 'ready'); - + updateLoadingProgress(100, "ready"); + // 获取主窗口和加载窗口的位置和大小 const loadingBounds = loadingWindow.getBounds(); - + // 确保主窗口已创建 if (!mainWindow) { // 如果主窗口不存在,直接关闭加载窗口 @@ -559,73 +580,81 @@ function closeLoadingWindow() { loadingWindow = null; return; } - + const mainBounds = mainWindow.getBounds(); - + // 动画的总帧数和当前帧 const totalFrames = 40; // 动画时长约为40帧,大约600-800ms let currentFrame = 0; - + // 窗口属性的起始和结束值 const startProps = { x: loadingBounds.x, y: loadingBounds.y, width: loadingBounds.width, height: loadingBounds.height, - opacity: 1 + opacity: 1, }; - + const endProps = { x: mainBounds.x, y: mainBounds.y, width: mainBounds.width, height: mainBounds.height, - opacity: 0.3 + opacity: 0.3, }; - + // 创建动画计时器 const animationTimer = setInterval(() => { currentFrame++; - + // 使用缓动函数计算当前帧的属性值 const progress = easeOutCubic(currentFrame / totalFrames); - + // 计算当前帧的窗口位置和大小 const newBounds = { x: Math.round(startProps.x + (endProps.x - startProps.x) * progress), y: Math.round(startProps.y + (endProps.y - startProps.y) * progress), - width: Math.round(startProps.width + (endProps.width - startProps.width) * progress), - height: Math.round(startProps.height + (endProps.height - startProps.height) * progress) + width: Math.round( + startProps.width + (endProps.width - startProps.width) * progress + ), + height: Math.round( + startProps.height + (endProps.height - startProps.height) * progress + ), }; - + // 计算当前帧的不透明度 - const newOpacity = startProps.opacity + (endProps.opacity - startProps.opacity) * progress; - + const newOpacity = + startProps.opacity + (endProps.opacity - startProps.opacity) * progress; + // 应用新的边界和不透明度 loadingWindow.setBounds(newBounds); loadingWindow.setOpacity(newOpacity); - + // 动画完成后执行淡出并显示主窗口 if (currentFrame >= totalFrames) { clearInterval(animationTimer); - + // 先显示主窗口,让它可以在背景中显示 if (mainWindow && !mainWindow.isVisible()) { mainWindow.show(); } - + // 执行额外的淡出动画 let fadeFrame = 0; const fadeTotalFrames = 20; // 约300ms的淡出时间 - + const fadeTimer = setInterval(() => { fadeFrame++; // 使用easeInCubic让淡出渐渐加速 const fadeProgress = fadeFrame / fadeTotalFrames; - const fadeOpacity = Math.max(0, endProps.opacity * (1 - fadeProgress)); - + const fadeOpacity = Math.max( + 0, + endProps.opacity * (1 - fadeProgress) + ); + loadingWindow.setOpacity(fadeOpacity); - + if (fadeFrame >= fadeTotalFrames) { clearInterval(fadeTimer); loadingWindow.close(); @@ -634,7 +663,7 @@ function closeLoadingWindow() { }, 15); } }, 16); // 约60fps的刷新率 - + // 缓动函数,使动画更自然 function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); @@ -647,20 +676,20 @@ function closeLoadingWindow() { */ function setupWindowEvents() { if (!mainWindow) { - console.error('无法设置窗口事件:窗口未创建'); + console.error("无法设置窗口事件:窗口未创建"); return; } - + // 使用轮询方法检测鼠标是否在窗口上 let isMouseOverWindow = false; let pollInterval = null; - + // 开始轮询检测鼠标位置 const startMouseDetection = () => { if (pollInterval) { clearInterval(pollInterval); } - + pollInterval = setInterval(() => { if (!mainWindow) { // 如果窗口已关闭,停止轮询 @@ -670,63 +699,65 @@ function setupWindowEvents() { } return; } - + try { // 获取鼠标位置 const mousePos = screen.getCursorScreenPoint(); // 获取窗口位置和大小 const bounds = mainWindow.getBounds(); - + // 检查鼠标是否在窗口内 - const isInWindow = ( - mousePos.x >= bounds.x && - mousePos.x <= bounds.x + bounds.width && - mousePos.y >= bounds.y && - mousePos.y <= bounds.y + bounds.height - ); - + const isInWindow = + mousePos.x >= bounds.x && + mousePos.x <= bounds.x + bounds.width && + mousePos.y >= bounds.y && + mousePos.y <= bounds.y + bounds.height; + // 状态变化时才发送事件和更新点击穿透 if (isInWindow !== isMouseOverWindow) { isMouseOverWindow = isInWindow; - + // 通知渲染进程 - 无论是开发还是生产模式都需要 if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send('mouse-hover-change', isMouseOverWindow); + mainWindow.webContents.send( + "mouse-hover-change", + isMouseOverWindow + ); } } } catch (err) { - console.error('鼠标检测错误:', err); + console.error("鼠标检测错误:", err); } }, 100); // 每100毫秒检测一次,可根据需要调整 }; - + // 创建窗口后立即开始检测 startMouseDetection(); - + // 监听窗口位置变化事件,保存新位置 - mainWindow.on('moved', function() { + mainWindow.on("moved", function () { if (!mainWindow) return; const bounds = mainWindow.getBounds(); saveWindowConfig(bounds); }); - + // 窗口关闭前保存位置 - mainWindow.on('close', function() { + mainWindow.on("close", function () { if (!mainWindow) return; const bounds = mainWindow.getBounds(); saveWindowConfig(bounds); }); - + // 窗口关闭时清除轮询 - mainWindow.on('closed', () => { + mainWindow.on("closed", () => { if (pollInterval) { clearInterval(pollInterval); pollInterval = null; } }); - + // 确保窗口在所有空间都可见(macOS特有) - if (process.platform === 'darwin') { + if (process.platform === "darwin") { // 设置窗口可见于所有工作区 mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); } @@ -739,60 +770,61 @@ function setupWindowEvents() { */ function setupIPC(backendPort, frontendPort) { // IPC 处理函数,提供详细输出便于调试 - ipcMain.handle('get-backend-url', () => { + ipcMain.handle("get-backend-url", () => { const url = `http://127.0.0.1:${backendPort}`; console.log(`向前端提供后端URL: ${url}`); return url; }); // 处理IPC事件:获取前端URL - ipcMain.handle('get-frontend-url', () => { + ipcMain.handle("get-frontend-url", () => { return `http://localhost:${frontendPort}`; }); - + // 监听窗口位置和大小变化 - ipcMain.on('save-window-bounds', (event, bounds) => { + ipcMain.on("save-window-bounds", (event, bounds) => { saveWindowConfig(bounds); }); // 监听最小化窗口请求 - ipcMain.on('minimize-window', () => { + ipcMain.on("minimize-window", () => { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.minimize(); } }); // 监听退出应用请求 - ipcMain.on('quit-app', () => { + ipcMain.on("quit-app", () => { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.close(); } }); // 监听获取端口请求 - ipcMain.handle('get-ports', async () => { + ipcMain.handle("get-ports", async () => { return { backendPort, - frontendPort + frontendPort, }; }); - + // 监听加载窗口完成事件 - ipcMain.on('loading-finished', () => { + ipcMain.on("loading-finished", () => { closeLoadingWindow(); }); - + // 处理窗口拖动 - 使用系统原生拖动 - ipcMain.on('start-window-drag', () => { - if (process.platform === 'darwin') { + ipcMain.on("start-window-drag", () => { + if (process.platform === "darwin") { // 使用BrowserWindow.startWindowDrag() API (Electron 14+) try { - console.log('使用Electron原生窗口拖动API'); - mainWindow.webContents.executeJavaScript('document.documentElement.style.cursor = "grab";') - .catch(err => console.error('设置光标样式失败:', err)); - + console.log("使用Electron原生窗口拖动API"); + mainWindow.webContents + .executeJavaScript('document.documentElement.style.cursor = "grab";') + .catch((err) => console.error("设置光标样式失败:", err)); + // 使用较新的原生API - if (typeof mainWindow.startWindowDrag === 'function') { + if (typeof mainWindow.startWindowDrag === "function") { mainWindow.startWindowDrag(); } else { // 回退到老方法 @@ -800,15 +832,15 @@ function setupIPC(backendPort, frontendPort) { mainWindow.moveTop(); } } catch (error) { - console.error('窗口拖动错误:', error); + console.error("窗口拖动错误:", error); } } }); - + // 处理窗口锁定状态设置 - ipcMain.on('set-window-lock-state', (_, locked) => { - console.log('设置窗口锁定状态:', locked); - + ipcMain.on("set-window-lock-state", (_, locked) => { + console.log("设置窗口锁定状态:", locked); + // 确保锁定状态下窗口始终忽略鼠标事件 if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.setIgnoreMouseEvents(locked); @@ -816,40 +848,40 @@ function setupIPC(backendPort, frontendPort) { }); // 处理锁定时close window的渲染 - ipcMain.on('set-close-window-state', (_, showLock) => { - console.log('设置关闭按钮', showLock); + ipcMain.on("set-close-window-state", (_, showLock) => { + console.log("设置关闭按钮", showLock); if (showLock) { // 展示解锁窗口 showUnlockWindow(); } else { // 关闭解锁窗口 - hideUnlockWindow() + hideUnlockWindow(); } - }) - + }); + // 监听解锁窗口的解锁事件 - ipcMain.on('unlock-window', () => { - console.log('收到解锁窗口事件'); - + ipcMain.on("unlock-window", () => { + console.log("收到解锁窗口事件"); + // 通知主窗口解锁 if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.setIgnoreMouseEvents(false); - mainWindow.webContents.send('toggle-lock-window', false); + mainWindow.webContents.send("toggle-lock-window", false); } - + // 关闭解锁窗口 - hideUnlockWindow() + hideUnlockWindow(); }); // 处理语言切换 - ipcMain.on('change-language', (_, lang) => { - console.log('收到语言切换请求:', lang); + ipcMain.on("change-language", (_, lang) => { + console.log("收到语言切换请求:", lang); i18next.changeLanguage(lang); // 通知所有窗口语言已更改 - BrowserWindow.getAllWindows().forEach(window => { + BrowserWindow.getAllWindows().forEach((window) => { if (!window.isDestroyed()) { - window.webContents.send('change-language', lang); + window.webContents.send("change-language", lang); } }); // 更新右键菜单 @@ -871,5 +903,5 @@ module.exports = { setupWindowEvents, setupIPC, mainWindow: () => mainWindow, - saveWindowConfig + saveWindowConfig, }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 40aa8be..a493f54 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "0.7.0", + "version": "0.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "0.7.0", + "version": "0.7.1", "dependencies": { "axios": "^1.7.7", "pinia": "^2.2.6",