chore: update build script and language settings in window manager

This commit is contained in:
ethan.chen
2025-06-25 14:11:38 +08:00
parent 63eda91fd6
commit ba3435e1a0
4 changed files with 296 additions and 264 deletions

View File

@@ -2,8 +2,8 @@
### ###
# @Date: 2025-04-25 13:45:35 # @Date: 2025-04-25 13:45:35
# @LastEditors: 陈子健 # @LastEditors: 陈子健
# @LastEditTime: 2025-05-26 17:14:22 # @LastEditTime: 2025-06-25 14:01:43
# @FilePath: /mac-lyric-vue/build.sh # @FilePath: /lyroc/build.sh
### ###
# lyroc 打包脚本 # lyroc 打包脚本
@@ -25,7 +25,7 @@ command -v python3 >/dev/null 2>&1 || { echo -e "${RED}错误: python3 未安装
# 项目根目录 # 项目根目录
ROOT_DIR="$(pwd)" ROOT_DIR="$(pwd)"
FRONTEND_DIR="$ROOT_DIR/frontend" FRONTEND_DIR="$ROOT_DIR/frontend"
BACKEND_DIR="$ROOT_DIR/backend" BACKEND_DIR="$ROOT_DIR/backend_local"
ELECTRON_DIR="$ROOT_DIR/electron-app" ELECTRON_DIR="$ROOT_DIR/electron-app"
# 1. 构建前端 # 1. 构建前端

View File

@@ -169,7 +169,7 @@
// 设置语言 // 设置语言
function setLanguage(lang) { function setLanguage(lang) {
const t = translations[lang] || translations.en; const t = translations[lang] || translations.zh;
loadingTitle.textContent = t.title; loadingTitle.textContent = t.title;
progressLabel.textContent = t.progress; progressLabel.textContent = t.progress;
if (currentProgress === 0) { if (currentProgress === 0) {

View File

@@ -2,16 +2,19 @@
* 窗口管理模块 * 窗口管理模块
* 负责创建、配置和管理主窗口 * 负责创建、配置和管理主窗口
*/ */
const { BrowserWindow, screen, Menu, ipcMain, shell } = require('electron'); const { BrowserWindow, screen, Menu, ipcMain, shell } = require("electron");
const path = require('path'); const path = require("path");
const fs = require('fs'); const fs = require("fs");
const i18next = require('./i18n'); const i18next = require("./i18n");
// 全局变量 // 全局变量
let mainWindow = null; let mainWindow = null;
let unlockWindow = null; // 解锁窗口 let unlockWindow = null; // 解锁窗口
let contextMenu = null; // 存储右键菜单实例 let contextMenu = null; // 存储右键菜单实例
// 设置默认语言
i18next.changeLanguage("zh");
// 窗口配置文件路径 // 窗口配置文件路径
let configPath = null; let configPath = null;
@@ -20,7 +23,7 @@ let configPath = null;
* @param {string} userDataPath 用户数据目录路径 * @param {string} userDataPath 用户数据目录路径
*/ */
function initConfig(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() { function loadWindowConfig() {
try { try {
if (fs.existsSync(configPath)) { if (fs.existsSync(configPath)) {
const data = fs.readFileSync(configPath, 'utf8'); const data = fs.readFileSync(configPath, "utf8");
const config = JSON.parse(data); const config = JSON.parse(data);
console.log('加载窗口配置成功:', config); console.log("加载窗口配置成功:", config);
// 如果配置中有语言设置更新i18next // 如果配置中有语言设置更新i18next
if (config.language) { if (config.language) {
console.log('更新i18next语言:', config.language); console.log("更新i18next语言:", config.language);
i18next.changeLanguage(config.language); i18next.changeLanguage(config.language);
} }
return config; return config;
} }
} catch (error) { } catch (error) {
console.error('加载窗口配置失败:', error); console.error("加载窗口配置失败:", error);
} }
return null; return null;
} }
@@ -57,12 +60,12 @@ function saveWindowConfig(bounds) {
// 合并窗口位置和语言设置 // 合并窗口位置和语言设置
const config = { const config = {
...bounds, ...bounds,
language: i18next.language language: i18next.language,
}; };
fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
console.log('保存窗口配置成功:', config); console.log("保存窗口配置成功:", config);
} catch (error) { } catch (error) {
console.error('保存窗口配置失败:', error); console.error("保存窗口配置失败:", error);
} }
} }
@@ -78,7 +81,7 @@ function fixStaticAssetsPaths(htmlContent) {
.replace(/href="\/assets\//g, 'href="./assets/') .replace(/href="\/assets\//g, 'href="./assets/')
.replace(/href="\/favicon.svg"/g, 'href="./favicon.svg"'); .replace(/href="\/favicon.svg"/g, 'href="./favicon.svg"');
console.log('已修复资源路径引用'); console.log("已修复资源路径引用");
return fixedContent; return fixedContent;
} }
/** /**
@@ -90,11 +93,11 @@ function fixStaticAssetsPaths(htmlContent) {
* @returns {BrowserWindow} 创建的主窗口 * @returns {BrowserWindow} 创建的主窗口
*/ */
function createWindow(frontendUrl, frontendPort, backendPort, isDev) { function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
console.log('开始创建主窗口...'); console.log("开始创建主窗口...");
// 加载窗口配置 // 加载窗口配置
const config = loadWindowConfig(); const config = loadWindowConfig();
console.log('加载窗口配置成功:', config); console.log("加载窗口配置成功:", config);
// 提取窗口尺寸和位置 // 提取窗口尺寸和位置
let windowWidth = 500; let windowWidth = 500;
@@ -142,7 +145,11 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
y = Math.floor((primaryDisplay.workAreaSize.height - windowHeight) / 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({ mainWindow = new BrowserWindow({
@@ -152,26 +159,30 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
y: y, y: y,
frame: false, frame: false,
transparent: true, transparent: true,
backgroundColor: '#00000000', backgroundColor: "#00000000",
hasShadow: false, // 禁用窗口阴影以防止透明边缘出现灰色 hasShadow: false, // 禁用窗口阴影以防止透明边缘出现灰色
titleBarStyle: 'hidden', titleBarStyle: "hidden",
alwaysOnTop: true, alwaysOnTop: true,
show: false, // 初始不显示,等待加载完成后显示 show: false, // 初始不显示,等待加载完成后显示
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
preload: path.join(__dirname, '..', 'preload.js'), preload: path.join(__dirname, "..", "preload.js"),
} },
}); });
// 确保窗口在屏幕上可见,设置居中位置 // 确保窗口在屏幕上可见,设置居中位置
mainWindow.setPosition(x, y); mainWindow.setPosition(x, y);
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}`
);
console.log('窗口创建成功,现在设置窗口事件...'); console.log("窗口创建成功,现在设置窗口事件...");
// 确保窗口及其内容完全透明 // 确保窗口及其内容完全透明
mainWindow.webContents.on('did-finish-load', () => { mainWindow.webContents.on("did-finish-load", () => {
// 设置背景色为透明 // 设置背景色为透明
mainWindow.webContents.insertCSS(` mainWindow.webContents.insertCSS(`
html, body { html, body {
@@ -182,21 +193,21 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
}); });
// 设置窗口永远在最上层使用screen-saver级别确保在所有空间可见 // 设置窗口永远在最上层使用screen-saver级别确保在所有空间可见
mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); mainWindow.setAlwaysOnTop(true, "screen-saver", 1);
// 在macOS上隐藏流量灯按钮 // 在macOS上隐藏流量灯按钮
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// 完全隐藏所有默认窗口控件 // 完全隐藏所有默认窗口控件
mainWindow.setWindowButtonVisibility(false); mainWindow.setWindowButtonVisibility(false);
mainWindow.setMovable(true); mainWindow.setMovable(true);
// macOS 窗口设置 // macOS 窗口设置
try { try {
// 部分 Electron 版本可能不支持这个 API // 部分 Electron 版本可能不支持这个 API
if (typeof mainWindow.setConstraints === 'function') { if (typeof mainWindow.setConstraints === "function") {
mainWindow.setConstraints({ minWidth: null, minHeight: null }); mainWindow.setConstraints({ minWidth: null, minHeight: null });
} }
} catch (e) { } catch (e) {
console.log('无法设置窗口约束,使用默认设置'); console.log("无法设置窗口约束,使用默认设置");
} }
} }
@@ -205,17 +216,19 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
// 开发模式: 加载本地开发服务器 // 开发模式: 加载本地开发服务器
frontendUrl = `http://localhost:${frontendPort}/`; frontendUrl = `http://localhost:${frontendPort}/`;
// 检查开发服务器是否在运行 // 检查开发服务器是否在运行
const http = require('http'); const http = require("http");
const checkDevServer = () => { const checkDevServer = () => {
http.get(frontendUrl, (res) => { http
if (res.statusCode === 200) { .get(frontendUrl, (res) => {
mainWindow.loadURL(frontendUrl); if (res.statusCode === 200) {
} else { mainWindow.loadURL(frontendUrl);
} else {
loadBuiltFiles(mainWindow);
}
})
.on("error", () => {
loadBuiltFiles(mainWindow); loadBuiltFiles(mainWindow);
} });
}).on('error', () => {
loadBuiltFiles(mainWindow);
});
}; };
// 尝试连接开发服务器 // 尝试连接开发服务器
@@ -231,13 +244,13 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
contextMenu = Menu.buildFromTemplate(getMenuTemplate()); contextMenu = Menu.buildFromTemplate(getMenuTemplate());
// 添加右键菜单事件监听 // 添加右键菜单事件监听
mainWindow.webContents.on('context-menu', (e, params) => { mainWindow.webContents.on("context-menu", (e, params) => {
e.preventDefault(); e.preventDefault();
contextMenu.popup({ window: mainWindow }); contextMenu.popup({ window: mainWindow });
}); });
// 阻止窗口导航到外部链接 // 阻止窗口导航到外部链接
mainWindow.webContents.on('will-navigate', (event, url) => { mainWindow.webContents.on("will-navigate", (event, url) => {
if (!url.startsWith(frontendUrl)) { if (!url.startsWith(frontendUrl)) {
event.preventDefault(); event.preventDefault();
shell.openExternal(url); shell.openExternal(url);
@@ -245,7 +258,7 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
}); });
// 创建关闭按钮window // 创建关闭按钮window
createUnlockWindow() createUnlockWindow();
return mainWindow; return mainWindow;
@@ -256,67 +269,75 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
function getMenuTemplate() { function getMenuTemplate() {
return [ return [
{ {
label: i18next.t('menu.lockWindow'), label: i18next.t("menu.lockWindow"),
click: () => { click: () => {
console.log('锁定窗口 - 启用点击穿透'); console.log("锁定窗口 - 启用点击穿透");
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.blur() mainWindow.blur();
mainWindow.webContents.send('toggle-lock-window', true); mainWindow.webContents.send("toggle-lock-window", true);
showUnlockWindow() showUnlockWindow();
} }
} },
}, },
{ type: 'separator' }, { type: "separator" },
{ {
label: i18next.t('menu.refresh'), label: i18next.t("menu.refresh"),
click: () => { mainWindow.reload(); } click: () => {
mainWindow.reload();
},
}, },
{ {
label: i18next.t('menu.openDevTools'), label: i18next.t("menu.openDevTools"),
click: () => { mainWindow.webContents.openDevTools(); } click: () => {
mainWindow.webContents.openDevTools();
},
}, },
{ {
label: i18next.t('menu.language'), label: i18next.t("menu.language"),
submenu: [ submenu: [
{ {
label: i18next.t('menu.chinese'), label: i18next.t("menu.chinese"),
click: () => { click: () => {
i18next.changeLanguage('zh'); i18next.changeLanguage("zh");
mainWindow.webContents.send('change-language', 'zh'); mainWindow.webContents.send("change-language", "zh");
searchWindow && searchWindow.webContents.send('change-language', 'zh'); searchWindow &&
searchWindow.webContents.send("change-language", "zh");
updateContextMenu(); updateContextMenu();
} },
}, },
{ {
label: i18next.t('menu.english'), label: i18next.t("menu.english"),
click: () => { click: () => {
i18next.changeLanguage('en'); i18next.changeLanguage("en");
mainWindow.webContents.send('change-language', 'en'); mainWindow.webContents.send("change-language", "en");
searchWindow && searchWindow.webContents.send('change-language', 'en'); searchWindow &&
searchWindow.webContents.send("change-language", "en");
updateContextMenu(); updateContextMenu();
} },
} },
] ],
}, },
{ {
label: i18next.t('menu.deleteLyrics'), label: i18next.t("menu.deleteLyrics"),
click: () => { click: () => {
mainWindow.webContents.send('delete-current-lyrics'); mainWindow.webContents.send("delete-current-lyrics");
} },
}, },
{ {
label: i18next.t('menu.searchLyrics'), label: i18next.t("menu.searchLyrics"),
click: () => { click: () => {
console.log('右键菜单:点击搜索歌词'); console.log("右键菜单:点击搜索歌词");
const searchWindowManager = require('../windows/search-window'); const searchWindowManager = require("../windows/search-window");
searchWindow = searchWindowManager.createWindow(loadBuiltFiles); searchWindow = searchWindowManager.createWindow(loadBuiltFiles);
} },
}, },
{ type: 'separator' }, { type: "separator" },
{ {
label: i18next.t('menu.quit'), label: i18next.t("menu.quit"),
click: () => { require('electron').app.quit(); } click: () => {
} require("electron").app.quit();
},
},
]; ];
} }
@@ -333,8 +354,8 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
function loadBuiltFiles(window, search) { function loadBuiltFiles(window, search) {
// 修正前端资源路径匹配package.json中的配置 // 修正前端资源路径匹配package.json中的配置
const frontendDistPath = isDev const frontendDistPath = isDev
? path.join(__dirname, '..', '..', 'frontend', 'dist') ? path.join(__dirname, "..", "..", "frontend", "dist")
: path.join(process.resourcesPath, 'app'); : path.join(process.resourcesPath, "app");
console.log(`加载已构建的前端文件: ${frontendDistPath}`); console.log(`加载已构建的前端文件: ${frontendDistPath}`);
@@ -344,32 +365,32 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) {
console.error(`前端资源路径不存在: ${frontendDistPath}`); console.error(`前端资源路径不存在: ${frontendDistPath}`);
// 尝试列出可能的路径 // 尝试列出可能的路径
if (!isDev) { if (!isDev) {
console.log('资源路径内容:'); console.log("资源路径内容:");
console.log(fs.readdirSync(process.resourcesPath)); console.log(fs.readdirSync(process.resourcesPath));
} }
} }
// 读取index.html文件内容 // 读取index.html文件内容
const indexPath = path.join(frontendDistPath, 'index.html'); const indexPath = path.join(frontendDistPath, "index.html");
if (!fs.existsSync(indexPath)) { if (!fs.existsSync(indexPath)) {
console.error(`HTML文件不存在: ${indexPath}`); console.error(`HTML文件不存在: ${indexPath}`);
return; return;
} }
let htmlContent = fs.readFileSync(indexPath, 'utf8'); let htmlContent = fs.readFileSync(indexPath, "utf8");
// 修复资源路径 // 修复资源路径
htmlContent = fixStaticAssetsPaths(htmlContent); htmlContent = fixStaticAssetsPaths(htmlContent);
// 将修复后的HTML内容写入临时文件 // 将修复后的HTML内容写入临时文件
const tempIndexPath = path.join(frontendDistPath, '_fixed_index.html'); const tempIndexPath = path.join(frontendDistPath, "_fixed_index.html");
fs.writeFileSync(tempIndexPath, htmlContent, 'utf8'); fs.writeFileSync(tempIndexPath, htmlContent, "utf8");
console.log(`已创建临时HTML文件: ${tempIndexPath}`); console.log(`已创建临时HTML文件: ${tempIndexPath}`);
// 加载修复后的HTML文件 // 加载修复后的HTML文件
window.loadFile(tempIndexPath, { search }); window.loadFile(tempIndexPath, { search });
} catch (err) { } catch (err) {
console.error('加载前端文件失败:', err); console.error("加载前端文件失败:", err);
} }
} }
} }
@@ -405,26 +426,26 @@ function createUnlockWindow() {
resizable: false, resizable: false,
transparent: true, transparent: true,
alwaysOnTop: true, alwaysOnTop: true,
backgroundColor: '#00000000', backgroundColor: "#00000000",
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false contextIsolation: false,
} },
}); });
// 加载HTML文件 // 加载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(); e.preventDefault();
}); });
// 设置窗口永远在最上层使用screen-saver级别确保在所有空间可见 // 设置窗口永远在最上层使用screen-saver级别确保在所有空间可见
unlockWindow.setAlwaysOnTop(true, 'screen-saver', 1); unlockWindow.setAlwaysOnTop(true, "screen-saver", 1);
// 在macOS上设置可见于所有工作区 // 在macOS上设置可见于所有工作区
if (process.platform === 'darwin') { if (process.platform === "darwin") {
unlockWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); unlockWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
} }
@@ -440,7 +461,7 @@ function showUnlockWindow() {
const y = mainBounds.y + 24; const y = mainBounds.y + 24;
unlockWindow.setPosition(x, y); unlockWindow.setPosition(x, y);
unlockWindow.showInactive() unlockWindow.showInactive();
mainWindow.setIgnoreMouseEvents(true); mainWindow.setIgnoreMouseEvents(true);
} }
@@ -449,7 +470,7 @@ function showUnlockWindow() {
function hideUnlockWindow() { function hideUnlockWindow() {
if (unlockWindow) { if (unlockWindow) {
unlockWindow.hide() unlockWindow.hide();
} }
} }
@@ -473,26 +494,26 @@ function createLoadingWindow() {
alwaysOnTop: true, alwaysOnTop: true,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false contextIsolation: false,
} },
}); });
// 加载HTML文件 // 加载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(); e.preventDefault();
}); });
// 窗口准备好时发送当前语言设置 // 窗口准备好时发送当前语言设置
loadingWindow.webContents.on('did-finish-load', () => { loadingWindow.webContents.on("did-finish-load", () => {
// 先尝试从配置中读取语言设置 // 先尝试从配置中读取语言设置
const config = loadWindowConfig(); const config = loadWindowConfig();
console.log('加载窗口配置:', config); console.log("加载窗口配置:", config);
const currentLang = config?.language || i18next.language; const currentLang = config?.language || i18next.language;
console.log('发送当前语言到加载窗口:', currentLang); console.log("发送当前语言到加载窗口:", currentLang);
loadingWindow.webContents.send('change-language', currentLang); loadingWindow.webContents.send("change-language", currentLang);
}); });
return loadingWindow; return loadingWindow;
@@ -508,35 +529,35 @@ function updateLoadingProgress(progress, message) {
// 获取当前语言的消息 // 获取当前语言的消息
const messages = { const messages = {
en: { en: {
initializing: 'Initializing application...', initializing: "Initializing application...",
loadingBackend: 'Loading backend service...', loadingBackend: "Loading backend service...",
allocatingPorts: 'Allocating ports...', allocatingPorts: "Allocating ports...",
loadingFrontend: 'Loading frontend resources...', loadingFrontend: "Loading frontend resources...",
connecting: 'Connecting to Apple Music...', connecting: "Connecting to Apple Music...",
ready: 'Ready', ready: "Ready",
loadingLyrics: 'Loading lyrics...' loadingLyrics: "Loading lyrics...",
}, },
zh: { zh: {
initializing: '正在初始化应用...', initializing: "正在初始化应用...",
loadingBackend: '正在加载后端服务...', loadingBackend: "正在加载后端服务...",
allocatingPorts: '正在分配端口...', allocatingPorts: "正在分配端口...",
loadingFrontend: '正在加载前端资源...', loadingFrontend: "正在加载前端资源...",
connecting: '正在连接 Apple Music...', connecting: "正在连接 Apple Music...",
ready: '准备就绪', ready: "准备就绪",
loadingLyrics: '正在加载歌词...' loadingLyrics: "正在加载歌词...",
} },
}; };
// 获取当前语言 // 获取当前语言
const currentLang = i18next.language; const currentLang = i18next.language;
const t = messages[currentLang] || messages.en; const t = messages[currentLang] || messages.zh;
// 如果消息是预定义的键,则使用翻译 // 如果消息是预定义的键,则使用翻译
const translatedMessage = t[message] || message; const translatedMessage = t[message] || message;
loadingWindow.webContents.send('update-progress', { loadingWindow.webContents.send("update-progress", {
progress, progress,
message: translatedMessage message: translatedMessage,
}); });
} }
} }
@@ -547,7 +568,7 @@ function updateLoadingProgress(progress, message) {
function closeLoadingWindow() { function closeLoadingWindow() {
if (loadingWindow && !loadingWindow.isDestroyed()) { if (loadingWindow && !loadingWindow.isDestroyed()) {
// 设置最终进度为100% // 设置最终进度为100%
updateLoadingProgress(100, 'ready'); updateLoadingProgress(100, "ready");
// 获取主窗口和加载窗口的位置和大小 // 获取主窗口和加载窗口的位置和大小
const loadingBounds = loadingWindow.getBounds(); const loadingBounds = loadingWindow.getBounds();
@@ -572,7 +593,7 @@ function closeLoadingWindow() {
y: loadingBounds.y, y: loadingBounds.y,
width: loadingBounds.width, width: loadingBounds.width,
height: loadingBounds.height, height: loadingBounds.height,
opacity: 1 opacity: 1,
}; };
const endProps = { const endProps = {
@@ -580,7 +601,7 @@ function closeLoadingWindow() {
y: mainBounds.y, y: mainBounds.y,
width: mainBounds.width, width: mainBounds.width,
height: mainBounds.height, height: mainBounds.height,
opacity: 0.3 opacity: 0.3,
}; };
// 创建动画计时器 // 创建动画计时器
@@ -594,12 +615,17 @@ function closeLoadingWindow() {
const newBounds = { const newBounds = {
x: Math.round(startProps.x + (endProps.x - startProps.x) * progress), x: Math.round(startProps.x + (endProps.x - startProps.x) * progress),
y: Math.round(startProps.y + (endProps.y - startProps.y) * progress), y: Math.round(startProps.y + (endProps.y - startProps.y) * progress),
width: Math.round(startProps.width + (endProps.width - startProps.width) * progress), width: Math.round(
height: Math.round(startProps.height + (endProps.height - startProps.height) * progress) 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.setBounds(newBounds);
@@ -622,7 +648,10 @@ function closeLoadingWindow() {
fadeFrame++; fadeFrame++;
// 使用easeInCubic让淡出渐渐加速 // 使用easeInCubic让淡出渐渐加速
const fadeProgress = fadeFrame / fadeTotalFrames; 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); loadingWindow.setOpacity(fadeOpacity);
@@ -647,7 +676,7 @@ function closeLoadingWindow() {
*/ */
function setupWindowEvents() { function setupWindowEvents() {
if (!mainWindow) { if (!mainWindow) {
console.error('无法设置窗口事件:窗口未创建'); console.error("无法设置窗口事件:窗口未创建");
return; return;
} }
@@ -678,12 +707,11 @@ function setupWindowEvents() {
const bounds = mainWindow.getBounds(); const bounds = mainWindow.getBounds();
// 检查鼠标是否在窗口内 // 检查鼠标是否在窗口内
const isInWindow = ( const isInWindow =
mousePos.x >= bounds.x && mousePos.x >= bounds.x &&
mousePos.x <= bounds.x + bounds.width && mousePos.x <= bounds.x + bounds.width &&
mousePos.y >= bounds.y && mousePos.y >= bounds.y &&
mousePos.y <= bounds.y + bounds.height mousePos.y <= bounds.y + bounds.height;
);
// 状态变化时才发送事件和更新点击穿透 // 状态变化时才发送事件和更新点击穿透
if (isInWindow !== isMouseOverWindow) { if (isInWindow !== isMouseOverWindow) {
@@ -691,11 +719,14 @@ function setupWindowEvents() {
// 通知渲染进程 - 无论是开发还是生产模式都需要 // 通知渲染进程 - 无论是开发还是生产模式都需要
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('mouse-hover-change', isMouseOverWindow); mainWindow.webContents.send(
"mouse-hover-change",
isMouseOverWindow
);
} }
} }
} catch (err) { } catch (err) {
console.error('鼠标检测错误:', err); console.error("鼠标检测错误:", err);
} }
}, 100); // 每100毫秒检测一次可根据需要调整 }, 100); // 每100毫秒检测一次可根据需要调整
}; };
@@ -704,21 +735,21 @@ function setupWindowEvents() {
startMouseDetection(); startMouseDetection();
// 监听窗口位置变化事件,保存新位置 // 监听窗口位置变化事件,保存新位置
mainWindow.on('moved', function() { mainWindow.on("moved", function () {
if (!mainWindow) return; if (!mainWindow) return;
const bounds = mainWindow.getBounds(); const bounds = mainWindow.getBounds();
saveWindowConfig(bounds); saveWindowConfig(bounds);
}); });
// 窗口关闭前保存位置 // 窗口关闭前保存位置
mainWindow.on('close', function() { mainWindow.on("close", function () {
if (!mainWindow) return; if (!mainWindow) return;
const bounds = mainWindow.getBounds(); const bounds = mainWindow.getBounds();
saveWindowConfig(bounds); saveWindowConfig(bounds);
}); });
// 窗口关闭时清除轮询 // 窗口关闭时清除轮询
mainWindow.on('closed', () => { mainWindow.on("closed", () => {
if (pollInterval) { if (pollInterval) {
clearInterval(pollInterval); clearInterval(pollInterval);
pollInterval = null; pollInterval = null;
@@ -726,7 +757,7 @@ function setupWindowEvents() {
}); });
// 确保窗口在所有空间都可见macOS特有 // 确保窗口在所有空间都可见macOS特有
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// 设置窗口可见于所有工作区 // 设置窗口可见于所有工作区
mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
} }
@@ -739,60 +770,61 @@ function setupWindowEvents() {
*/ */
function setupIPC(backendPort, frontendPort) { function setupIPC(backendPort, frontendPort) {
// IPC 处理函数,提供详细输出便于调试 // IPC 处理函数,提供详细输出便于调试
ipcMain.handle('get-backend-url', () => { ipcMain.handle("get-backend-url", () => {
const url = `http://127.0.0.1:${backendPort}`; const url = `http://127.0.0.1:${backendPort}`;
console.log(`向前端提供后端URL: ${url}`); console.log(`向前端提供后端URL: ${url}`);
return url; return url;
}); });
// 处理IPC事件获取前端URL // 处理IPC事件获取前端URL
ipcMain.handle('get-frontend-url', () => { ipcMain.handle("get-frontend-url", () => {
return `http://localhost:${frontendPort}`; return `http://localhost:${frontendPort}`;
}); });
// 监听窗口位置和大小变化 // 监听窗口位置和大小变化
ipcMain.on('save-window-bounds', (event, bounds) => { ipcMain.on("save-window-bounds", (event, bounds) => {
saveWindowConfig(bounds); saveWindowConfig(bounds);
}); });
// 监听最小化窗口请求 // 监听最小化窗口请求
ipcMain.on('minimize-window', () => { ipcMain.on("minimize-window", () => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.minimize(); mainWindow.minimize();
} }
}); });
// 监听退出应用请求 // 监听退出应用请求
ipcMain.on('quit-app', () => { ipcMain.on("quit-app", () => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.close(); mainWindow.close();
} }
}); });
// 监听获取端口请求 // 监听获取端口请求
ipcMain.handle('get-ports', async () => { ipcMain.handle("get-ports", async () => {
return { return {
backendPort, backendPort,
frontendPort frontendPort,
}; };
}); });
// 监听加载窗口完成事件 // 监听加载窗口完成事件
ipcMain.on('loading-finished', () => { ipcMain.on("loading-finished", () => {
closeLoadingWindow(); closeLoadingWindow();
}); });
// 处理窗口拖动 - 使用系统原生拖动 // 处理窗口拖动 - 使用系统原生拖动
ipcMain.on('start-window-drag', () => { ipcMain.on("start-window-drag", () => {
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// 使用BrowserWindow.startWindowDrag() API (Electron 14+) // 使用BrowserWindow.startWindowDrag() API (Electron 14+)
try { try {
console.log('使用Electron原生窗口拖动API'); console.log("使用Electron原生窗口拖动API");
mainWindow.webContents.executeJavaScript('document.documentElement.style.cursor = "grab";') mainWindow.webContents
.catch(err => console.error('设置光标样式失败:', err)); .executeJavaScript('document.documentElement.style.cursor = "grab";')
.catch((err) => console.error("设置光标样式失败:", err));
// 使用较新的原生API // 使用较新的原生API
if (typeof mainWindow.startWindowDrag === 'function') { if (typeof mainWindow.startWindowDrag === "function") {
mainWindow.startWindowDrag(); mainWindow.startWindowDrag();
} else { } else {
// 回退到老方法 // 回退到老方法
@@ -800,14 +832,14 @@ function setupIPC(backendPort, frontendPort) {
mainWindow.moveTop(); mainWindow.moveTop();
} }
} catch (error) { } catch (error) {
console.error('窗口拖动错误:', error); console.error("窗口拖动错误:", error);
} }
} }
}); });
// 处理窗口锁定状态设置 // 处理窗口锁定状态设置
ipcMain.on('set-window-lock-state', (_, locked) => { ipcMain.on("set-window-lock-state", (_, locked) => {
console.log('设置窗口锁定状态:', locked); console.log("设置窗口锁定状态:", locked);
// 确保锁定状态下窗口始终忽略鼠标事件 // 确保锁定状态下窗口始终忽略鼠标事件
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
@@ -816,40 +848,40 @@ function setupIPC(backendPort, frontendPort) {
}); });
// 处理锁定时close window的渲染 // 处理锁定时close window的渲染
ipcMain.on('set-close-window-state', (_, showLock) => { ipcMain.on("set-close-window-state", (_, showLock) => {
console.log('设置关闭按钮', showLock); console.log("设置关闭按钮", showLock);
if (showLock) { if (showLock) {
// 展示解锁窗口 // 展示解锁窗口
showUnlockWindow(); showUnlockWindow();
} else { } else {
// 关闭解锁窗口 // 关闭解锁窗口
hideUnlockWindow() hideUnlockWindow();
} }
}) });
// 监听解锁窗口的解锁事件 // 监听解锁窗口的解锁事件
ipcMain.on('unlock-window', () => { ipcMain.on("unlock-window", () => {
console.log('收到解锁窗口事件'); console.log("收到解锁窗口事件");
// 通知主窗口解锁 // 通知主窗口解锁
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.setIgnoreMouseEvents(false); mainWindow.setIgnoreMouseEvents(false);
mainWindow.webContents.send('toggle-lock-window', false); mainWindow.webContents.send("toggle-lock-window", false);
} }
// 关闭解锁窗口 // 关闭解锁窗口
hideUnlockWindow() hideUnlockWindow();
}); });
// 处理语言切换 // 处理语言切换
ipcMain.on('change-language', (_, lang) => { ipcMain.on("change-language", (_, lang) => {
console.log('收到语言切换请求:', lang); console.log("收到语言切换请求:", lang);
i18next.changeLanguage(lang); i18next.changeLanguage(lang);
// 通知所有窗口语言已更改 // 通知所有窗口语言已更改
BrowserWindow.getAllWindows().forEach(window => { BrowserWindow.getAllWindows().forEach((window) => {
if (!window.isDestroyed()) { if (!window.isDestroyed()) {
window.webContents.send('change-language', lang); window.webContents.send("change-language", lang);
} }
}); });
// 更新右键菜单 // 更新右键菜单
@@ -871,5 +903,5 @@ module.exports = {
setupWindowEvents, setupWindowEvents,
setupIPC, setupIPC,
mainWindow: () => mainWindow, mainWindow: () => mainWindow,
saveWindowConfig saveWindowConfig,
}; };

View File

@@ -1,12 +1,12 @@
{ {
"name": "frontend", "name": "frontend",
"version": "0.7.0", "version": "0.7.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "frontend", "name": "frontend",
"version": "0.7.0", "version": "0.7.1",
"dependencies": { "dependencies": {
"axios": "^1.7.7", "axios": "^1.7.7",
"pinia": "^2.2.6", "pinia": "^2.2.6",