This commit is contained in:
sqj
2025-08-19 22:10:35 +08:00
parent 0c4b9beb2f
commit 49493f9db1
5 changed files with 388 additions and 637 deletions

View File

@@ -50,7 +50,7 @@
"marked": "^16.1.2",
"mitt": "^3.0.1",
"NeteaseCloudMusicApi": "^4.27.0",
"node-fetch": "^3.3.2",
"node-fetch": "2",
"pinia": "^3.0.3",
"tdesign-vue-next": "^1.15.2",
"vue-router": "^4.5.1"

View File

@@ -1,19 +1,64 @@
import { app, shell, BrowserWindow, ipcMain, Tray } from 'electron'
import { is } from '@electron-toolkit/utils'
import { app, shell, BrowserWindow, ipcMain, Tray, Menu } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/logo.png?asset'
import path from 'node:path'
import musicService from './services/music'
import pluginService from './services/plugin'
import useWindow from './window/index'
import aiEvents from './events/ai'
let tray: Tray | null = null
let mainWindow: BrowserWindow | null = null
// 使用对象包装布尔值,以便通过引用传递
const isQuittingState = { value: false }
const tray: { value: Tray | null } = { value: null }
let isQuitting = false
function createTray(): void {
// 创建系统托盘
const trayIconPath = path.join(__dirname, '../../resources/logo.png')
tray = new Tray(trayIconPath)
// 创建托盘菜单
const contextMenu = Menu.buildFromTemplate([
{
label: '显示窗口',
click: () => {
if (mainWindow) {
mainWindow.show()
mainWindow.focus()
}
}
},
{
label: '播放/暂停',
click: () => {
// 这里可以添加播放控制逻辑
mainWindow?.webContents.send('music-control')
}
},
{ type: 'separator' },
{
label: '退出',
click: () => {
isQuitting = true
app.quit()
}
}
])
tray.setContextMenu(contextMenu)
tray.setToolTip('Ceru Music')
// 双击托盘图标显示窗口
tray.on('click', () => {
if (mainWindow) {
if (mainWindow.isVisible()) {
mainWindow.hide()
} else {
mainWindow.show()
mainWindow.focus()
}
}
})
}
function createWindow(): void {
// Create the browser window.
@@ -47,13 +92,13 @@ function createWindow(): void {
// 阻止窗口关闭,改为隐藏到系统托盘
mainWindow.on('close', (event) => {
if (!isQuittingState.value) {
if (!isQuitting) {
event.preventDefault()
mainWindow?.hide()
// 显示托盘通知
if (tray.value) {
tray.value.displayBalloon({
if (tray) {
tray.displayBalloon({
iconType: 'info',
title: 'Ceru Music',
content: '已最小化到系统托盘啦,点击托盘图标可重新打开~'
@@ -95,14 +140,98 @@ ipcMain.handle('service-music-request', async (_, api, args) => {
aiEvents(mainWindow)
app.on('before-quit', () => {
isQuittingState.value = true
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.cerulean.music')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
// IPC test
ipcMain.on('ping', () => console.log('pong'))
// 窗口控制 IPC 处理
ipcMain.on('window-minimize', () => {
const window = BrowserWindow.getFocusedWindow()
if (window) {
window.minimize()
}
})
ipcMain.on('window-maximize', () => {
const window = BrowserWindow.getFocusedWindow()
if (window) {
if (window.isMaximized()) {
window.unmaximize()
} else {
window.maximize()
}
}
})
ipcMain.on('window-close', () => {
const window = BrowserWindow.getFocusedWindow()
if (window) {
window.close()
}
})
// Mini 模式 IPC 处理 - 最小化到系统托盘
ipcMain.on('window-mini-mode', (_, isMini) => {
if (mainWindow) {
if (isMini) {
// 进入 Mini 模式:隐藏窗口到系统托盘
mainWindow.hide()
// 显示托盘通知(可选)
if (tray) {
tray.displayBalloon({
title: '澜音 Music',
content: '已最小化到系统托盘啦,点击托盘图标可重新打开~'
})
}
} else {
// 退出 Mini 模式:显示窗口
mainWindow.show()
mainWindow.focus()
}
}
})
// 全屏模式 IPC 处理
ipcMain.on('window-toggle-fullscreen', () => {
if (mainWindow) {
const isFullScreen = mainWindow.isFullScreen()
mainWindow.setFullScreen(!isFullScreen)
}
})
createWindow()
createTray()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// 创建窗口并注册window服务
app.whenReady().then(() => {
// 先创建窗口
createWindow()
// 然后注册window服务确保mainWindow已经创建
useWindow(() => {}, ipcMain, app, mainWindow, isQuittingState, tray)
// 当所有窗口关闭时不退出应用,因为我们有系统托盘
app.on('window-all-closed', () => {
// 在 macOS 上,应用通常会保持活跃状态
// 在其他平台上,我们也保持应用运行,因为有系统托盘
})
// 应用退出前的清理
app.on('before-quit', () => {
isQuitting = true
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

View File

@@ -5,4 +5,6 @@
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {}
declare global {
}

786
yarn.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +0,0 @@
# 音频播放器控件功能完善
## Core Features
- 播放列表管理(添加/删除/排序)
- 音量调节控件
- 多种播放模式(顺序/随机/单曲循环)
- 播放列表导出/导入功能
- 播放列表一键清空功能
## Tech Stack
{
"Web": {
"arch": "vue",
"component": "tdesign"
}
}
## Design
现代简约风格深色背景配合高对比度控件主色调为TDesign主题蓝(#0052D9),包含固定底部播放控制区、垂直弹出式音量控制、图标式播放模式选择器和可拖拽排序的播放列表侧边栏。新增导出/导入功能使用TDesign对话框组件提供文件导出/导入和内容复制/粘贴两种方式,所有导出内容进行加密处理。清空功能添加二次确认对话框防止误操作。
## Plan
Note:
- [ ] is holding
- [/] is doing
- [x] is done
---
[ ] 扩展Pinia状态管理在ControlAudioStore中添加音量控制、播放模式和播放列表相关状态
[ ] 创建VolumeControl.vue组件实现音量调节滑动条和静音按钮功能
[ ] 创建PlayModeSelector.vue组件实现三种播放模式的切换功能
[ ] 创建PlaylistManager.vue组件实现播放列表的基本显示功能
[ ] 在PlaylistManager组件中实现添加和删除曲目功能
[ ] 集成拖拽排序功能到PlaylistManager组件
[ ] 将新组件集成到主播放器界面,调整布局确保合理性
[ ] 实现播放列表与音频控制的联动,确保播放状态正确反映
[ ] 添加键盘快捷键支持和状态反馈机制
[ ] 进行组件间通信测试,确保功能协调工作
[ ] 创建播放列表加密/解密工具函数
[ ] 实现播放列表导出功能(文件导出和内容复制)
[ ] 实现播放列表导入功能(文件导入和内容粘贴)
[ ] 实现播放列表一键清空功能及二次确认机制
[ ] 测试导出/导入功能的加密解密正确性