diff --git a/app.js b/app.js index 8d014cc..c2c1839 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ const path = require('path'); const Store = require('electron-store'); const setJson = require('./electron/set.json'); const { loadLyricWindow } = require('./electron/lyric'); +const config = require('./electron/config'); let mainWin = null; function createWindow() { @@ -11,8 +12,8 @@ function createWindow() { height: 780, frame: false, webPreferences: { - nodeIntegration: true, - // contextIsolation: false, + nodeIntegration: false, + contextIsolation: true, preload: path.join(__dirname, '/electron/preload.js'), }, }); @@ -20,7 +21,7 @@ function createWindow() { win.setMinimumSize(1200, 780); if (process.env.NODE_ENV === 'development') { win.webContents.openDevTools({ mode: 'detach' }); - win.loadURL('http://localhost:4488/'); + win.loadURL(`http://localhost:${config.development.mainPort}/`); } else { win.loadURL(`file://${__dirname}/dist/index.html`); } diff --git a/components.d.ts b/components.d.ts index 94678ab..511a56e 100644 --- a/components.d.ts +++ b/components.d.ts @@ -13,6 +13,7 @@ declare module 'vue' { MvPlayer: typeof import('./src/components/MvPlayer.vue')['default'] NAvatar: typeof import('naive-ui')['NAvatar'] NButton: typeof import('naive-ui')['NButton'] + NButtonGroup: typeof import('naive-ui')['NButtonGroup'] NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NDialogProvider: typeof import('naive-ui')['NDialogProvider'] NDrawer: typeof import('naive-ui')['NDrawer'] diff --git a/electron/config.js b/electron/config.js new file mode 100644 index 0000000..e849ffb --- /dev/null +++ b/electron/config.js @@ -0,0 +1,11 @@ +module.exports = { + // 开发环境配置 + development: { + mainPort: 4488, + lyricPort: 4488, + }, + // 生产环境配置 + production: { + distPath: '../dist', + }, +}; diff --git a/electron/lyric.js b/electron/lyric.js index bb307e4..5171324 100644 --- a/electron/lyric.js +++ b/electron/lyric.js @@ -1,5 +1,6 @@ const { BrowserWindow } = require('electron'); const path = require('path'); +const config = require('./config'); let lyricWindow = null; @@ -10,13 +11,16 @@ const createWin = () => { frame: false, show: false, transparent: true, + hasShadow: false, webPreferences: { - nodeIntegration: true, + nodeIntegration: false, + contextIsolation: true, preload: `${__dirname}/preload.js`, - contextIsolation: false, + webSecurity: false, }, }); }; + const loadLyricWindow = (ipcMain) => { ipcMain.on('open-lyric', () => { if (lyricWindow) { @@ -28,9 +32,9 @@ const loadLyricWindow = (ipcMain) => { createWin(); if (process.env.NODE_ENV === 'development') { lyricWindow.webContents.openDevTools({ mode: 'detach' }); - lyricWindow.loadURL('http://localhost:4678/#/lyric'); + lyricWindow.loadURL(`http://localhost:${config.development.lyricPort}/#/lyric`); } else { - const distPath = path.resolve(__dirname, '../dist'); + const distPath = path.resolve(__dirname, config.production.distPath); lyricWindow.loadURL(`file://${distPath}/index.html#/lyric`); } diff --git a/electron/preload.js b/electron/preload.js index 4d5f489..ebee7d8 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -1,5 +1,6 @@ -const { contextBridge, ipcRenderer, app } = require('electron'); +const { contextBridge, ipcRenderer } = require('electron'); +// 主进程通信 contextBridge.exposeInMainWorld('electronAPI', { minimize: () => ipcRenderer.send('minimize-window'), maximize: () => ipcRenderer.send('maximize-window'), @@ -11,18 +12,19 @@ contextBridge.exposeInMainWorld('electronAPI', { sendLyric: (data) => ipcRenderer.send('send-lyric', data), }); -const electronHandler = { +// 存储相关 +contextBridge.exposeInMainWorld('electron', { ipcRenderer: { - setStoreValue: (key, value) => { - ipcRenderer.send('setStore', key, value); + setStoreValue: (key, value) => ipcRenderer.send('setStore', key, value), + getStoreValue: (key) => ipcRenderer.sendSync('getStore', key), + on: (channel, func) => { + ipcRenderer.on(channel, (event, ...args) => func(...args)); }, - - getStoreValue(key) { - const resp = ipcRenderer.sendSync('getStore', key); - return resp; + once: (channel, func) => { + ipcRenderer.once(channel, (event, ...args) => func(...args)); + }, + send: (channel, data) => { + ipcRenderer.send(channel, data); }, }, - app, -}; - -contextBridge.exposeInMainWorld('electron', electronHandler); +}); diff --git a/electron/update.js b/electron/update.js new file mode 100644 index 0000000..55e51ba --- /dev/null +++ b/electron/update.js @@ -0,0 +1,69 @@ +const { app, BrowserWindow } = require('electron'); +const axios = require('axios'); +const fs = require('fs'); +const path = require('path'); +const AdmZip = require('adm-zip'); + +class Updater { + constructor(mainWindow) { + this.mainWindow = mainWindow; + this.updateUrl = 'http://your-server.com/update'; // 更新服务器地址 + this.version = app.getVersion(); + } + + // 检查更新 + async checkForUpdates() { + try { + const response = await axios.get(`${this.updateUrl}/check`, { + params: { + version: this.version, + }, + }); + + if (response.data.hasUpdate) { + await this.downloadUpdate(response.data.downloadUrl); + } + } catch (error) { + console.error('检查更新失败:', error); + } + } + + // 下载更新 + async downloadUpdate(downloadUrl) { + try { + const response = await axios({ + url: downloadUrl, + method: 'GET', + responseType: 'arraybuffer', + }); + + const tempPath = path.join(app.getPath('temp'), 'update.zip'); + fs.writeFileSync(tempPath, response.data); + + await this.extractUpdate(tempPath); + } catch (error) { + console.error('下载更新失败:', error); + } + } + + // 解压更新 + async extractUpdate(zipPath) { + try { + const zip = new AdmZip(zipPath); + const targetPath = path.join(__dirname, '../dist'); // 前端文件目录 + + // 解压文件 + zip.extractAllTo(targetPath, true); + + // 删除临时文件 + fs.unlinkSync(zipPath); + + // 刷新页面 + this.mainWindow.webContents.reload(); + } catch (error) { + console.error('解压更新失败:', error); + } + } +} + +module.exports = Updater; diff --git a/package.json b/package.json index e49d227..5e0124c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "autoprefixer": "^10.4.20", "axios": "^1.7.7", "cross-env": "^7.0.3", - "electron": "^32.0.1", + "electron": "^32.2.7", "electron-builder": "^25.0.5", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", diff --git a/src/hooks/MusicHook.ts b/src/hooks/MusicHook.ts index 2071c78..228e2a2 100644 --- a/src/hooks/MusicHook.ts +++ b/src/hooks/MusicHook.ts @@ -160,37 +160,110 @@ export const getLrcTimeRange = (index: number) => ({ nextTime: lrcTimeArray.value[index + 1], }); +// 监听歌词数组变化,当切换歌曲时重新初始化歌词窗口 +watch( + () => lrcArray.value, + (newLrcArray) => { + if (newLrcArray.length > 0 && isElectron.value) { + // 重新初始化歌词数据 + initLyricWindow(); + // 发送当前状态 + sendLyricToWin(); + } + }, +); + +// 监听播放状态变化 +watch(isPlaying, (newIsPlaying) => { + if (isElectron.value) { + sendLyricToWin(newIsPlaying); + } +}); + +// 监听时间变化 +watch(nowTime, (newTime) => { + const newIndex = getLrcIndex(newTime); + if (newIndex !== nowIndex.value) { + nowIndex.value = newIndex; + currentLrcProgress.value = 0; // 重置进度 + // 当索引变化时发送更新 + if (isElectron.value) { + sendLyricToWin(); + } + } +}); + +// 处理歌曲结束 +export const handleEnded = () => { + // ... 原有的结束处理逻辑 ... + + // 如果有歌词窗口,发送初始化数据 + if (isElectron.value) { + // 延迟一下等待新歌曲加载完成 + setTimeout(() => { + initLyricWindow(); + sendLyricToWin(); + }, 100); + } +}; + +// 初始化歌词数据 +export const initLyricWindow = () => { + if (!isElectron.value) return; + try { + if (lrcArray.value.length > 0) { + console.log('Initializing lyric window with data:', { + lrcArray: lrcArray.value, + lrcTimeArray: lrcTimeArray.value, + allTime: allTime.value, + }); + + const staticData = { + type: 'init', + lrcArray: lrcArray.value, + lrcTimeArray: lrcTimeArray.value, + allTime: allTime.value, + }; + windowData.electronAPI.sendLyric(JSON.stringify(staticData)); + } else { + console.log('No lyrics available for initialization'); + } + } catch (error) { + console.error('Error initializing lyric window:', error); + } +}; + +// 发送歌词更新数据 export const sendLyricToWin = (isPlay: boolean = true) => { if (!isElectron.value) return; try { if (lrcArray.value.length > 0) { const nowIndex = getLrcIndex(nowTime.value); - const { currentLrc, nextLrc } = getCurrentLrc(); - const { currentTime, nextTime } = getLrcTimeRange(nowIndex); - // 设置lyricWinData 获取 当前播放的两句歌词 和歌词时间 - const lyricWinData = { - currentLrc, - nextLrc, - currentTime, - nextTime, + const updateData = { + type: 'update', nowIndex, - lrcTimeArray: lrcTimeArray.value, - lrcArray: lrcArray.value, nowTime: nowTime.value, - allTime: allTime.value, startCurrentTime: lrcTimeArray.value[nowIndex], + nextTime: lrcTimeArray.value[nowIndex + 1], isPlay, }; - windowData.electronAPI.sendLyric(JSON.stringify(lyricWinData)); + windowData.electronAPI.sendLyric(JSON.stringify(updateData)); } } catch (error) { - console.error('Error sending lyric to window:', error); + console.error('Error sending lyric update:', error); } }; export const openLyric = () => { if (!isElectron.value) return; + console.log('Opening lyric window'); windowData.electronAPI.openLyric(); - sendLyricToWin(); + + // 延迟一下初始化,确保窗口已经创建 + setTimeout(() => { + console.log('Initializing lyric window after delay'); + initLyricWindow(); + sendLyricToWin(); + }, 500); }; diff --git a/src/layout/components/MusicFull.vue b/src/layout/components/MusicFull.vue index 2e7eee3..4ca8b09 100644 --- a/src/layout/components/MusicFull.vue +++ b/src/layout/components/MusicFull.vue @@ -252,6 +252,7 @@ defineExpose({ background-color: inherit; width: 500px; height: 550px; + mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%); &-text { @apply text-2xl cursor-pointer font-bold px-2 py-4; transition: all 0.3s ease; diff --git a/src/views/lyric/index.vue b/src/views/lyric/index.vue index c333f3e..b1bf2bb 100644 --- a/src/views/lyric/index.vue +++ b/src/views/lyric/index.vue @@ -1,76 +1,107 @@