diff --git a/components.d.ts b/components.d.ts index 7bb3c20..94e7b9f 100644 --- a/components.d.ts +++ b/components.d.ts @@ -97,7 +97,6 @@ declare module 'vue' { NP: typeof import('naive-ui')['NP'] NPopconfirm: typeof import('naive-ui')['NPopconfirm'] NPopover: typeof import('naive-ui')['NPopover'] - NProgress: typeof import('naive-ui')['NProgress'] NQrCode: typeof import('naive-ui')['NQrCode'] NRadio: typeof import('naive-ui')['NRadio'] NRadioGroup: typeof import('naive-ui')['NRadioGroup'] @@ -107,7 +106,6 @@ declare module 'vue' { NSlider: typeof import('naive-ui')['NSlider'] NSpin: typeof import('naive-ui')['NSpin'] NSwitch: typeof import('naive-ui')['NSwitch'] - NTab: typeof import('naive-ui')['NTab'] NTabPane: typeof import('naive-ui')['NTabPane'] NTabs: typeof import('naive-ui')['NTabs'] NTag: typeof import('naive-ui')['NTag'] diff --git a/electron/main/ipc/ipc-lyric.ts b/electron/main/ipc/ipc-lyric.ts index 2cb903a..40d16b7 100644 --- a/electron/main/ipc/ipc-lyric.ts +++ b/electron/main/ipc/ipc-lyric.ts @@ -8,7 +8,6 @@ import mainWindow from "../windows/main-window"; */ const initLyricIpc = (): void => { const store = useStore(); - const mainWin = mainWindow.getWin(); // 歌词窗口 let lyricWin: BrowserWindow | null = null; @@ -58,6 +57,7 @@ const initLyricIpc = (): void => { // 更新歌词窗口配置 ipcMain.on("update-desktop-lyric-option", (_, option, callback: boolean = false) => { + const mainWin = mainWindow.getWin(); if (!option || !isWinAlive(lyricWin)) return; // 增量更新 const prevOption = store.get("lyric.config"); @@ -69,7 +69,9 @@ const initLyricIpc = (): void => { if (callback && isWinAlive(lyricWin)) { lyricWin.webContents.send("update-desktop-lyric-option", option); } - mainWin?.webContents.send("update-desktop-lyric-option", option); + if (isWinAlive(mainWin)) { + mainWin?.webContents.send("update-desktop-lyric-option", option); + } }); // 播放状态更改 @@ -155,7 +157,8 @@ const initLyricIpc = (): void => { // 请求歌词数据 ipcMain.on("request-desktop-lyric-data", () => { - if (!isWinAlive(lyricWin)) return; + const mainWin = mainWindow.getWin(); + if (!isWinAlive(lyricWin) || !isWinAlive(mainWin)) return; // 触发窗口更新 mainWin?.webContents.send("request-desktop-lyric-data"); }); @@ -171,14 +174,16 @@ const initLyricIpc = (): void => { // 关闭桌面歌词 ipcMain.on("closeDesktopLyric", () => { - if (!isWinAlive(lyricWin)) return; + const mainWin = mainWindow.getWin(); + if (!isWinAlive(lyricWin) || !isWinAlive(mainWin)) return; lyricWin.hide(); mainWin?.webContents.send("closeDesktopLyric"); }); // 锁定/解锁桌面歌词 ipcMain.on("toogleDesktopLyricLock", (_, isLock: boolean, isTemp: boolean = false) => { - if (!isWinAlive(lyricWin)) return; + const mainWin = mainWindow.getWin(); + if (!isWinAlive(lyricWin) || !isWinAlive(mainWin)) return; // 是否穿透 if (isLock) { lyricWin.setIgnoreMouseEvents(true, { forward: true }); diff --git a/electron/main/ipc/ipc-shortcut.ts b/electron/main/ipc/ipc-shortcut.ts index 439dcfd..420744d 100644 --- a/electron/main/ipc/ipc-shortcut.ts +++ b/electron/main/ipc/ipc-shortcut.ts @@ -7,13 +7,12 @@ import mainWindow from "../windows/main-window"; * @returns void */ const initShortcutIpc = (): void => { - const mainWin = mainWindow.getWin(); - // 快捷键是否被注册 ipcMain.handle("is-shortcut-registered", (_, shortcut: string) => isShortcutRegistered(shortcut)); // 注册快捷键 ipcMain.handle("register-all-shortcut", (_, allShortcuts: any): string[] | false => { + const mainWin = mainWindow.getWin(); if (!mainWin || !allShortcuts) return false; // 卸载所有快捷键 unregisterShortcuts(); diff --git a/electron/main/ipc/ipc-system.ts b/electron/main/ipc/ipc-system.ts index 571dea5..15d25b9 100644 --- a/electron/main/ipc/ipc-system.ts +++ b/electron/main/ipc/ipc-system.ts @@ -1,4 +1,4 @@ -import { ipcMain, net, powerSaveBlocker, session } from "electron"; +import { app, ipcMain, net, powerSaveBlocker, session } from "electron"; import { ipcLog } from "../logger"; import { getFonts } from "font-list"; import { useStore } from "../store"; @@ -10,7 +10,6 @@ import mainWindow from "../windows/main-window"; */ const initSystemIpc = (): void => { const store = useStore(); - const mainWin = mainWindow.getWin(); /** 阻止系统息屏 ID */ let preventId: number | null = null; @@ -28,6 +27,11 @@ const initSystemIpc = (): void => { } }); + // 退出应用 + ipcMain.on("quit-app", () => { + app.exit(); + }); + // 获取系统全部字体 ipcMain.handle("get-all-fonts", async () => { try { @@ -41,13 +45,18 @@ const initSystemIpc = (): void => { // 取消代理 ipcMain.on("remove-proxy", () => { + const mainWin = mainWindow.getWin(); store.set("proxy", ""); - mainWin?.webContents.session.setProxy({ proxyRules: "" }); + if (mainWin) { + mainWin?.webContents.session.setProxy({ proxyRules: "" }); + } ipcLog.info("✅ Remove proxy successfully"); }); // 配置网络代理 ipcMain.on("set-proxy", (_, config) => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; const proxyRules = `${config.protocol}://${config.server}:${config.port}`; store.set("proxy", proxyRules); mainWin?.webContents.session.setProxy({ proxyRules }); diff --git a/electron/main/ipc/ipc-tray.ts b/electron/main/ipc/ipc-tray.ts index 55c3627..dcad051 100644 --- a/electron/main/ipc/ipc-tray.ts +++ b/electron/main/ipc/ipc-tray.ts @@ -7,12 +7,13 @@ import lyricWindow from "../windows/lyric-window"; */ const initTrayIpc = (): void => { const tray = getMainTray(); - const lyricWin = lyricWindow.getWin(); // 音乐播放状态更改 ipcMain.on("play-status-change", (_, playStatus: boolean) => { + const lyricWin = lyricWindow.getWin(); tray?.setPlayState(playStatus ? "play" : "pause"); - lyricWin?.webContents.send("play-status-change", playStatus); + if (!lyricWin) return; + lyricWin.webContents.send("play-status-change", playStatus); }); // 音乐名称更改 diff --git a/electron/main/ipc/ipc-update.ts b/electron/main/ipc/ipc-update.ts index cd7b1bd..c74d2a6 100644 --- a/electron/main/ipc/ipc-update.ts +++ b/electron/main/ipc/ipc-update.ts @@ -3,10 +3,12 @@ import { checkUpdate, startDownloadUpdate } from "../update"; import mainWindow from "../windows/main-window"; const initUpdateIpc = () => { - const mainWin = mainWindow.getWin(); - // 检查更新 - ipcMain.on("check-update", (_event, showTip) => checkUpdate(mainWin!, showTip)); + ipcMain.on("check-update", (_event, showTip) => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; + checkUpdate(mainWin, showTip); + }); // 开始下载更新 ipcMain.on("start-download-update", () => startDownloadUpdate()); diff --git a/electron/main/ipc/ipc-window.ts b/electron/main/ipc/ipc-window.ts index e4467fb..7b8dfdd 100644 --- a/electron/main/ipc/ipc-window.ts +++ b/electron/main/ipc/ipc-window.ts @@ -11,22 +11,24 @@ import loginWindow from "../windows/login-window"; * @returns void */ const initWindowsIpc = (): void => { - // 相关窗口 - const mainWin = mainWindow.getWin(); - const loadWin = loadWindow.getWin(); // store const store = useStore(); // 当前窗口状态 ipcMain.on("win-state", (event) => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; event.returnValue = mainWin?.isMaximized(); }); // 加载完成 ipcMain.on("win-loaded", () => { + const loadWin = loadWindow.getWin(); + const mainWin = mainWindow.getWin(); if (loadWin && !loadWin.isDestroyed()) loadWin.destroy(); const isMaximized = store.get("window")?.maximized; if (isMaximized) mainWin?.maximize(); + if (!mainWin) return; mainWin?.show(); mainWin?.focus(); // 解决窗口不立即显示 @@ -45,34 +47,37 @@ const initWindowsIpc = (): void => { // 最小化 ipcMain.on("win-min", (event) => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; event.preventDefault(); mainWin?.minimize(); }); // 最大化 ipcMain.on("win-max", () => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; mainWin?.maximize(); }); // 还原 ipcMain.on("win-restore", () => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; mainWin?.restore(); }); - // 关闭 - ipcMain.on("win-close", (event) => { - event.preventDefault(); - mainWin?.close(); - app.quit(); - }); - // 隐藏 ipcMain.on("win-hide", () => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; mainWin?.hide(); }); // 显示 ipcMain.on("win-show", () => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; mainWin?.show(); mainWin?.focus(); }); @@ -85,11 +90,15 @@ const initWindowsIpc = (): void => { // 向主窗口发送事件 ipcMain.on("send-to-mainWin", (_, eventName, ...args) => { - mainWin?.webContents.send(eventName, ...args); + const mainWin = mainWindow.getWin(); + if (!mainWin || mainWin.isDestroyed() || mainWin.webContents.isDestroyed()) return; + mainWin.webContents.send(eventName, ...args); }); // 显示进度 ipcMain.on("set-bar", (_event, val: number | "none" | "indeterminate" | "error" | "paused") => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; switch (val) { case "none": mainWin?.setProgressBar(-1); @@ -115,6 +124,8 @@ const initWindowsIpc = (): void => { // 开启控制台 ipcMain.on("open-dev-tools", () => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; mainWin?.webContents.openDevTools({ title: "SPlayer DevTools", mode: isDev ? "right" : "detach", @@ -122,10 +133,16 @@ const initWindowsIpc = (): void => { }); // 开启登录窗口 - ipcMain.on("open-login-web", () => loginWindow.create(mainWin!)); + ipcMain.on("open-login-web", () => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; + loginWindow.create(mainWin); + }); // 开启设置 ipcMain.on("open-setting", (_, type) => { + const mainWin = mainWindow.getWin(); + if (!mainWin) return; mainWin?.show(); mainWin?.focus(); mainWin?.webContents.send("openSetting", type); diff --git a/electron/main/windows/lyric-window.ts b/electron/main/windows/lyric-window.ts index c24f76e..bb93665 100644 --- a/electron/main/windows/lyric-window.ts +++ b/electron/main/windows/lyric-window.ts @@ -28,9 +28,11 @@ class LyricWindow { }); // 歌词窗口关闭 this.win?.on("close", () => { + this.win = null; const mainWin = mainWindow?.getWin(); - if (!mainWin || mainWin.isDestroyed() || mainWin.webContents.isDestroyed()) return; - mainWin?.webContents.send("closeDesktopLyric"); + if (mainWin) { + mainWin?.webContents.send("closeDesktopLyric"); + } }); } /** @@ -79,7 +81,8 @@ class LyricWindow { * @returns BrowserWindow | null */ getWin(): BrowserWindow | null { - return this.win; + if (this.win && !this.win?.isDestroyed()) return this.win; + return null; } } diff --git a/electron/main/windows/main-window.ts b/electron/main/windows/main-window.ts index a9ac8ce..67945d8 100644 --- a/electron/main/windows/main-window.ts +++ b/electron/main/windows/main-window.ts @@ -81,6 +81,13 @@ class MainWindow { this.saveBounds(); }); } + // 窗口关闭 + this.win?.on("close", (event) => { + event.preventDefault(); + this.win?.show(); + this.win?.focus(); + this.win?.webContents.send("win-will-close"); + }); } /** * 创建窗口 @@ -110,7 +117,10 @@ class MainWindow { * @returns BrowserWindow | null */ getWin(): BrowserWindow | null { - return this.win; + if (this.win && !this.win.isDestroyed()) { + return this.win; + } + return null; } /** * 显示主窗口 diff --git a/src/components/Layout/Nav.vue b/src/components/Layout/Nav.vue index 6086897..fd2be63 100644 --- a/src/components/Layout/Nav.vue +++ b/src/components/Layout/Nav.vue @@ -124,7 +124,7 @@ const hideOrClose = (action: "hide" | "exit") => { settingStore.closeAppMethod = action; } showCloseModal.value = false; - window.electron.ipcRenderer.send(action === "hide" ? "win-hide" : "win-close"); + window.electron.ipcRenderer.send(action === "hide" ? "win-hide" : "quit-app"); }; // 尝试关闭软件 @@ -203,6 +203,9 @@ onMounted(() => { window.electron.ipcRenderer.on("win-state-change", (_event, value: boolean) => { isMax.value = value; }); + window.electron.ipcRenderer.on("win-will-close", () => { + tryClose(); + }); } }); diff --git a/src/components/Modal/UserAgreement.vue b/src/components/Modal/UserAgreement.vue index 4f0e73d..a4b6607 100644 --- a/src/components/Modal/UserAgreement.vue +++ b/src/components/Modal/UserAgreement.vue @@ -130,7 +130,7 @@ const isReadOver = useElementVisibility(readOverRef); // 关闭软件 const closeApp = () => { - window.electron.ipcRenderer.send("win-close"); + window.electron.ipcRenderer.send("quit-app"); }; diff --git a/src/views/List/liked.vue b/src/views/List/liked.vue index a9531f6..28f4c59 100644 --- a/src/views/List/liked.vue +++ b/src/views/List/liked.vue @@ -175,7 +175,7 @@ import { playlistDetail, playlistAllSongs } from "@/api/playlist"; import { formatCoverList, formatSongsList } from "@/utils/format"; import { coverLoaded, formatNumber, fuzzySearch, renderIcon } from "@/utils/helper"; import { renderToolbar } from "@/utils/meta"; -import { debounce, isObject } from "lodash-es"; +import { debounce, isObject, uniqBy } from "lodash-es"; import { useDataStore, useStatusStore } from "@/stores"; import { openBatchList, openUpdatePlaylist } from "@/utils/modal"; import { formatTimestamp } from "@/utils/time"; @@ -302,7 +302,8 @@ const getPlaylistData = async (id: number, getList: boolean, refresh: boolean) = if (isLogin() === 1 && (playlistDetailData.value?.count as number) < 800) { const ids: number[] = detail.privileges.map((song: any) => song.id as number); const result = await songDetail(ids); - playlistData.value = formatSongsList(result.songs); + // 直接批量详情返回时也进行一次按 id 去重 + playlistData.value = uniqBy(formatSongsList(result.songs), "id"); } else { await getPlaylistAllSongs(id, playlistDetailData.value.count || 0, refresh); } @@ -317,7 +318,8 @@ const loadLikedCache = () => { playlistDetailData.value = dataStore.likeSongsList.detail; } if (dataStore.likeSongsList.data.length) { - playlistData.value = dataStore.likeSongsList.data; + // 去重缓存中的歌曲,避免重复展示与后续重复拼接 + playlistData.value = uniqBy(dataStore.likeSongsList.data, "id"); } }; @@ -339,11 +341,13 @@ const getPlaylistAllSongs = async ( const result = await playlistAllSongs(id, limit, offset); const songData = formatSongsList(result.songs); listData.push(...songData); - if (!refresh) playlistData.value = playlistData.value.concat(songData); + // 非刷新模式下,增量拼接时进行去重,避免与缓存或上一页数据重复 + if (!refresh) playlistData.value = uniqBy([...playlistData.value, ...songData], "id"); // 更新数据 offset += limit; } while (offset < count && isLikedPage.value); - if (refresh) playlistData.value = listData; + // 刷新模式下,统一以最终聚合数据为准,并进行去重 + if (refresh) playlistData.value = uniqBy(listData, "id"); // 关闭加载 loadingMsgShow(false); };