diff --git a/docs/guide/design.md b/docs/guide/design.md deleted file mode 100644 index a10e4d0..0000000 --- a/docs/guide/design.md +++ /dev/null @@ -1,600 +0,0 @@ -# Ceru Music 产品设计文档 - -## 项目概述 - -Ceru Music 是一个基于 Electron + Vue 3 的跨平台桌面音乐播放器,支持多音乐平台数据源,提供流畅的音乐播放体验。 - -## 项目架构 - -### 技术栈 - -- **前端框架**: Vue 3 + TypeScript + Composition API -- **桌面框架**: Electron (v37.2.3) -- **UI组件库**: TDesign Vue Next (v1.15.2) -- ![image-20250813180317221](..\assets\image-20250813180317221.png) -- **状态管理**: Pinia (v3.0.3) -- **路由管理**: Vue Router (v4.5.1) -- **构建工具**: Vite + electron-vite -- **包管理器**: PNPM -- **Node pnpm 版本**: - - ```bash - PS D:\code\Ceru-Music> node -v - v22.17.0 - PS D:\code\Ceru-Music> pnpm -v - 10.14.0 - ``` - -- - -### 架构设计 - -```asp -Ceru Music -├── 主进程 (Main Process) -│ ├── 应用生命周期管理 -│ ├── 窗口管理 -│ ├── 系统集成 (托盘、快捷键) -│ └── 文件系统操作 -├── 渲染进程 (Renderer Process) -│ ├── Vue 3 应用 -│ ├── 用户界面 -│ ├── 音乐播放控制 -│ └── 数据展示 -└── 预加载脚本 (Preload Script) - └── 安全的 IPC 通信桥梁 -``` - -### 目录结构 - -``` -src/ -├── main/ # 主进程代码 -│ ├── index.ts # 主进程入口 -│ ├── window.ts # 窗口管理 -│ └── services/ # 主进程服务 -├── preload/ # 预加载脚本 -│ └── index.ts # IPC 通信接口 -└── renderer/ # 渲染进程 (Vue 应用) - ├── src/ - │ ├── components/ # Vue 组件 - │ ├── views/ # 页面视图 - │ ├── stores/ # Pinia 状态管理 - │ ├── services/ # API 服务 - │ ├── utils/ # 工具函数 - │ └── types/ # TypeScript 类型定义 - └── index.html # 应用入口 -``` - -## 项目开发使用方式 - -### 开发环境启动 - -```bash -# 安装依赖 -pnpm install - -# 启动开发服务器 -pnpm dev - -# 代码检查 -pnpm lint - -# 类型检查 -pnpm typecheck -``` - -### 构建打包 - -```bash -# 构建当前平台 -pnpm build - -# 构建 Windows 版本 -pnpm build:win - -# 构建 macOS 版本 -pnpm build:mac - -# 构建 Linux 版本 -pnpm build:linux -``` - -## 音乐数据源接口设计 - -### 接口1: 网易云音乐原生接口 (主要数据源) - -#### 获取音乐信息 - -- **请求地址**: `https://music.163.com/api/song/detail` -- **请求参数**: `ids=[ID1,ID2,ID3,...]` 音乐ID列表 -- **示例**: `https://music.163.com/api/song/detail?ids=[36270426]` - -#### 获取音乐直链 - -- **请求地址**: `https://music.163.com/song/media/outer/url` -- **请求参数**: `id=123` 音乐ID -- **示例**: `https://music.163.com/song/media/outer/url?id=36270426.mp3` - -#### 获取歌词 - -- **请求地址**: `https://music.163.com/api/song/lyric` -- **请求参数**: - - `id=123` 音乐ID - - `lv=-1` 获取歌词 - - `yv=-1` 获取逐字歌词 - - `tv=-1` 获取歌词翻译 -- **示例**: `https://music.163.com/api/song/lyric?id=36270426&lv=-1&yv=-1&tv=-1` - -#### 搜索歌曲 - -- **请求地址**: `https://music.163.com/api/search/get/web` -- **请求参数**: - - `s` 歌名 - - `type=1` 搜索类型 - - `offset=0` 偏移量 - - `limit=10` 搜索结果数量 -- **示例**: `https://music.163.com/api/search/get/web?s=来自天堂的魔鬼&type=1&offset=0&limit=10` - -### 接口2: Meting API (备用数据源) - -#### 参数说明 - -- **server**: 数据源 - - `netease` 网易云音乐(默认) - - `tencent` QQ音乐 -- **type**: 类型 - - `name` 歌曲名 - - `artist` 歌手 - - `url` 链接 - - `pic` 封面 - - `lrc` 歌词 - - `song` 单曲 - - `playlist` 歌单 -- **id**: 类型ID(封面ID/单曲ID/歌单ID) - -#### 使用示例 - -``` -https://api.qijieya.cn/meting/?type=url&id=1969519579 -https://api.qijieya.cn/meting/?type=song&id=591321 -https://api.qijieya.cn/meting/?type=playlist&id=2619366284 -``` - -### 接口3: 备选接口 - -- **地址**: https://doc.vkeys.cn/api-doc/ -- **说明**: 不建议使用,延迟较高 - -### 接口4: 自部署接口 (备用) - -- **地址**: `https://music.shiqianjiang.cn?id=你是我的风景&server=netease` -- **说明**: 不支持分页,用于获取歌曲源、歌词源等 -- **文档**: [API文档](./api.md) - -## 核心功能设计 - -### 通用请求函数设计 - -```typescript -// 音乐服务接口定义 -interface MusicService { - search({keyword: string, page?: number, limit?: number}): Promise - getSongDetail({id: string)}: Promise - getSongUrl({id: string}): Promise - getLyric({id: string}): Promise - getPlaylist({id: string}): Promise -} - -// 通用请求函数 -async function request(method: string, ...args: any{},isLoading=false): Promise { - try { - switch (method) { - case 'search': - return await musicService.search(args) - case 'getSongDetail': - return await musicService.getSongDetail(args) - case 'getSongUrl': - return await musicService.getSongUrl(args) - case 'getLyric': - return await musicService.getLyric(args) - default: - throw new Error(`未知的方法: ${method}`) - } - } catch (error) { - console.error(`请求失败: ${method}`, error) - throw error - } -} - -// 使用示例 -request('search', '周杰伦', 1, 20).then((result) => { - console.log('搜索结果:', result) -}) -``` - -### 状态管理设计 (Pinia + LocalStorage) - -```typescript -// stores/music.ts -import { defineStore } from 'pinia' - -export const useMusicStore = defineStore('music', { - state: () => ({ - // 当前播放歌曲 - currentSong: null as Song | null, - // 播放列表 - playlist: [] as Song[], - // 播放状态 - isPlaying: false, - // 播放模式 (顺序、随机、单曲循环) - playMode: 'order' as 'order' | 'random' | 'repeat', - // 音量 - volume: 0.8, - // 播放进度 - currentTime: 0, - duration: 0 - }), - - actions: { - // 播放歌曲 - async playSong(song: Song) { - this.currentSong = song - this.isPlaying = true - this.saveToStorage() - }, - - // 添加到播放列表 - addToPlaylist(songs: Song[]) { - this.playlist.push(...songs) - this.saveToStorage() - }, - - // 保存到本地存储 - saveToStorage() { - localStorage.setItem( - 'music-state', - JSON.stringify({ - currentSong: this.currentSong, - playlist: this.playlist, - playMode: this.playMode, - volume: this.volume - }) - ) - }, - - // 从本地存储恢复 - loadFromStorage() { - const saved = localStorage.getItem('music-state') - if (saved) { - const state = JSON.parse(saved) - Object.assign(this, state) - } - } - } -}) -``` - -### 虚拟滚动列表设计 - -使用 TDesign 的虚拟滚动组件展示大量歌曲数据: - -```vue - -``` - -### 本地数据存储设计 - -#### 播放列表存储 - -```typescript -// 方案1: LocalStorage (简单方案) -class PlaylistStorage { - private key = 'ceru-playlists' - - save(playlists: Playlist[]) { - localStorage.setItem(this.key, JSON.stringify(playlists)) - } - - load(): Playlist[] { - const data = localStorage.getItem(this.key) - return data ? JSON.parse(data) : [] - } -} - -// 方案2: Node.js 文件存储 (最优方案,支持分享) -class FileStorage { - private filePath = path.join(app.getPath('userData'), 'playlists.json') - - async save(playlists: Playlist[]) { - await fs.writeFile(this.filePath, JSON.stringify(playlists, null, 2)) - } - - async load(): Promise { - try { - const data = await fs.readFile(this.filePath, 'utf-8') - return JSON.parse(data) - } catch { - return [] - } - } - - // 导出播放列表 - async export(playlist: Playlist, exportPath: string) { - await fs.writeFile(exportPath, JSON.stringify(playlist, null, 2)) - } - - // 导入播放列表 - async import(importPath: string): Promise { - const data = await fs.readFile(importPath, 'utf-8') - return JSON.parse(data) - } -} -``` - -## 用户体验设计 - -### 首次启动流程 - -```typescript -// stores/app.ts -export const useAppStore = defineStore('app', { - state: () => ({ - isFirstLaunch: true, - hasCompletedWelcome: false, - userPreferences: { - theme: 'auto' as 'light' | 'dark' | 'auto', - language: 'zh-CN', - defaultMusicSource: 'netease', - autoPlay: false - } - }), - - actions: { - checkFirstLaunch() { - const hasLaunched = localStorage.getItem('has-launched') - this.isFirstLaunch = !hasLaunched - - if (this.isFirstLaunch) { - // 跳转到欢迎页面 - router.push('/welcome') - } else { - // 加载用户配置 - this.loadUserPreferences() - router.push('/home') - } - }, - - completeWelcome(preferences?: Partial) { - if (preferences) { - Object.assign(this.userPreferences, preferences) - } - - this.hasCompletedWelcome = true - localStorage.setItem('has-launched', 'true') - localStorage.setItem('user-preferences', JSON.stringify(this.userPreferences)) - - router.push('/home') - } - } -}) -``` - -### 欢迎页面设计 - -![image-20250813180856660](..\assets\image-20250813180856660.png) - -```vue - - - - - -``` - -##### 界面UI参考 - -![..\assets\image-20250813180944752.png) - -## 页面动画设计 - -### 路由过渡动画 - -```vue - - - - - -``` - -## 核心组件设计 - -### 音乐播放器组件 - -```vue - -``` - -## 开发规范 - -### 代码规范 - -- 使用 TypeScript 进行类型检查 -- 遵循 ESLint 配置的代码规范 -- 使用 Prettier 进行代码格式化 -- 组件命名使用 PascalCase -- 文件命名使用 kebab-case - -### Git 提交规范 - -``` -feat: 新功能 -fix: 修复bug -docs: 文档更新 -style: 代码格式调整 -refactor: 代码重构 -test: 测试相关 -chore: 构建过程或辅助工具的变动 -``` - -### 性能优化 - -- 使用虚拟滚动处理大列表 -- 图片懒加载 -- 组件按需加载 -- 音频预加载和缓存 -- 防抖和节流优化用户交互 - -## 待补充功能 - -1. **歌词显示**: 滚动歌词、逐字高亮 -2. **音效处理**: 均衡器、音效增强 -3. **主题系统**: 多主题切换、自定义主题 -4. **快捷键**: 全局快捷键支持 -5. **系统集成**: 媒体键支持、系统通知 -6. **云同步**: 播放列表云端同步 -7. **插件系统**: 支持第三方插件扩展 -8. **音乐推荐**: 基于听歌历史的智能推荐 - ---- - -_本设计文档将随着项目开发进度持续更新和完善。_