From d741391484d449af802c9e8a5a179ff10d8debf0 Mon Sep 17 00:00:00 2001 From: gejiuyuan <2507188687@qq.com> Date: Sat, 5 Nov 2022 14:17:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=B0=83=E6=95=B4prettier?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 82 +- README.md | 19 +- docs/howler.md | 280 ++---- global.d.ts | 82 +- index.html | 66 +- postcss.config.js | 14 +- prettier.config.js | 68 +- public/pwa/site.webmanifest | 34 +- scripts/lint-staged.mjs | 42 +- scripts/vite/constant.ts | 22 +- scripts/vite/manualChunk.ts | 34 +- scripts/vite/proxy.ts | 15 +- scripts/vite/pwa.ts | 70 +- scripts/vite/vite.config.ts | 391 ++++---- scripts/vite/viteConf.d.ts | 28 +- src/App.scss | 44 +- src/App.tsx | 349 +++---- src/api/auth.ts | 155 ++- src/api/broadcast.ts | 321 +++--- src/api/homeInfo.ts | 39 +- src/api/music.ts | 221 ++--- src/api/mv.ts | 192 ++-- src/api/other.ts | 122 +-- src/api/playlist.ts | 396 ++++---- src/api/search.ts | 110 ++- src/api/singer.ts | 237 +++-- src/api/user.ts | 327 +++---- src/api/video.ts | 241 +++-- src/assets/icons/iconfont.css | 163 +-- src/assets/js/colorthief.js | 417 +++++++- src/components/album/index.scss | 1 + src/components/album/index.tsx | 25 +- src/components/artist/album/index.scss | 82 +- src/components/artist/album/index.tsx | 116 ++- src/components/artist/allSongs/index.scss | 12 +- src/components/artist/allSongs/index.tsx | 216 ++-- src/components/artist/description/index.scss | 39 +- src/components/artist/description/index.tsx | 98 +- src/components/artist/featured/index.scss | 7 +- src/components/artist/featured/index.tsx | 62 +- src/components/artist/index.scss | 108 +- src/components/artist/index.tsx | 401 ++++---- src/components/artist/mv/index.scss | 5 +- src/components/artist/mv/index.tsx | 106 +- .../artist/similarSinger/index.scss | 4 +- src/components/artist/similarSinger/index.tsx | 77 +- src/components/home-header/index.scss | 120 ++- src/components/home-header/index.tsx | 152 +-- src/components/home-left/index.scss | 56 +- src/components/home-left/index.tsx | 192 ++-- src/components/lyric/index.scss | 329 +++---- src/components/lyric/index.tsx | 925 +++++++++--------- src/components/music-hall/artist/index.scss | 8 +- src/components/music-hall/artist/index.tsx | 380 ++++--- .../music-hall/featured/banner/index.scss | 141 +-- .../music-hall/featured/banner/index.tsx | 187 ++-- src/components/music-hall/featured/index.scss | 114 +-- src/components/music-hall/featured/index.tsx | 180 ++-- src/components/music-hall/index.scss | 16 +- src/components/music-hall/index.tsx | 58 +- .../music-hall/newestdisc/index.scss | 24 +- .../music-hall/newestdisc/index.tsx | 399 ++++---- .../music-hall/newestmusic/index.scss | 12 +- .../music-hall/newestmusic/index.tsx | 167 ++-- src/components/music-hall/songlist/index.scss | 78 +- src/components/music-hall/songlist/index.tsx | 331 +++---- src/components/music-hall/top/index.scss | 234 +++-- src/components/music-hall/top/index.tsx | 242 +++-- src/components/music-radio/index.scss | 4 +- src/components/music-radio/index.tsx | 18 +- src/components/mv/index.scss | 1 + src/components/mv/index.tsx | 24 +- src/components/my-page/index.scss | 116 ++- src/components/my-page/index.tsx | 211 ++-- src/components/online-video/all/index.scss | 18 +- src/components/online-video/all/index.tsx | 151 +-- .../online-video/category/index.scss | 12 +- .../online-video/category/index.tsx | 264 ++--- src/components/online-video/index.scss | 17 +- src/components/online-video/index.tsx | 47 +- src/components/personal-recommend/index.scss | 175 ++-- src/components/personal-recommend/index.tsx | 356 +++---- src/components/player-controller/index.scss | 279 +++--- src/components/player-controller/index.tsx | 262 ++--- src/components/player-queue/index.scss | 272 +++-- src/components/player-queue/index.tsx | 217 ++-- src/components/search/album/index.scss | 8 +- src/components/search/album/index.tsx | 73 +- src/components/search/index.scss | 94 +- src/components/search/index.tsx | 562 ++++++----- src/components/search/lyric/index.scss | 102 +- src/components/search/lyric/index.tsx | 299 +++--- src/components/search/mv/index.scss | 1 + src/components/search/mv/index.tsx | 90 +- src/components/search/radio/index.scss | 4 +- src/components/search/radio/index.tsx | 20 +- src/components/search/singer/index.scss | 54 +- src/components/search/singer/index.tsx | 44 +- src/components/search/songlist/index.scss | 108 +- src/components/search/songlist/index.tsx | 171 ++-- src/components/search/songs/index.scss | 4 +- src/components/search/songs/index.tsx | 79 +- src/components/search/user/index.scss | 54 +- src/components/search/user/index.tsx | 149 ++- src/components/search/video/index.scss | 7 +- src/components/search/video/index.tsx | 154 ++- src/components/setting/general/index.scss | 14 +- src/components/setting/general/index.tsx | 191 ++-- src/components/setting/index.scss | 96 +- src/components/setting/index.tsx | 65 +- src/components/song-page/comment/index.scss | 203 ++-- src/components/song-page/comment/index.tsx | 254 ++--- src/components/song-page/detail/index.scss | 31 +- src/components/song-page/detail/index.tsx | 55 +- src/components/song-page/index.scss | 71 +- src/components/song-page/index.tsx | 286 +++--- src/components/song-page/util.ts | 60 +- src/components/songlist/comment/index.scss | 4 +- src/components/songlist/comment/index.tsx | 30 +- src/components/songlist/index.scss | 219 +++-- src/components/songlist/index.tsx | 551 +++++------ src/components/songlist/music/index.scss | 4 +- src/components/songlist/music/index.tsx | 26 +- src/components/songlist/subscriber/index.scss | 4 +- src/components/songlist/subscriber/index.tsx | 168 ++-- src/components/user/collection/index.scss | 5 +- src/components/user/collection/index.tsx | 75 +- src/components/user/config.ts | 12 +- src/components/user/index.scss | 90 +- src/components/user/index.tsx | 379 ++++--- src/components/user/play-record/index.scss | 46 +- src/components/user/play-record/index.tsx | 164 ++-- src/components/user/songlist/index.scss | 3 +- src/components/user/songlist/index.tsx | 71 +- src/components/video/index.scss | 159 ++- src/components/video/index.tsx | 569 ++++++----- src/database/audio.ts | 4 +- src/database/database.d.ts | 8 +- src/database/index.ts | 4 +- src/database/player.ts | 81 +- src/dependency/enum.ts | 54 +- src/hooks/index.ts | 8 +- src/hooks/onBeforeInstallPrompt.ts | 45 +- src/hooks/onRouteHook.ts | 89 +- src/hooks/useBetterFullscreen.ts | 93 +- src/hooks/useChildren.ts | 163 +-- src/hooks/useHowler.ts | 327 +++---- src/hooks/useParent.ts | 69 +- src/hooks/useTimeSlice.ts | 54 +- src/main.ts | 64 +- src/request/index.ts | 103 +- src/router/index.ts | 70 +- src/router/routes.ts | 549 ++++++----- src/scss/common.scss | 237 ++--- src/scss/main.scss | 9 +- src/scss/variable.scss | 91 +- src/shim.d.ts | 62 +- src/stores/audio.ts | 519 +++++----- src/stores/index.ts | 4 +- src/stores/initStore.ts | 11 +- src/stores/player.ts | 447 ++++----- src/stores/user.ts | 180 ++-- src/sw.ts | 9 +- src/types/album.d.ts | 36 +- src/types/auth.d.ts | 120 +-- src/types/lyric.d.ts | 86 +- src/types/mv.d.ts | 58 +- src/types/singer.d.ts | 40 +- src/types/song.ts | 310 +++--- src/types/songlist.d.ts | 324 +++--- src/types/user.d.ts | 206 ++-- src/types/video.d.ts | 313 +++--- src/utils/apiSpecial.ts | 234 +++-- src/utils/auth.ts | 62 +- src/utils/battery.ts | 128 ++- src/utils/browser.ts | 239 +++-- src/utils/calc.ts | 57 +- src/utils/common.ts | 348 ++++--- src/utils/constants.ts | 28 +- src/utils/event/event.ts | 140 +-- src/utils/event/register.ts | 4 +- src/utils/index.ts | 18 +- src/utils/lyricParser.ts | 148 +-- src/utils/preference.ts | 189 ++-- src/utils/time.ts | 196 ++-- src/utils/timeSlice.ts | 109 +-- src/views/home/index.scss | 64 +- src/views/home/index.tsx | 111 ++- src/views/lyric-page/index.scss | 209 ++-- src/views/lyric-page/index.tsx | 214 ++-- src/webComponents/index.ts | 4 +- src/webComponents/textarea/index.tsx | 266 ++--- src/widgets/album-list/index.scss | 84 +- src/widgets/album-list/index.tsx | 394 ++++---- src/widgets/artist-list/index.scss | 39 +- src/widgets/artist-list/index.tsx | 143 ++- src/widgets/aside-route-list/index.scss | 58 +- src/widgets/aside-route-list/index.tsx | 146 +-- src/widgets/battery/index.scss | 14 +- src/widgets/battery/index.tsx | 99 +- src/widgets/common-renderer/index.scss | 10 +- src/widgets/common-renderer/index.tsx | 55 +- src/widgets/common-router-list/index.scss | 76 +- src/widgets/common-router-list/index.tsx | 96 +- src/widgets/follow-button/index.scss | 20 +- src/widgets/follow-button/index.tsx | 145 +-- src/widgets/header-setting/index.scss | 111 +-- src/widgets/header-setting/index.tsx | 158 +-- src/widgets/infinity-scroll/index.ts | 278 +++--- .../infinity-scroll/infinity-scroll.tsx | 160 ++- src/widgets/music-list/index.scss | 251 +++-- src/widgets/music-list/index.tsx | 472 +++++---- src/widgets/music-tiny-comp/index.scss | 247 +++-- src/widgets/music-tiny-comp/index.tsx | 639 ++++++------ src/widgets/mv-list/index.scss | 112 +-- src/widgets/mv-list/index.tsx | 309 +++--- src/widgets/progress-bar/index.scss | 122 +-- src/widgets/progress-bar/index.tsx | 478 ++++----- src/widgets/reply-textarea/index.scss | 21 +- src/widgets/reply-textarea/index.tsx | 73 +- src/widgets/route-pagination/index.scss | 16 +- src/widgets/route-pagination/index.tsx | 228 +++-- src/widgets/song-list/index.scss | 140 ++- src/widgets/song-list/index.tsx | 409 ++++---- src/widgets/song-table/index.scss | 119 ++- src/widgets/song-table/index.tsx | 311 +++--- src/widgets/song-table/yuan-table/index.scss | 56 +- src/widgets/song-table/yuan-table/index.tsx | 265 +++-- src/widgets/subscriber-list/index.scss | 28 +- src/widgets/subscriber-list/index.tsx | 222 ++--- src/widgets/user-login/index.scss | 301 +++--- src/widgets/user-login/index.tsx | 912 +++++++++-------- src/widgets/video-list/index.scss | 54 +- src/widgets/video-list/index.tsx | 194 ++-- src/widgets/yuan-button/index.scss | 56 +- src/widgets/yuan-button/index.tsx | 146 +-- tsconfig.json | 105 +- vercel.json | 12 +- 238 files changed, 17621 insertions(+), 17089 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 8171168..0753493 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,46 +1,46 @@ /** - * @introduction 详见: - * eslint rules: https://eslint.bootcss.com/docs/rules/ + * eslint rules: https://eslint.bootcss.com/docs/rules/ * eslint typescript rules: https://www.npmjs.com/package/@typescript-eslint/eslint-plugin + * + * @format + * @introduction 详见: */ + module.exports = { - "env": { - "browser": true, - "es2021": true - }, - "extends": [ - "eslint:recommended", - "plugin:vue/essential", - "plugin:@typescript-eslint/recommended" - ], - "parserOptions": { - "ecmaVersion": 12, - "parser": "@typescript-eslint/parser", - "sourceType": "module" - }, - "plugins": [ - "vue", - "@typescript-eslint" - ], - "rules": { - "no-alert": "off", - "no-unsafe-finally": "off", - "no-empty": "off", - "no-extra-boolean-cast": "off", - //是否禁用不必要的\转义字符 - "no-useless-escape": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unused-vars": "off", - //允许!非空断言 - "@typescript-eslint/no-non-null-assertion": "off", - //允许声明any类型 - "@typescript-eslint/no-explicit-any": "off", - //导出函数和类的公共类方法是否需要显式返回和参数类型 - "@typescript-eslint/explicit-module-boundary-types": "off", - //是否允许空接口(interface) - "@typescript-eslint/no-empty-interface": ["off"], - //是否允许有额外的分号(;),如{}块级作用域后就不需要; - "@typescript-eslint/no-extra-semi": ["warn"] - } + env: { + browser: true, + es2021: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:vue/essential', + 'plugin:@typescript-eslint/recommended', + ], + parserOptions: { + ecmaVersion: 12, + parser: '@typescript-eslint/parser', + sourceType: 'module', + }, + plugins: ['vue', '@typescript-eslint'], + rules: { + 'no-alert': 'off', + 'no-unsafe-finally': 'off', + 'no-empty': 'off', + 'no-extra-boolean-cast': 'off', + //是否禁用不必要的\转义字符 + 'no-useless-escape': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-unused-vars': 'off', + //允许!非空断言 + '@typescript-eslint/no-non-null-assertion': 'off', + //允许声明any类型 + '@typescript-eslint/no-explicit-any': 'off', + //导出函数和类的公共类方法是否需要显式返回和参数类型 + '@typescript-eslint/explicit-module-boundary-types': 'off', + //是否允许空接口(interface) + '@typescript-eslint/no-empty-interface': ['off'], + //是否允许有额外的分号(;),如{}块级作用域后就不需要; + '@typescript-eslint/no-extra-semi': ['warn'], + }, }; diff --git a/README.md b/README.md index 740afc9..9067087 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,21 @@ -捣鼓的一个biu涕仸音乐网站~~ 🤗🤗🤗🤗🤗 - -后端接口使用:[`Binaryify`](https://binaryify.github.io/NeteaseCloudMusicApi) + +捣鼓的一个 biu 涕仸音乐网站~~ 🤗🤗🤗🤗🤗 +后端接口使用:[`Binaryify`](https://binaryify.github.io/NeteaseCloudMusicApi) 目前还在开发中~~ -还是先看看几张app界面😊: +还是先看看几张 app 界面 😊: ![](./public/img/homepage.png) - - ![](./public/img/myPage.png) - - ![](./public/img/songlist.png) - - ![](./public/img/myPlaylist.png) - - ![](./public/img/singerDetail.png) - - ![](./public/img/lyricDetailPage.png) - diff --git a/docs/howler.md b/docs/howler.md index c3f67d7..27cd587 100644 --- a/docs/howler.md +++ b/docs/howler.md @@ -1,40 +1,38 @@ + + ### 一、描述 ------- +--- -howler.js(吼叫)是一个现代web音频库。它默认使用Web Audio技术,同时兼容HTML5 Audio方案。这使得JavaScript更容易处理音频,并且运行在所有浏览器平台上。 +howler.js(吼叫)是一个现代 web 音频库。它默认使用 Web Audio 技术,同时兼容 HTML5 Audio 方案。这使得 JavaScript 更容易处理音频,并且运行在所有浏览器平台上。 可在[howlerjs.com](`howlerjs.com`)上获取更多信息、在线延时和用户展示。 -关注howler.js的推特账号,并参与相关发展讨论:[@GoldFireStudios](https://twitter.com/goldfirestudios) - - +关注 howler.js 的推特账号,并参与相关发展讨论:[@GoldFireStudios](https://twitter.com/goldfirestudios) ### 二、特性 ------- +--- -- 满足所有音频需求的单一API -- 默认使用Web Audio API,并使用HTML5 Audio向后兼容 -- 处理跨环境(平台)的边缘情况和错误 +- 满足所有音频需求的单一 API +- 默认使用 Web Audio API,并使用 HTML5 Audio 向后兼容 +- 处理跨环境(平台)的边缘情况和错误 - 支持所有编解码器,实现全面跨浏览器支持 - 自动缓存,以提升性能 - 单独、分组或全局控制声音 - 一次播放多种声音 - 简单的声音精灵定义和播放 - 完全控制衰弱、速率、搜索、音量等 -- 轻松添加3D空间音效或立体声平移 +- 轻松添加 3D 空间音效或立体声平移 - 模块化 - 随心所以、易于扩展 -- 没有外部依赖,只有纯JavaScript -- 压缩后只有7Kb - - +- 没有外部依赖,只有纯 JavaScript +- 压缩后只有 7Kb ### 三、浏览器兼容性 ------- +--- -- 谷歌Chrome 7.0+ +- 谷歌 Chrome 7.0+ - IE 9.0+ - FireFox 4.0+ - Safari 5.1.4+ @@ -42,11 +40,9 @@ howler.js(吼叫)是一个现代web音频库。它默认使用Web Audio技 - Opera 12.0+ - Microsoft Edge - - ### 四、在线演示 ------- +--- - [Audio Player](https://howlerjs.com/#player) @@ -54,11 +50,9 @@ howler.js(吼叫)是一个现代web音频库。它默认使用Web Audio技 - [Spatial Audio](https://howlerjs.com/#spatial) - [Audio Sprites](https://howlerjs.com/#sprite) - - ### 五、使用文档 ------- +--- #### 目录 @@ -76,21 +70,19 @@ howler.js(吼叫)是一个现代web音频库。它默认使用Web Audio技 - 组合播放(Group Playback) - 移动端播放 - 杜比音效播放 -- Facebook即时游戏 +- Facebook 即时游戏 - 格式推荐 - 许可证 - - ### 六、快速开始 获取运行的几种方式: - 拷贝仓库:[`git clone https://github.com/goldfire/howler.js.git`](git clone https://github.com/goldfire/howler.js.git) -- npm安装:npm install howler -- yarn安装:yarn add howler -- bower安装:bower install howler -- 使用CDN:[`cdnjs`](https://cdnjs.com/libraries/howler) [`jsDelivr`](https://www.jsdelivr.com/projects/howler.js) +- npm 安装:npm install howler +- yarn 安装:yarn add howler +- bower 安装:bower install howler +- 使用 CDN:[`cdnjs`](https://cdnjs.com/libraries/howler) [`jsDelivr`](https://www.jsdelivr.com/projects/howler.js) 在浏览器中: @@ -105,7 +97,7 @@ howler.js(吼叫)是一个现代web音频库。它默认使用Web Audio技 作为项目依赖: -``` +``` import { Howl, Howler } from 'howler'; ``` @@ -115,15 +107,13 @@ const { Howl, Howler } = require('howler'); 包含的分发文件: -- howler:这是默认的、完全捆绑的源,包括howler.core和howler.spatial,这包含了howler附带的所有功能。 -- howler.core:这只包括旨在在Web AUdio和HTML Audio之间创建奇偶校验的核心功能,但它不包含任何空间/立体声音频功能。 -- howler.spatial:这是一个添加空间/立体声音频功能的插件。他需要howler.core才能运行,因为它只是howler.core的扩展组件。 - - +- howler:这是默认的、完全捆绑的源,包括 howler.core 和 howler.spatial,这包含了 howler 附带的所有功能。 +- howler.core:这只包括旨在在 Web AUdio 和 HTML Audio 之间创建奇偶校验的核心功能,但它不包含任何空间/立体声音频功能。 +- howler.spatial:这是一个添加空间/立体声音频功能的插件。他需要 howler.core 才能运行,因为它只是 howler.core 的扩展组件。 ### 七、案例 -最基本的,播放一个MP3: +最基本的,播放一个 MP3: ``` var sound = new Howl({ @@ -224,83 +214,79 @@ sound.play(); Howler.volume(0.5); ``` -可以在示例目录中找到更深入的示例(含有[`在线演示`]([examples directory](https://github.com/goldfire/howler.js/tree/master/examples).))。 - - +可以在示例目录中找到更深入的示例(含有[`在线演示`]([examples directory](https://github.com/goldfire/howler.js/tree/master/examples).))。 ### 八、核心 ------- +--- #### 配置选项 -| 属性 | 类型 | 默认值 | 含义 | -| ----------- | -------------- | ------ | ------------------------------------------------------------ | -| src | Array/String | [] | 加载的音频轨道来源(URL或base64)。这应该是按照优先顺序排列的,howler.js会自动加载第一个与当前浏览器兼容的。如果没有文件扩展名,则需使用format属性明确指定扩展名 | -| volume | Number | 1.0 | 指定音频轨道的声音,从0.0到1.0 | -| html5 | Boolean | false | 设置true强制使用HTML5 Audio。这应该用于大型音频文件,这样你就不必再播放之前等待完整文件下载和解码 | -| loop | Boolean | false | 设置true让声音自动从头自动播放 | -| preload | Boolean/String | true | 定义Howl时,自动下载音频文件。如果使用HTML5音频,你可以将其设置为'metadata',表示仅加载文件的元数据。(例如,无需下载整个文件,即可获取其duration值) | -| autoplay | Boolean | false | 设置true,音频加载好时,开始自动播放 | -| mute | Boolean | false | 设置true,让音频静音 | -| sprite | Object | {} | 定义一个音频片段。偏移量和持续时间以毫秒为单位。第三个可选参数可用于将片段设置为循环。生成兼容声音片段的一种简单方法是[audiosprite](https://github.com/tonistiigi/audiosprite) | -| rate | Number | 1.0 | 播放速率,范围:0.5-4.0 | -| pool | Number | 5 | 非活动声音池的大小。一旦声音停止或播放完毕,它们会被标记为结束并准备好清理。我们保留了一个池来回收利用,以提升性能。一般不需要修改。但要记住,当声音暂停时,它不会从池中删除,仍会被认为是活动的,以便后续恢复 | -| format | Array | [] | howler.js会自动从扩展(extension)中检测你的文件格式,但你也可以在提取无效的情况下手动指定格式(例如使用分隔SoundCloud声云流时) | -| xhr | Object | null | 当使用Web Audio时,howler.js使用一个XHR请求去加载音频文件。如果你需要发送自定义请求头,就可以设置HTTP方法或启用withCredentials,请将它们与此参数一起包含。每个都是可选的,method默认为GET,headers默认为null,withCredentials默认为false | -| onload | Function | | 当音频数据加载完时触发 | -| onloaderror | Function | | 当音频数据加载失败时触发。其第一个参数是音频的ID(如果存在的话),第二个则是错误消息(message)或代码(code)。错误代码在HTML规范总定义如下:
1表示资源请求过程中被用户代理终止;
2表示网络错误;
3表示资源解码错误;
4表示src提供的资源不支持 | -| onplayerror | Function | | 当音频不能播放时触发。第一个参数是音频的ID,第二个是错误消息/代码 | -| onplay | Function | | 当音频开始播放时触发。第一个参数是音频的ID | -| onend | Function | | 当音频播放结束时触发。第一个参数是音频的ID。
注意:如果是loop模式,那么每次播放完后都会触发。 | -| onpause | Function | | 当音频暂停时触发。第一个参数是音频的ID | -| onstop | Function | | 当音频被停止时触发。第一个参数是音频的ID | -| onmute | Function | | 当音频被静音或取消静音时触发。第一个参数是音频的ID | -| onvolume | Function | | 当音频音量变化时触发。第一个参数是音频的ID | -| onrate | Function | | 当音频播放速率变化时触发。第一个参数是音频的ID | -| onseek | Function | | 当音频跳转播放时触发。第一个参数是音频的ID | -| onfade | Function | | 当当前音频完成fading in/out时触发。第一个参数是音频的ID | -| onunlock | Function | | 当音频通过触摸/点击事件自动解锁时触发。 | - - +| 属性 | 类型 | 默认值 | 含义 | +| ----------- | -------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| src | Array/String | [] | 加载的音频轨道来源(URL 或 base64)。这应该是按照优先顺序排列的,howler.js 会自动加载第一个与当前浏览器兼容的。如果没有文件扩展名,则需使用 format 属性明确指定扩展名 | +| volume | Number | 1.0 | 指定音频轨道的声音,从 0.0 到 1.0 | +| html5 | Boolean | false | 设置 true 强制使用 HTML5 Audio。这应该用于大型音频文件,这样你就不必再播放之前等待完整文件下载和解码 | +| loop | Boolean | false | 设置 true 让声音自动从头自动播放 | +| preload | Boolean/String | true | 定义 Howl 时,自动下载音频文件。如果使用 HTML5 音频,你可以将其设置为'metadata',表示仅加载文件的元数据。(例如,无需下载整个文件,即可获取其 duration 值) | +| autoplay | Boolean | false | 设置 true,音频加载好时,开始自动播放 | +| mute | Boolean | false | 设置 true,让音频静音 | +| sprite | Object | {} | 定义一个音频片段。偏移量和持续时间以毫秒为单位。第三个可选参数可用于将片段设置为循环。生成兼容声音片段的一种简单方法是[audiosprite](https://github.com/tonistiigi/audiosprite) | +| rate | Number | 1.0 | 播放速率,范围:0.5-4.0 | +| pool | Number | 5 | 非活动声音池的大小。一旦声音停止或播放完毕,它们会被标记为结束并准备好清理。我们保留了一个池来回收利用,以提升性能。一般不需要修改。但要记住,当声音暂停时,它不会从池中删除,仍会被认为是活动的,以便后续恢复 | +| format | Array | [] | howler.js 会自动从扩展(extension)中检测你的文件格式,但你也可以在提取无效的情况下手动指定格式(例如使用分隔 SoundCloud 声云流时) | +| xhr | Object | null | 当使用 Web Audio 时,howler.js 使用一个 XHR 请求去加载音频文件。如果你需要发送自定义请求头,就可以设置 HTTP 方法或启用 withCredentials,请将它们与此参数一起包含。每个都是可选的,method 默认为 GET,headers 默认为 null,withCredentials 默认为 false | +| onload | Function | | 当音频数据加载完时触发 | +| onloaderror | Function | | 当音频数据加载失败时触发。其第一个参数是音频的 ID(如果存在的话),第二个则是错误消息(message)或代码(code)。错误代码在 HTML 规范总定义如下:
1 表示资源请求过程中被用户代理终止;
2 表示网络错误;
3 表示资源解码错误;
4 表示 src 提供的资源不支持 | +| onplayerror | Function | | 当音频不能播放时触发。第一个参数是音频的 ID,第二个是错误消息/代码 | +| onplay | Function | | 当音频开始播放时触发。第一个参数是音频的 ID | +| onend | Function | | 当音频播放结束时触发。第一个参数是音频的 ID。
注意:如果是 loop 模式,那么每次播放完后都会触发。 | +| onpause | Function | | 当音频暂停时触发。第一个参数是音频的 ID | +| onstop | Function | | 当音频被停止时触发。第一个参数是音频的 ID | +| onmute | Function | | 当音频被静音或取消静音时触发。第一个参数是音频的 ID | +| onvolume | Function | | 当音频音量变化时触发。第一个参数是音频的 ID | +| onrate | Function | | 当音频播放速率变化时触发。第一个参数是音频的 ID | +| onseek | Function | | 当音频跳转播放时触发。第一个参数是音频的 ID | +| onfade | Function | | 当当前音频完成 fading in/out 时触发。第一个参数是音频的 ID | +| onunlock | Function | | 当音频通过触摸/点击事件自动解锁时触发。 | #### 方法 ##### 1、play: -开始播放音频。返回要与其它方法结合使用的音频ID。唯一不能链接的方法。 +开始播放音频。返回要与其它方法结合使用的音频 ID。唯一不能链接的方法。 -- sprite/id:字符串或数字,可选。接受一个可以是sprite或音频ID的参数。 - - 如果为sprite,则根据sprite的定义播放新声音。 - - 如果为音频ID,则将播放之前的声音(例如暂停后的)。但若为已从pool中去除的音频ID,则不生效。 +- sprite/id:字符串或数字,可选。接受一个可以是 sprite 或音频 ID 的参数。 + - 如果为 sprite,则根据 sprite 的定义播放新声音。 + - 如果为音频 ID,则将播放之前的声音(例如暂停后的)。但若为已从 pool 中去除的音频 ID,则不生效。 ##### 2、pause: -暂停音频或保存着播放seek的组。 +暂停音频或保存着播放 seek 的组。 -- id:数字类型,可选。音频ID。如果没传,所有音频组都将被暂停 +- id:数字类型,可选。音频 ID。如果没传,所有音频组都将被暂停 ##### 3、stop: -停止音频播放,并重置seek播放时间位置到0。 +停止音频播放,并重置 seek 播放时间位置到 0。 -- id:数字类型,可选。音频ID。如果没传,所有音频组都将被停止 +- id:数字类型,可选。音频 ID。如果没传,所有音频组都将被停止 -**注意:此时只是暂停播放,期间可修改seek、volume等,再次触发play方法即可播放。** +**注意:此时只是暂停播放,期间可修改 seek、volume 等,再次触发 play 方法即可播放。** ##### 4、mute: 让音频静音,但不暂停播放 -- volume:数字类型,可选。声音范围0-1 -- id:数字类型,可选。音频ID。如果没传,组中所有声音都将静音了 +- volume:数字类型,可选。声音范围 0-1 +- id:数字类型,可选。音频 ID。如果没传,组中所有声音都将静音了 ##### 5、volume: 获取或设置音频或整组音频的音量。这个方法的两个参数都是可选的。 - volume:数字类型。音量范围:0.0-1.0 -- id:数字类型。音频ID。如果没传,组中所有声音的音量都相对于它们自己的音量发生了变化 +- id:数字类型。音频 ID。如果没传,组中所有声音的音量都相对于它们自己的音量发生了变化 ##### 6、fade: @@ -309,47 +295,46 @@ Howler.volume(0.5); - from:数字类型。淡出开始音量 - to:数字类型。淡出结束音量 - duration:数字类型。淡出持续时间(毫秒单位) -- id:数字类型。音频ID。如果没传,所有音频组都将被fade +- id:数字类型。音频 ID。如果没传,所有音频组都将被 fade ##### 7、rate: 获取或设置音频播放速率。这个方法的两个参数都是可选的。 -- rate:数字类型。值范围:0.5-4.0,正常值为1 -- id:数字类型。音频ID。如果没传,所有音频组的播放速率都将被修改 +- rate:数字类型。值范围:0.5-4.0,正常值为 1 +- id:数字类型。音频 ID。如果没传,所有音频组的播放速率都将被修改 ##### 8、seek: 获取或设置音频播放的位置(多少毫秒)。这个方法的两个参数都是可选的。 - seek:数字类型。要播放的时间 -- id:数字类型。音频ID,如果没传,音频组的第一个音频将被修改 +- id:数字类型。音频 ID,如果没传,音频组的第一个音频将被修改 -**注意:必须在onload触发后,设置才有效** +**注意:必须在 onload 触发后,设置才有效** ##### 9、loop: 获取或设置是否让音频或音频组循环播放。这个方法的两个参数都是可选的。 - loop:布尔类型。设置循环还是不循环,这是一个问题 -- id:数字类型。音频ID。 如果没传,音频组所有音频都将设为更新后的loop值 +- id:数字类型。音频 ID。 如果没传,音频组所有音频都将设为更新后的 loop 值 ##### 10、state: -检查Howl的加载状态,返回值:unloaded、loading和loaded三者之一。 +检查 Howl 的加载状态,返回值:unloaded、loading 和 loaded 三者之一。 ##### 11、playing: -检测一个音频是否正在播放。如果没有一个音频ID传入,请检查Howl组中是否有任何音频正在播放。 - -- id:数字类型,可选。要检测的音频ID。 +检测一个音频是否正在播放。如果没有一个音频 ID 传入,请检查 Howl 组中是否有任何音频正在播放。 +- id:数字类型,可选。要检测的音频 ID。 ##### 12、duration: -获取音频源的总时长。会返回0,直到load事件触发后。 +获取音频源的总时长。会返回 0,直到 load 事件触发后。 -- id:数字类型,可选。要检测的音频ID。传入一个ID将返回在此实例上播放的精灵的持续时间。否则返回完整的音频媒体总时长。 +- id:数字类型,可选。要检测的音频 ID。传入一个 ID 将返回在此实例上播放的精灵的持续时间。否则返回完整的音频媒体总时长。 ##### 13、on: @@ -361,51 +346,47 @@ Howler.volume(0.5); ##### 14、once: -和on一样,只会触发一次。 +和 on 一样,只会触发一次。 ##### 15、load: -这是默认调用的,但如果将preload设为false,则必须调用此load方法,然后才能播放音频。 +这是默认调用的,但如果将 preload 设为 false,则必须调用此 load 方法,然后才能播放音频。 ##### 16、unload: -卸载并销毁Howl对象。这将立即停止附加的所有音频,并从缓存中删除它们。**同时触发onstop事件,后销毁所有事件** - - +卸载并销毁 Howl 对象。这将立即停止附加的所有音频,并从缓存中删除它们。**同时触发 onstop 事件,后销毁所有事件** #### 全局配置 -| 属性 | 类型 | 默认值 | 含义 | -| ------------- | ------- | ----------------- | ------------------------------------------------------------ | -| usingWebAudio | Boolean | | 如果Web Audio可用,该值为true | -| noAudio | Boolean | | 如果没有音频可用,该值为true | -| autoUnlock | Boolean | true | 自动尝试在移动设备和桌面端启用音频 | -| html5PoolSize | Number | 10 | 每个HTML5 Audio对象都必须单独解锁,因此我们保留一个全局解锁节点池,以便在所有Howl实例之间共享。该池在对此用户交互时创建,并设置该属性值为它的大小 | -| autoSuspend | Boolean | true | 在30秒不活动后自动暂停Web Audio AudioContext,以减少资源占用。新播放时自动恢复。设置此属性为false,以禁用这一行为。 | -| ctx | Boolean | Web Audio Only | 暴露Web Audio API的AudioContext上下文对象 | -| masterMain | Boolean | Web Audio Only | 暴露Web Audio的master GainNode。这对于编写插件或高级用法很有用 | - - +| 属性 | 类型 | 默认值 | 含义 | +| ------------- | ------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| usingWebAudio | Boolean | | 如果 Web Audio 可用,该值为 true | +| noAudio | Boolean | | 如果没有音频可用,该值为 true | +| autoUnlock | Boolean | true | 自动尝试在移动设备和桌面端启用音频 | +| html5PoolSize | Number | 10 | 每个 HTML5 Audio 对象都必须单独解锁,因此我们保留一个全局解锁节点池,以便在所有 Howl 实例之间共享。该池在对此用户交互时创建,并设置该属性值为它的大小 | +| autoSuspend | Boolean | true | 在 30 秒不活动后自动暂停 Web Audio AudioContext,以减少资源占用。新播放时自动恢复。设置此属性为 false,以禁用这一行为。 | +| ctx | Boolean | Web Audio Only | 暴露 Web Audio API 的 AudioContext 上下文对象 | +| masterMain | Boolean | Web Audio Only | 暴露 Web Audio 的 master GainNode。这对于编写插件或高级用法很有用 | #### 全局方法 -以下方法用于全局修改所有音频,并从Howler对象上调用。 +以下方法用于全局修改所有音频,并从 Howler 对象上调用。 ##### 1、mute: -- muted:true表示静音,false不静音。 +- muted:true 表示静音,false 不静音。 ##### 2、stop: -停止所有音频播放,并重置播放时间为0。 +停止所有音频播放,并重置播放时间为 0。 ##### 3、volume: -获取或设置volume音量。 +获取或设置 volume 音量。 ##### 4、codecs: -检测支持的音频编解码器。如果编解码器支持当前浏览器,返回true。 +检测支持的音频编解码器。如果编解码器支持当前浏览器,返回 true。 - ext:字符串类型文件拓展名。可选值:mp3、mpeg、opus、ogg、oga、wav、aac、caf、m4a、m4b、mp4、weba、webm、dolby、flac。 @@ -413,8 +394,6 @@ Howler.volume(0.5); 前文已述。 - - ### 九、插件:Spatial #### 配置选项 @@ -429,47 +408,29 @@ Howler.volume(0.5); | onpos | Function | | | | onorientation | Function | | | - - #### 方法 ##### 1、stereo: - - ##### 2、pos: - - ##### 3、orientation: - - ##### 4、pannerAttr: - - -#### 全局方法 +#### 全局方法 ##### 1、stereo: - - ##### 2、pos: - - ##### 3、orientation: - - ##### 4、pannerAttr: - - ### 十、组播放 -每个新的Howl实例也是一个组。你可以播放来自Howl的多个声音,并单独作为一组控制它们。例如,下面播放来自一个精灵(片段)的两个声音,一起改变它们的声音,然后同时暂停它们。 +每个新的 Howl 实例也是一个组。你可以播放来自 Howl 的多个声音,并单独作为一组控制它们。例如,下面播放来自一个精灵(片段)的两个声音,一起改变它们的声音,然后同时暂停它们。 ``` var sound = new Howl({ @@ -490,17 +451,15 @@ sound.volume(0.5) sound.pause(); ``` +### 十一、移动端/Chrome 播放 - -### 十一、移动端/Chrome播放 - -默认情况下,移动端浏览器和Chrome/Safari上的音频被锁定,直到用户交互才会播放音频,然后页面会话的其它部分正常播放(Apple文档)。howler.js的默认行为是尝试通过在第一个touchend事件上播放空缓冲区来静默解锁音频播放。可以通过如下代码禁用此行为。 +默认情况下,移动端浏览器和 Chrome/Safari 上的音频被锁定,直到用户交互才会播放音频,然后页面会话的其它部分正常播放(Apple 文档)。howler.js 的默认行为是尝试通过在第一个 touchend 事件上播放空缓冲区来静默解锁音频播放。可以通过如下代码禁用此行为。 ``` Howler.autoUnlock = false; //不自动解锁 ``` -如果你尝试在页面加载后自动播放音频,你可以监听playerror事件,然后等待unlock事件触发,并尝试再次播放这段音频。 +如果你尝试在页面加载后自动播放音频,你可以监听 playerror 事件,然后等待 unlock 事件触发,并尝试再次播放这段音频。 ``` var sound = new Howl({ @@ -513,11 +472,9 @@ var sound = new Howl({ }) ``` - - ### 十二、杜比音频播放 -全面支持杜比音频格式的播放(目前支持Edge和Safari)。但是,你必须指定要加载的文件时杜比音频文件,因为它位于mp4容器中。 +全面支持杜比音频格式的播放(目前支持 Edge 和 Safari)。但是,你必须指定要加载的文件时杜比音频文件,因为它位于 mp4 容器中。 ``` var dolbySound = new Howl({ @@ -526,43 +483,14 @@ var dolbySound = new Howl({ }) ``` - - ### 十三、格式推荐 -Howler.js支持各种具有不同浏览器支持的音频编解码器(mp3、opus、ogg、wav、aac、m4a、m4b、mp4、webm等),但如果你想要完整的浏览器覆盖,你仍然需要至少使用其中两个。如果您的目标是在小文件大小和高质量之间取得最佳平衡,根据广泛的生产测试,你最好的选择是默认使用webm,并回退到mp3。webm具有几乎完整的浏览器覆盖范围,具有压缩和质量的完美结合。你好需要IE的mp3回退。 +Howler.js 支持各种具有不同浏览器支持的音频编解码器(mp3、opus、ogg、wav、aac、m4a、m4b、mp4、webm 等),但如果你想要完整的浏览器覆盖,你仍然需要至少使用其中两个。如果您的目标是在小文件大小和高质量之间取得最佳平衡,根据广泛的生产测试,你最好的选择是默认使用 webm,并回退到 mp3。webm 具有几乎完整的浏览器覆盖范围,具有压缩和质量的完美结合。你好需要 IE 的 mp3 回退。 -请务必记住,howler.js从你的源数组中选择第一个兼容的音频。因此,如果你希望在mp3之前使用webm,则需要按此顺序放置源。 +请务必记住,howler.js 从你的源数组中选择第一个兼容的音频。因此,如果你希望在 mp3 之前使用 webm,则需要按此顺序放置源。 -如果你希望您的webm文件可以再FireFox中查找,请务必使用提示元素对它们进行编码。一种方法是在ffmpeg中使用破折号标志: +如果你希望您的 webm 文件可以再 FireFox 中查找,请务必使用提示元素对它们进行编码。一种方法是在 ffmpeg 中使用破折号标志: ``` ffmpeg -i sound1.wav -dash 1 sound1.webm ``` - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/global.d.ts b/global.d.ts index edc6ea4..18a40fe 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,36 +1,38 @@ +/** @format */ + interface document { - documentMode: number; + documentMode: number; } interface BeforeInstallPromptEvent extends Event { - platforms: string[]; - timeStamp: number; - type: "beforeinstallprompt"; - prompt: () => void; - userChoice: Promise<{ - outcome: 'dismissed' | 'accepted' | string; - platform: string; - }> + platforms: string[]; + timeStamp: number; + type: 'beforeinstallprompt'; + prompt: () => void; + userChoice: Promise<{ + outcome: 'dismissed' | 'accepted' | string; + platform: string; + }>; } interface WindowEventMap { - beforeinstallprompt: BeforeInstallPromptEvent + beforeinstallprompt: BeforeInstallPromptEvent; } interface Event { - detail: any; + detail: any; } interface Navigator { - getBattery: () => Promise; - battery: PlainObject; - mozBattery: PlainObject; - webkitBattery: PlainObject; + getBattery: () => Promise; + battery: PlainObject; + mozBattery: PlainObject; + webkitBattery: PlainObject; } interface window { - webkitAudioContext: AudioContext; - requestIdleCallback: (cb: CommonFunction, options?: { timeout: number }) => void; + webkitAudioContext: AudioContext; + requestIdleCallback: (cb: CommonFunction, options?: { timeout: number }) => void; } declare type CommonFunction = (...args: any[]) => any; @@ -38,55 +40,53 @@ declare type CommonFunction = (...args: any[]) => any; declare type PlainObject = Record; //让数组也继承纯对象特性 -interface Array extends PlainObject { } +interface Array extends PlainObject {} declare type Writeable = { - -readonly [K in keyof T]: T[K]; + -readonly [K in keyof T]: T[K]; }; declare type FuncParamsType any> = T extends ( - ...args: infer P + ...args: infer P ) => any - ? P - : T; + ? P + : T; declare type CtorParamsType any> = - T extends new (...args: infer P) => any ? P : never; + T extends new (...args: infer P) => any ? P : never; /** * UnionToIntersection<{ a: string } | { b: string }> = { a: string } & { b: string } */ declare type UnionToIntersection = (T extends any ? (x: T) => any : never) extends ( - x: infer U + x: infer U, ) => any - ? U - : never; + ? U + : never; /** * LastUnion<'a' | 'b'> = 'b' */ declare type LastUnion = UnionToIntersection< - T extends any - ? (x: T) => any - : never + T extends any ? (x: T) => any : never > extends (x: infer L) => any - ? L - : never; + ? L + : never; /** * UnionToTuple<'a' | 'b'> = ['a', 'b'] */ declare type UnionToTuple> = [T] extends [never] - ? [] - : [...UnionToTuple>, Last]; - -declare module "*.css"; -declare module "*.scss"; -declare module "*.json"; -declare module "*.png"; -declare module "*.jpg"; -declare module "*.webp"; -declare module "*.svg"; + ? [] + : [...UnionToTuple>, Last]; + +declare module '*.css'; +declare module '*.scss'; +declare module '*.json'; +declare module '*.png'; +declare module '*.jpg'; +declare module '*.webp'; +declare module '*.svg'; // declare module "colorthief" { // class ColorThief { diff --git a/index.html b/index.html index 3efd3a2..a05137a 100644 --- a/index.html +++ b/index.html @@ -1,31 +1,39 @@ + + - - - - - - - RefrainMusic - - - - - - - - - - -
- - - \ No newline at end of file + + + + + + + RefrainMusic + + + + + + + + + + +
+ + + diff --git a/postcss.config.js b/postcss.config.js index 5b86d14..ac6eeee 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,9 +1,11 @@ -const PostcssPxtorem = require("postcss-pxtorem"); +/** @format */ + +const PostcssPxtorem = require('postcss-pxtorem'); module.exports = { - plugins: [ - PostcssPxtorem({ - propList: ["*"], - }), - ], + plugins: [ + PostcssPxtorem({ + propList: ['*'], + }), + ], }; diff --git a/prettier.config.js b/prettier.config.js index 8da67df..5e443d3 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,54 +1,46 @@ -module.exports = { - tabWidth: 2, +/** @format */ - //一行最大宽度,90个字符 - printWidth: 70, +const os = require('os'); - //是否制表符替代空格控制缩进 - // useTabs: true, +const conf = { + //是否制表符替代空格控制缩进 + useTabs: true, - //每一行后始终打印分号 - semi: true, + tabWidth: 2, - //字符串使用单引号 - singleQuote: true, + //一行最大宽度,90个字符 + printWidth: 90, - //jsx中也使用单引号 - // jsxSingleQuote: true, + //每一行后始终打印分号 + semi: true, - quoteProps: "as-needed", + endOfLine: os.platform() === 'win32' ? 'crlf' : 'lf', - //文字和括号之间打印空格 - bracketSpacing: true, + //字符串使用单引号 + singleQuote: true, - //使用(x)=>x形式,而非x=>x - arrowParens: "always", + quoteProps: 'as-needed', - requirePragma: true, + //文字和括号之间打印空格 + bracketSpacing: true, - //尾随逗号处理 - //详见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas#trailing_commas_in_functions - trailingComma: 'all', + //使用(x)=>x形式,而非x=>x + arrowParens: 'always', - insertPragma: true, + //尾随逗号处理 + //详见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas#trailing_commas_in_functions + trailingComma: 'all', - //对html等daima进行换行 - //详见:https://www.prettier.cn/docs/options.html#html-whitespace-sensitivity - htmlWhitespaceSensitivity: "strict", + insertPragma: true, - //对.vue文件进行代码缩进 - vueIndentScriptAndStyle: true, + embeddedLanguageFormatting: 'off', - // endOfLine: "lf", + //对html等daima进行换行 + //详见:https://www.prettier.cn/docs/options.html#html-whitespace-sensitivity + htmlWhitespaceSensitivity: 'strict', - embeddedLanguageFormatting: "off", - - // overrides: [ - // { - // files: '*.tsx', - // options: { - // parser: 'typescript' - // } - // } - // ] + //对.vue文件进行代码缩进 + vueIndentScriptAndStyle: true, }; + +module.exports = conf; diff --git a/public/pwa/site.webmanifest b/public/pwa/site.webmanifest index fa99de7..9591150 100644 --- a/public/pwa/site.webmanifest +++ b/public/pwa/site.webmanifest @@ -1,19 +1,19 @@ { - "name": "", - "short_name": "", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" } diff --git a/scripts/lint-staged.mjs b/scripts/lint-staged.mjs index 8c8b7fe..306be74 100644 --- a/scripts/lint-staged.mjs +++ b/scripts/lint-staged.mjs @@ -1,31 +1,33 @@ -/** +/** * 对commit的文件进行一系列检测 + * + * @format */ import lintStaged from 'lint-staged'; -const formatCmd = ["npm run prettier"]; +const formatCmd = ['npm run prettier']; -const checkCmd = ["npm run check"]; +const checkCmd = ['npm run check']; -const globCmdMap = { - "*.{ts,tsx,html,scss,css,js,mjs,jsx,md}": formatCmd, - "*.{ts,tsx,js,mjs}": checkCmd, - "*.d.ts": checkCmd -} +const globCmdMap = { + '*.{ts,tsx,html,scss,css,js,mjs,jsx,md}': formatCmd, + '*.{ts,tsx,js,mjs}': checkCmd, + '*.d.ts': checkCmd, +}; -const config = { - concurrent: false, - // configPath: '../lint-staged.config.js', - config: globCmdMap, - cwd: process.cwd(), - maxArgLength: null, -} +const config = { + concurrent: false, + // configPath: '../lint-staged.config.js', + config: globCmdMap, + cwd: process.cwd(), + maxArgLength: null, +}; try { - const success = await lintStaged(config); - console.log(success ? 'lint-staged检验成功!' : 'lint-staged检验失败!') + const success = await lintStaged(config); + console.log(success ? 'lint-staged检验成功!' : 'lint-staged检验失败!'); } catch (e) { - // Failed to load configuration - console.error(e) -} \ No newline at end of file + // Failed to load configuration + console.error(e); +} diff --git a/scripts/vite/constant.ts b/scripts/vite/constant.ts index afba4f2..e4ac248 100644 --- a/scripts/vite/constant.ts +++ b/scripts/vite/constant.ts @@ -1,19 +1,21 @@ +/** @format */ + import proxyConfig from './proxy'; -import path from 'path'; +import path from 'path'; + +const extend = Object.assign; -const extend = Object.assign - const pathResolve = (dir: string) => path.resolve(`${__dirname}`, '../', dir); const hostname = '127.0.0.1'; const port = 2021; const viteConstants: ViteConstant = { - pathResolve, - hostname, - port, - extend, - proxy: proxyConfig -} + pathResolve, + hostname, + port, + extend, + proxy: proxyConfig, +}; -export default viteConstants; \ No newline at end of file +export default viteConstants; diff --git a/scripts/vite/manualChunk.ts b/scripts/vite/manualChunk.ts index 6b81624..66bd09d 100644 --- a/scripts/vite/manualChunk.ts +++ b/scripts/vite/manualChunk.ts @@ -1,27 +1,27 @@ -import { BuildOptions } from "vite"; +/** @format */ + +import { BuildOptions } from 'vite'; export type ManualChunksFn = UnionToTuple< - NonNullable< - NonNullable['output'] - > + NonNullable['output']> >[0]['manualChunks']; const chunkNameMapWithUrlKey: PlainObject = { - 'naive-ui': 'navie', - 'swiper': 'swiper', -} + 'naive-ui': 'navie', + swiper: 'swiper', +}; const NODE_MODULES = 'node_modules'; const manualChunksHandler: ManualChunksFn = (url, { getModuleIds, getModuleInfo }) => { - if (!url.includes(NODE_MODULES)) { - return void 0; - } - for (const urlKey in chunkNameMapWithUrlKey) { - if (url.includes(`${NODE_MODULES}/${urlKey}`)) { - return chunkNameMapWithUrlKey[urlKey]; - } - } -} + if (!url.includes(NODE_MODULES)) { + return void 0; + } + for (const urlKey in chunkNameMapWithUrlKey) { + if (url.includes(`${NODE_MODULES}/${urlKey}`)) { + return chunkNameMapWithUrlKey[urlKey]; + } + } +}; -export default manualChunksHandler; \ No newline at end of file +export default manualChunksHandler; diff --git a/scripts/vite/proxy.ts b/scripts/vite/proxy.ts index 6a50bcf..7e1aef4 100644 --- a/scripts/vite/proxy.ts +++ b/scripts/vite/proxy.ts @@ -1,10 +1,11 @@ -const proxy: ProxyConfig = { - '/api': { - target: 'http://localhost:3000/', - changeOrigin: true, - rewrite: path => path.replace(/^\/api/, '') - }, +/** @format */ -} +const proxy: ProxyConfig = { + '/api': { + target: 'http://localhost:3000/', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + }, +}; export default proxy; diff --git a/scripts/vite/pwa.ts b/scripts/vite/pwa.ts index 37c5f14..1c23907 100644 --- a/scripts/vite/pwa.ts +++ b/scripts/vite/pwa.ts @@ -1,37 +1,37 @@ +/** @format */ + import { VitePWA } from 'vite-plugin-pwa'; export default VitePWA({ - strategies: 'injectManifest', - srcDir: 'src', - filename: 'sw.ts', - base: "/", - injectManifest: { - maximumFileSizeToCacheInBytes: 3000000 - }, - workbox: { - cleanupOutdatedCaches: true, - }, - registerType: 'autoUpdate', - includeAssets: [ - 'pwa/**.{png,svg}' - ], - manifest: { - name: 'RefrainMusic', - short_name: 'RefrainMusic', - theme_color: '#ffffff', - background_color: '#ffffff', - icons: [ - { - "src": "/pwa/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/pwa/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - display: 'standalone', - description: 'A high appearance level music player based on netease Cloud API!', - }, -}) \ No newline at end of file + strategies: 'injectManifest', + srcDir: 'src', + filename: 'sw.ts', + base: '/', + injectManifest: { + maximumFileSizeToCacheInBytes: 3000000, + }, + workbox: { + cleanupOutdatedCaches: true, + }, + registerType: 'autoUpdate', + includeAssets: ['pwa/**.{png,svg}'], + manifest: { + name: 'RefrainMusic', + short_name: 'RefrainMusic', + theme_color: '#ffffff', + background_color: '#ffffff', + icons: [ + { + src: '/pwa/android-chrome-192x192.png', + sizes: '192x192', + type: 'image/png', + }, + { + src: '/pwa/android-chrome-512x512.png', + sizes: '512x512', + type: 'image/png', + }, + ], + display: 'standalone', + description: 'A high appearance level music player based on netease Cloud API!', + }, +}); diff --git a/scripts/vite/vite.config.ts b/scripts/vite/vite.config.ts index 6516748..9826212 100644 --- a/scripts/vite/vite.config.ts +++ b/scripts/vite/vite.config.ts @@ -1,4 +1,12 @@ -import { defineConfig, UserConfig, UserConfigFn, ServerOptions, RollupCommonJSOptions } from 'vite'; +/** @format */ + +import { + defineConfig, + UserConfig, + UserConfigFn, + ServerOptions, + RollupCommonJSOptions, +} from 'vite'; //vue 3 jsx synxtax import VueJsx from '@vitejs/plugin-vue-jsx'; @@ -7,223 +15,212 @@ import VueJsx from '@vitejs/plugin-vue-jsx'; // import styleImport from 'vite-plugin-style-import' //插件:解析.svg成内联元素 -import svgLoader from 'vite-svg-loader' +import svgLoader from 'vite-svg-loader'; -//插件:解析.vue单文件组件 -import vue from '@vitejs/plugin-vue' +//插件:解析.vue单文件组件 +import vue from '@vitejs/plugin-vue'; //插件:导入jpg、png、webp、gif和svg图片的插件 -import image from '@rollup/plugin-image' +import image from '@rollup/plugin-image'; //插件:解析.md文件 // import Markdown from 'vite-plugin-md'; //导入配置文件 import viteConstant from './constant'; -const { extend } = viteConstant +const { extend } = viteConstant; -import path from 'path' +import path from 'path'; import manualChunksHandler from './manualChunk'; import VitePWAPluginConfig from './pwa'; -const pathResolve = path.resolve +const pathResolve = path.resolve; const baseConfig: UserConfig = { - - //配置运行环境,即import.meta.env.MODE(client.js中的window.process.env.NODE_ENV = __MODE__的值) - //可选值:development、production - mode: process.env.NODE_ENV, - - //项目根目录,用于查找vite配置文件(vite服务器运行的虚拟目录) 。默认:process.cwd(),npm run执行命令的目录 - root: process.cwd(), - - //需要支持的静态资源格式(默认支持字体、图片和媒体文件格式) - assetsInclude: [ - /\.(png|jpeg|gif|jpg|svg|webp|avif)$/, - /\.(mp4|webm|m3u8|avi)$/, - /\.(ttf|woff|woff2|eot|otf)$/, - ], - //以便import().then()动态导入支持更多格式转换成js语法。在2.0.0-beta.3之后已移除 - // transformInclude: [ - // /\.(js|ts|jsx|tsx|json|vue|scss)$/ - // ], - - resolve: { - - //别名 - alias: [ - { find: '@', replacement: pathResolve('src') }, - { find: '@api', replacement: pathResolve('src/api') }, - { find: '@router', replacement: pathResolve('src/router') }, - { find: '@type', replacement: pathResolve('src/types') }, - { find: '@components', replacement: pathResolve('src/components') }, - { find: '@scss', replacement: pathResolve('src/scss') }, - { find: '@views', replacement: pathResolve('src/views') }, - { find: '@request', replacement: pathResolve('src/request') }, - { find: '@utils', replacement: pathResolve('src/utils') }, - { find: '@assets', replacement: pathResolve('src/assets') }, - { find: '@stores', replacement: pathResolve('src/stores') }, - { find: '@use', replacement: pathResolve('src/use') }, - { find: '@database', replacement: pathResolve('src/database') }, - { find: '@widgets', replacement: pathResolve('src/widgets') }, - ], - - extensions: ['.vue', '.ts', '.js', '.js', '.json', '.tsx', 'jsx'] - - }, - - json: { - //是否支持从json文件中按名导入 - // namedExports: false, - //为true时,json文件将转为export default JSON.parse('...'),比转成对象字面量性能更好,尤其是json文件比较大时 - //开启后,会禁用namedExports按名导入 - // stringify: true, - }, - - esbuild: { - minifyWhitespace: true, - minifyIdentifiers: true, - minifySyntax: true, - charset: 'utf8', - treeShaking: true, - }, - - //插件 - plugins: [ - vue({ - template: { - compilerOptions: { - // 将所有带native-的标签名都视为自定义元素 - isCustomElement: (tag) => { - return tag.startsWith('custom-') - }, - } - } - }), //.vue文件解析插件 - image(), //图片导入解析插件 - // Markdown(), //.md文件解析插件 - svgLoader(), //svg图片解析成内联代码 - VueJsx({ - transformOn: true, - }), - VitePWAPluginConfig,//pwa - ], - - //优化依赖 - optimizeDeps: { - - esbuildOptions: { - keepNames: true, - }, - - //需要强制预打包的依赖 - // include: [ - // ], - // exclude: [], - //vite服务器打开时自动运行pre-bounding预打包 - // auto: true, - //vite默认自会pre-bounding纯js文件,该选项允许砸门使用相关插件预打包其他类型文件,如.vue - //同样,这些插件还得写在前面的plugins选项中,以便支持生产环境。 - //注意:已废除 - // plugins: [ - // vue(), - // ], - } -} + //配置运行环境,即import.meta.env.MODE(client.js中的window.process.env.NODE_ENV = __MODE__的值) + //可选值:development、production + mode: process.env.NODE_ENV, + + //项目根目录,用于查找vite配置文件(vite服务器运行的虚拟目录) 。默认:process.cwd(),npm run执行命令的目录 + root: process.cwd(), + + //需要支持的静态资源格式(默认支持字体、图片和媒体文件格式) + assetsInclude: [ + /\.(png|jpeg|gif|jpg|svg|webp|avif)$/, + /\.(mp4|webm|m3u8|avi)$/, + /\.(ttf|woff|woff2|eot|otf)$/, + ], + //以便import().then()动态导入支持更多格式转换成js语法。在2.0.0-beta.3之后已移除 + // transformInclude: [ + // /\.(js|ts|jsx|tsx|json|vue|scss)$/ + // ], + + resolve: { + //别名 + alias: [ + { find: '@', replacement: pathResolve('src') }, + { find: '@api', replacement: pathResolve('src/api') }, + { find: '@router', replacement: pathResolve('src/router') }, + { find: '@type', replacement: pathResolve('src/types') }, + { find: '@components', replacement: pathResolve('src/components') }, + { find: '@scss', replacement: pathResolve('src/scss') }, + { find: '@views', replacement: pathResolve('src/views') }, + { find: '@request', replacement: pathResolve('src/request') }, + { find: '@utils', replacement: pathResolve('src/utils') }, + { find: '@assets', replacement: pathResolve('src/assets') }, + { find: '@stores', replacement: pathResolve('src/stores') }, + { find: '@use', replacement: pathResolve('src/use') }, + { find: '@database', replacement: pathResolve('src/database') }, + { find: '@widgets', replacement: pathResolve('src/widgets') }, + ], + + extensions: ['.vue', '.ts', '.js', '.js', '.json', '.tsx', 'jsx'], + }, + + json: { + //是否支持从json文件中按名导入 + // namedExports: false, + //为true时,json文件将转为export default JSON.parse('...'),比转成对象字面量性能更好,尤其是json文件比较大时 + //开启后,会禁用namedExports按名导入 + // stringify: true, + }, + + esbuild: { + minifyWhitespace: true, + minifyIdentifiers: true, + minifySyntax: true, + charset: 'utf8', + treeShaking: true, + }, + + //插件 + plugins: [ + vue({ + template: { + compilerOptions: { + // 将所有带native-的标签名都视为自定义元素 + isCustomElement: (tag) => { + return tag.startsWith('custom-'); + }, + }, + }, + }), //.vue文件解析插件 + image(), //图片导入解析插件 + // Markdown(), //.md文件解析插件 + svgLoader(), //svg图片解析成内联代码 + VueJsx({ + transformOn: true, + }), + VitePWAPluginConfig, //pwa + ], + + //优化依赖 + optimizeDeps: { + esbuildOptions: { + keepNames: true, + }, + + //需要强制预打包的依赖 + // include: [ + // ], + // exclude: [], + //vite服务器打开时自动运行pre-bounding预打包 + // auto: true, + //vite默认自会pre-bounding纯js文件,该选项允许砸门使用相关插件预打包其他类型文件,如.vue + //同样,这些插件还得写在前面的plugins选项中,以便支持生产环境。 + //注意:已废除 + // plugins: [ + // vue(), + // ], + }, +}; const devConfig = extend({}, baseConfig, { - - //在开发或生产环境下,网页运行的虚拟基础路径 - base: './', - - //日志记录方式:info(默认)、log、error和silent - logLevel: 'info', - - //为false时可避免vite清屏而错过在终端中打印某些关键信息 - clearScreen: false, - optimizeDeps: { - //强制预构建打包依赖包 - force: true - }, - server: { - host: viteConstant.hostname, - port: viteConstant.port, - //为true时,若端口已占用则直接退出,而非直接尝试下一个可用端口 - strictPort: false, - //值为字符串时,会被作为URL的路径名 - open: false, - watch: true, - cors: true, - proxy: viteConstant.proxy, - //热更新:借助websocket实现 - hmr: { - overlay: true, //是否覆盖报错,若为false,则不会显示错误提示界面 - }, - fs: { - strict: false - }, - } - + //在开发或生产环境下,网页运行的虚拟基础路径 + base: './', + + //日志记录方式:info(默认)、log、error和silent + logLevel: 'info', + + //为false时可避免vite清屏而错过在终端中打印某些关键信息 + clearScreen: false, + optimizeDeps: { + //强制预构建打包依赖包 + force: true, + }, + server: { + host: viteConstant.hostname, + port: viteConstant.port, + //为true时,若端口已占用则直接退出,而非直接尝试下一个可用端口 + strictPort: false, + //值为字符串时,会被作为URL的路径名 + open: false, + watch: true, + cors: true, + proxy: viteConstant.proxy, + //热更新:借助websocket实现 + hmr: { + overlay: true, //是否覆盖报错,若为false,则不会显示错误提示界面 + }, + fs: { + strict: false, + }, + }, } as UserConfig); const prodConfig = extend({}, baseConfig, { - - build: { - //打包后的代码所支持的运行环境。 - //可选值:es2020(默认值)、es2015(最低值)之类的js版本号;chrome58、safari11之类的浏览器版本号;node12.19.0版本号 - //写法:1、'es2020,chrome58,firefox57,node12.19.0';2、['es2020', 'chrome58'] - target: 'chrome70', - //打包后html输出的主目录,默认dist。相对于project root项目根目录确定 - outDir: 'dist', - //打包后输出的静态资源目录(包含css、js),默认assets,相对于outDir目录确定 - assetsDir: 'assets', - //静态资源导入大小限制,默认为4096(4kb) - assetsInlineLimit: 3072, - sourcemap: false, - //rollup配置选项,将会与vite内部的默认配置选项合并 - rollupOptions: { - output: { - manualChunks: manualChunksHandler - } - }, - //代码压缩。 - //可选值:true/false——是否允许压缩代码;terser——默认值,压缩体积更小但速度稍慢;esbuild,速度快但体积稍大 - minify: 'terser', - //build.minify为terser的附加配置选项 - terserOptions: { - compress: { - drop_console: true, - drop_debugger: true - } - }, - //动态导入的polyfill,默认true,如果build.target为esnext则不会使用polyfill,已废弃 - // polyfillDynamicImport: true, - //css代码分离,默认会在不同异步chunk块加载时插入(即css懒加载),否则所有css背会抽取到一个css文件中 - cssCodeSplit: true, - - //css优化选项,依赖于clean-css包,已废弃 - // cleanCssOptions: { - // format: 'keep-breaks', //选项:keep-breaks(保持换行)、beautify - // compatibility: 'ie11', - // }, - // 启用/禁用 brotli 压缩大小报告。压缩大型输出文件可能会很慢,因此禁用该功能可能会提高大型项目的构建性能 - brotliSize: false, - //开发插件库时所能用到 - // lib: { - - // } - } - - + build: { + //打包后的代码所支持的运行环境。 + //可选值:es2020(默认值)、es2015(最低值)之类的js版本号;chrome58、safari11之类的浏览器版本号;node12.19.0版本号 + //写法:1、'es2020,chrome58,firefox57,node12.19.0';2、['es2020', 'chrome58'] + target: 'chrome70', + //打包后html输出的主目录,默认dist。相对于project root项目根目录确定 + outDir: 'dist', + //打包后输出的静态资源目录(包含css、js),默认assets,相对于outDir目录确定 + assetsDir: 'assets', + //静态资源导入大小限制,默认为4096(4kb) + assetsInlineLimit: 3072, + sourcemap: false, + //rollup配置选项,将会与vite内部的默认配置选项合并 + rollupOptions: { + output: { + manualChunks: manualChunksHandler, + }, + }, + //代码压缩。 + //可选值:true/false——是否允许压缩代码;terser——默认值,压缩体积更小但速度稍慢;esbuild,速度快但体积稍大 + minify: 'terser', + //build.minify为terser的附加配置选项 + terserOptions: { + compress: { + drop_console: true, + drop_debugger: true, + }, + }, + //动态导入的polyfill,默认true,如果build.target为esnext则不会使用polyfill,已废弃 + // polyfillDynamicImport: true, + //css代码分离,默认会在不同异步chunk块加载时插入(即css懒加载),否则所有css背会抽取到一个css文件中 + cssCodeSplit: true, + + //css优化选项,依赖于clean-css包,已废弃 + // cleanCssOptions: { + // format: 'keep-breaks', //选项:keep-breaks(保持换行)、beautify + // compatibility: 'ie11', + // }, + // 启用/禁用 brotli 压缩大小报告。压缩大型输出文件可能会很慢,因此禁用该功能可能会提高大型项目的构建性能 + brotliSize: false, + //开发插件库时所能用到 + // lib: { + + // } + }, } as UserConfig); - type ViteConfEnvProp = { - command: 'serve' | 'build'; - mode: string; -} + command: 'serve' | 'build'; + mode: string; +}; -const viteConfFn: UserConfigFn = (envObj: ViteConfEnvProp) => ( - envObj.mode === 'development' ? devConfig : prodConfig -); +const viteConfFn: UserConfigFn = (envObj: ViteConfEnvProp) => + envObj.mode === 'development' ? devConfig : prodConfig; -export default defineConfig(viteConfFn); \ No newline at end of file +export default defineConfig(viteConfFn); diff --git a/scripts/vite/viteConf.d.ts b/scripts/vite/viteConf.d.ts index 4b701d6..7706bd5 100644 --- a/scripts/vite/viteConf.d.ts +++ b/scripts/vite/viteConf.d.ts @@ -1,17 +1,19 @@ +/** @format */ + declare type ProxyConfig = { - [key: string]: { - target: string; - changeOrigin: boolean; - rewrite?: (path: string) => string; - } -} + [key: string]: { + target: string; + changeOrigin: boolean; + rewrite?: (path: string) => string; + }; +}; declare type ViteConstant = { - pathResolve: (dir: string) => string; - hostname: string; - port: string | number; - extend: typeof Object.assign; - proxy: ProxyConfig -} + pathResolve: (dir: string) => string; + hostname: string; + port: string | number; + extend: typeof Object.assign; + proxy: ProxyConfig; +}; -declare module '@rollup/plugin-image' +declare module '@rollup/plugin-image'; diff --git a/src/App.scss b/src/App.scss index dae89d9..cd246ea 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,31 +1,33 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; html, body { - width: 100vw; - height: 100vh; - max-width: 2600px; - min-width: 980px; - min-height: 600px; - margin: 0 auto; - overflow: hidden; - background-color: $baseBackgroud; - font: 15px / 1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, cursive; + width: 100vw; + height: 100vh; + max-width: 2600px; + min-width: 980px; + min-height: 600px; + margin: 0 auto; + overflow: hidden; + background-color: $baseBackgroud; + font: 15px / 1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, + Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, cursive; } body { - contain: size style paint; + contain: size style paint; } #Yuan-Player { - @include noSelect; - width: 100%; - height: 100%; - overflow: hidden; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - .theme-layer { - height: 100%; - } + @include noSelect; + width: 100%; + height: 100%; + overflow: hidden; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + .theme-layer { + height: 100%; + } } diff --git a/src/App.tsx b/src/App.tsx index 5ceb7c8..be0d242 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,178 +1,187 @@ -import { computed, defineComponent, h, watch } from "vue"; -import { RouterView, useRouter } from "vue-router"; +/** @format */ + +import { computed, defineComponent, h, watch } from 'vue'; +import { RouterView, useRouter } from 'vue-router'; import { - GlobalThemeOverrides, useMessage, NxButton, - NConfigProvider, NLoadingBarProvider, NMessageProvider, NAvatar, - useLoadingBar, NNotificationProvider, useNotification, NDialogProvider, -} from "naive-ui"; + GlobalThemeOverrides, + useMessage, + NxButton, + NConfigProvider, + NLoadingBarProvider, + NMessageProvider, + NAvatar, + useLoadingBar, + NNotificationProvider, + useNotification, + NDialogProvider, +} from 'naive-ui'; import LyricPage from '@views/lyric-page'; -import { theme } from "./stores/player"; -import { messageBus } from "./utils/event/register"; +import { theme } from './stores/player'; +import { messageBus } from './utils/event/register'; import refrainPic from '@assets/img/refrain.png'; -import onBeforeInstallPrompt from "./hooks/onBeforeInstallPrompt"; -import { UNICODE_CHAR } from "./utils"; -import useUserStore from "./stores/user"; -import "./App.scss"; +import onBeforeInstallPrompt from './hooks/onBeforeInstallPrompt'; +import { UNICODE_CHAR } from './utils'; +import useUserStore from './stores/user'; +import './App.scss'; export const LogicLayer = defineComponent({ - name: 'LogicLayer', - setup() { - - const message = useMessage(); - const loading = useLoadingBar(); - messageBus.on('startLoading', () => loading.start()); - messageBus.on('finishLoading', () => loading.finish()); - messageBus.on('errorLoading', () => loading.error()); - messageBus.on('destroyAllMessage', () => message.destroyAll()); - messageBus.on('successMessage', (...args: FuncParamsType) => message.success(...args)); - messageBus.on('warnMessage', (...args: FuncParamsType) => message.warning(...args)); - messageBus.on('errorMessage', (...args: FuncParamsType) => message.error(...args)); - - const notification = useNotification(); - - onBeforeInstallPrompt((addToHomeScreen) => { - setTimeout(() => { - const n = notification.create({ - title: '添加RefrainMusic到主屏幕', - content: '添加后,便可从桌面图标、开始菜单等处打开应用', - meta: `${UNICODE_CHAR.smile.repeat(3)}`, - duration: 30000, - avatar: () => - h(NAvatar, { - size: 'small', - round: true, - src: refrainPic - }), - action: () => - h( - NxButton, - { - text: true, - type: 'success', - onClick: () => { - addToHomeScreen(); - n.destroy(); - } - }, - { - default: () => '好滴~~' - } - ), - }) - }, 5000); - }); - - const userStore = useUserStore(); - const router = useRouter(); - - watch(() => userStore.isLogin, (isLogin) => { - if (!isLogin) { - router.replace({ - path: '/musichall/featrued' - }) - } - }); - - return () => ( - <> - - - - ) - } -}) + name: 'LogicLayer', + setup() { + const message = useMessage(); + const loading = useLoadingBar(); + messageBus.on('startLoading', () => loading.start()); + messageBus.on('finishLoading', () => loading.finish()); + messageBus.on('errorLoading', () => loading.error()); + messageBus.on('destroyAllMessage', () => message.destroyAll()); + messageBus.on('successMessage', (...args: FuncParamsType) => + message.success(...args), + ); + messageBus.on('warnMessage', (...args: FuncParamsType) => + message.warning(...args), + ); + messageBus.on('errorMessage', (...args: FuncParamsType) => + message.error(...args), + ); + + const notification = useNotification(); + + onBeforeInstallPrompt((addToHomeScreen) => { + setTimeout(() => { + const n = notification.create({ + title: '添加RefrainMusic到主屏幕', + content: '添加后,便可从桌面图标、开始菜单等处打开应用', + meta: `${UNICODE_CHAR.smile.repeat(3)}`, + duration: 30000, + avatar: () => + h(NAvatar, { + size: 'small', + round: true, + src: refrainPic, + }), + action: () => + h( + NxButton, + { + text: true, + type: 'success', + onClick: () => { + addToHomeScreen(); + n.destroy(); + }, + }, + { + default: () => '好滴~~', + }, + ), + }); + }, 5000); + }); + + const userStore = useUserStore(); + const router = useRouter(); + + watch( + () => userStore.isLogin, + (isLogin) => { + if (!isLogin) { + router.replace({ + path: '/musichall/featrued', + }); + } + }, + ); + + return () => ( + <> + + + + ); + }, +}); export default defineComponent({ - name: "YuanPlayer", - setup(props, context) { - - const themeLayerStyle = computed(() => { - return `--theme:${theme.value};` - }); - - const NaiveThemeConfig = computed(() => { - const globalTheme = theme.value; - return { - Input: { - borderHoverWarning: globalTheme, - borderFocus: globalTheme, - borderHover: globalTheme - }, - Radio: { - buttonBorderColorActive: globalTheme, - buttonBorderColorHover: globalTheme, - buttonTextColorActive: globalTheme, - buttonTextColorHover: globalTheme, - buttonBoxShadowFocus: 'none', - fontSizeSmall: '12px', - }, - LoadingBar: { - colorLoading: globalTheme, - }, - BackTop: { - iconColorHover: globalTheme, - }, - Button: { - - textColorFocusPrimary: globalTheme, - textColorDisabledPrimary: globalTheme, - - textColorGhostPrimary: globalTheme, - textColorGhostHoverPrimary: globalTheme, - textColorGhostPressedPrimary: globalTheme, - textColorGhostFocusPrimary: globalTheme, - textColorGhostDisabledPrimary: globalTheme, - borderHoverPrimary: globalTheme, - - borderPrimary: globalTheme, - borderDisabledPrimary: globalTheme, - - }, - Pagination: { - - itemTextColorHover: globalTheme, - itemTextColorPressed: globalTheme, - itemTextColorActive: globalTheme, - itemBorder: globalTheme, - itemBorderHover: globalTheme, - itemBorderActive: globalTheme, - itemBorderDisabled: globalTheme, - itemBorderPressed: globalTheme, - - }, - InternalSelection: { - borderActive: globalTheme, - borderHover: globalTheme, - borderFocus: globalTheme, - }, - InternalSelectMenu: { - optionTextColorActive: globalTheme, - }, - Select: { - - }, - Dropdown: { - - } - } - }); - - return () => { - return ( -
- - - - - - - - - - - -
- ); - }; - }, + name: 'YuanPlayer', + setup(props, context) { + const themeLayerStyle = computed(() => { + return `--theme:${theme.value};`; + }); + + const NaiveThemeConfig = computed(() => { + const globalTheme = theme.value; + return { + Input: { + borderHoverWarning: globalTheme, + borderFocus: globalTheme, + borderHover: globalTheme, + }, + Radio: { + buttonBorderColorActive: globalTheme, + buttonBorderColorHover: globalTheme, + buttonTextColorActive: globalTheme, + buttonTextColorHover: globalTheme, + buttonBoxShadowFocus: 'none', + fontSizeSmall: '12px', + }, + LoadingBar: { + colorLoading: globalTheme, + }, + BackTop: { + iconColorHover: globalTheme, + }, + Button: { + textColorFocusPrimary: globalTheme, + textColorDisabledPrimary: globalTheme, + + textColorGhostPrimary: globalTheme, + textColorGhostHoverPrimary: globalTheme, + textColorGhostPressedPrimary: globalTheme, + textColorGhostFocusPrimary: globalTheme, + textColorGhostDisabledPrimary: globalTheme, + borderHoverPrimary: globalTheme, + + borderPrimary: globalTheme, + borderDisabledPrimary: globalTheme, + }, + Pagination: { + itemTextColorHover: globalTheme, + itemTextColorPressed: globalTheme, + itemTextColorActive: globalTheme, + itemBorder: globalTheme, + itemBorderHover: globalTheme, + itemBorderActive: globalTheme, + itemBorderDisabled: globalTheme, + itemBorderPressed: globalTheme, + }, + InternalSelection: { + borderActive: globalTheme, + borderHover: globalTheme, + borderFocus: globalTheme, + }, + InternalSelectMenu: { + optionTextColorActive: globalTheme, + }, + Select: {}, + Dropdown: {}, + }; + }); + + return () => { + return ( +
+ + + + + + + + + + + +
+ ); + }; + }, }); diff --git a/src/api/auth.ts b/src/api/auth.ts index e6458ff..4d42d5b 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,5 +1,7 @@ -import { anfrage, anfrageWithLoading } from "@/request"; -import { filterUselessKey, generateFormData } from "@utils/index"; +/** @format */ + +import { anfrage, anfrageWithLoading } from '@/request'; +import { filterUselessKey, generateFormData } from '@utils/index'; /** * 手机登录 @@ -14,19 +16,19 @@ import { filterUselessKey, generateFormData } from "@utils/index"; * @param {string=} params.md5_password */ export const loginWithPhone = (params: { - phone: string; - password: string; - countrycode?: string; - md5_password?: string; + phone: string; + password: string; + countrycode?: string; + md5_password?: string; }) => { - return anfrageWithLoading({ - url: "/login/cellphone", - method: "post", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/login/cellphone', + method: 'post', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); }; /** @@ -40,18 +42,18 @@ export const loginWithPhone = (params: { * @param {string} params.md5_password */ export function loginWithEmail(params: { - email: string; - password: string; - md5_password?: string; + email: string; + password: string; + md5_password?: string; }) { - return anfrageWithLoading({ - url: "/login", - method: "post", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/login', + method: 'post', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -60,10 +62,10 @@ export function loginWithEmail(params: { * - 调用例子 : /login/refresh */ export function refreshCookie() { - return anfrageWithLoading({ - url: "/login/refresh", - method: "post", - }); + return anfrageWithLoading({ + url: '/login/refresh', + method: 'post', + }); } /** @@ -71,87 +73,82 @@ export function refreshCookie() { * 说明 : 调用此接口 , 可退出登录 */ export function logout() { - return anfrageWithLoading({ - url: "/logout", - method: "post", - }); + return anfrageWithLoading({ + url: '/logout', + method: 'post', + }); } /** * 登录状态 */ export function loginStatus() { - return anfrageWithLoading({ - url: "/login/status", - method: 'post', - params: { - timestamp: new Date().valueOf(), - } - }); + return anfrageWithLoading({ + url: '/login/status', + method: 'post', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 获取用户等级信息 - * @returns + * @returns * @introduction * 说明 : 登录后调用此接口 , 可以获取用户等级信息, * 包含当前登录天数,听歌次数,下一等级需要的登录天数和听歌次数,当前等级进度, * 对应 https://music.163.com/#/user/level */ export function getMyLevelInfo() { - return anfrageWithLoading({ - url: "/user/level", - }) + return anfrageWithLoading({ + url: '/user/level', + }); } /** * 二维码key生成接口 - * @returns + * @returns */ export function getQrCodeKey() { - return anfrage({ - url: '/login/qr/key', - params: { - timestamp: new Date().valueOf(), - } - }) + return anfrage({ + url: '/login/qr/key', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 获取登录二维码图片 - * @param params - * @returns + * @param params + * @returns */ -export function getQrCodeImgInfo(params: { - key: string; - qrimg?: boolean; -}) { - const { key, qrimg = true } = params; - return anfrageWithLoading({ - url: '/login/qr/create', - params: { - key, - qrimg, - timestamp: new Date().valueOf(), - } - }) +export function getQrCodeImgInfo(params: { key: string; qrimg?: boolean }) { + const { key, qrimg = true } = params; + return anfrageWithLoading({ + url: '/login/qr/create', + params: { + key, + qrimg, + timestamp: new Date().valueOf(), + }, + }); } /** * 检测二维码扫码状态 - * @param params - * @returns + * @param params + * @returns * @introduction * 轮询此接口可获取二维码扫码状态,800为二维码过期,801为等待扫码,802为待确认,803为授权登录成功(803状态码下会返回cookies) */ -export function getQrCodeScanStatus(params: { - key: string; -}) { - return anfrage({ - url: '/login/qr/check', - params: { - ...params, - timestamp: new Date().valueOf(), - } - }) -} \ No newline at end of file +export function getQrCodeScanStatus(params: { key: string }) { + return anfrage({ + url: '/login/qr/check', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); +} diff --git a/src/api/broadcast.ts b/src/api/broadcast.ts index ee6e877..f52eb73 100644 --- a/src/api/broadcast.ts +++ b/src/api/broadcast.ts @@ -1,5 +1,7 @@ -import { anfrageWithLoading } from "@/request"; -import { filterUselessKey } from "@utils/index"; +/** @format */ + +import { anfrageWithLoading } from '@/request'; +import { filterUselessKey } from '@utils/index'; /** * 推荐电台 @@ -7,13 +9,13 @@ import { filterUselessKey } from "@utils/index"; * @returns */ export function djRecomment() { - return anfrageWithLoading({ - url: "/personalized/djprogram", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/personalized/djprogram', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** @@ -22,13 +24,13 @@ export function djRecomment() { * @returns */ export function djBanner() { - return anfrageWithLoading({ - url: "/dj/banner", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/dj/banner', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** @@ -37,14 +39,14 @@ export function djBanner() { * @returns */ export function djPersonalRecommend(params: { limit?: number | string }) { - return anfrageWithLoading({ - url: "/dj/personalize/recommend", - method: "get", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/dj/personalize/recommend', + method: 'get', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } /** @@ -52,19 +54,16 @@ export function djPersonalRecommend(params: { limit?: number | string }) { * @param params * @returns */ -export function djHot(params: { - limit?: number | string; - offset?: number | string; -}) { - const { limit = 30, offset = 0 } = params; - return anfrageWithLoading({ - url: "/dj/hot", - method: "get", - params: { - limit, - offset: +offset * +limit, - }, - }); +export function djHot(params: { limit?: number | string; offset?: number | string }) { + const { limit = 30, offset = 0 } = params; + return anfrageWithLoading({ + url: '/dj/hot', + method: 'get', + params: { + limit, + offset: +offset * +limit, + }, + }); } /** @@ -73,18 +72,18 @@ export function djHot(params: { * @returns */ export function djProgramTopList(params: { - limit?: number | string; - offset?: number | string; + limit?: number | string; + offset?: number | string; }) { - const { limit = 100, offset = 0 } = params; - return anfrageWithLoading({ - url: "/dj/program/toplist", - method: "get", - params: { - limit, - offset: +offset * +limit, - }, - }); + const { limit = 100, offset = 0 } = params; + return anfrageWithLoading({ + url: '/dj/program/toplist', + method: 'get', + params: { + limit, + offset: +offset * +limit, + }, + }); } /** @@ -93,14 +92,14 @@ export function djProgramTopList(params: { * @returns */ export function djProgramTopListDay(params: { limit?: number | string }) { - const { limit = 100 } = params; - return anfrageWithLoading({ - url: "/dj/program/toplist/hours", - method: "get", - params: { - limit, - }, - }); + const { limit = 100 } = params; + return anfrageWithLoading({ + url: '/dj/program/toplist/hours', + method: 'get', + params: { + limit, + }, + }); } /** @@ -109,20 +108,20 @@ export function djProgramTopListDay(params: { limit?: number | string }) { * @returns */ export function djHotOrNewTopList(params: { - limit?: number | string; - offset?: number | string; - type?: number | string; + limit?: number | string; + offset?: number | string; + type?: number | string; }) { - const { limit = 100, offset = 0, type } = params; - return anfrageWithLoading({ - url: "/dj/toplist", - method: "get", - params: filterUselessKey({ - type, - limit, - offset: +limit * +offset, - }), - }); + const { limit = 100, offset = 0, type } = params; + return anfrageWithLoading({ + url: '/dj/toplist', + method: 'get', + params: filterUselessKey({ + type, + limit, + offset: +limit * +offset, + }), + }); } /** @@ -131,164 +130,164 @@ export function djHotOrNewTopList(params: { * @returns */ export function djCateHot(params: { - limit?: number | string; - offset?: number | string; - cateId?: string | number; + limit?: number | string; + offset?: number | string; + cateId?: string | number; }) { - const { limit = 30, offset = 0, cateId } = params; - return anfrageWithLoading({ - url: "/dj/radio/hot", - method: "get", - params: filterUselessKey({ - cateId, - limit, - offset: +limit * +offset, - }), - }); + const { limit = 30, offset = 0, cateId } = params; + return anfrageWithLoading({ + url: '/dj/radio/hot', + method: 'get', + params: filterUselessKey({ + cateId, + limit, + offset: +limit * +offset, + }), + }); } /** * 电台推荐 (登录后) */ export function djRecommend() { - return anfrageWithLoading({ - url: "/dj/recommend", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/dj/recommend', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 电台 - 分类 */ export function djCateList() { - return anfrageWithLoading({ - url: "/dj/catelist", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/dj/catelist', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 电台 - 分类推荐 */ export function djRecommenCate(params: { type: number | string }) { - const { type } = params; - return anfrageWithLoading({ - url: "/dj/recommend/type", - method: "get", - params: { - type, - timestamp: new Date().valueOf(), - }, - }); + const { type } = params; + return anfrageWithLoading({ + url: '/dj/recommend/type', + method: 'get', + params: { + type, + timestamp: new Date().valueOf(), + }, + }); } /** * 电台 - 订阅 */ export function djSubscribe(params: { rid: number | string }) { - const { rid } = params; - return anfrageWithLoading({ - url: "/dj/sub", - method: "get", - params: { - rid, - timestamp: new Date().valueOf(), - }, - }); + const { rid } = params; + return anfrageWithLoading({ + url: '/dj/sub', + method: 'get', + params: { + rid, + timestamp: new Date().valueOf(), + }, + }); } /** * 电台 - 订阅列表 */ export function djSubList() { - return anfrageWithLoading({ - url: "/dj/sublist", - method: "get", - params: { - timestamp: new Date().valueOf(), - } - }); + return anfrageWithLoading({ + url: '/dj/sublist', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 电台 - 详情 */ export function djDetail(params: { rid: number | string }) { - return anfrageWithLoading({ - url: "/dj/detail", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/dj/detail', + method: 'get', + params, + }); } /** * 电台 - 非热门类型 */ export function djCateExcludehot() { - return anfrageWithLoading({ - url: "/dj/category/excludehot", - method: "get", - }); + return anfrageWithLoading({ + url: '/dj/category/excludehot', + method: 'get', + }); } /** * 电台 - 推荐类型 */ export function djCateRecommend() { - return anfrageWithLoading({ - url: "/dj/category/recommend", - method: "get", - }); + return anfrageWithLoading({ + url: '/dj/category/recommend', + method: 'get', + }); } /** * 电台 - 今日优选 */ export function djTodayPrefer() { - return anfrageWithLoading({ - url: "/dj/today/perfered", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/dj/today/perfered', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 电台 - 节目 */ export function djProgram(params: { - rid: number | string; - limit?: number | string; - offset?: number | string; - asc?: boolean; + rid: number | string; + limit?: number | string; + offset?: number | string; + asc?: boolean; }) { - const { rid, limit = 30, offset = 0, asc } = params; - return anfrageWithLoading({ - url: "/dj/program", - method: "get", - params: filterUselessKey({ - rid, - limit, - offset: +offset * +limit, - asc, - }), - }); + const { rid, limit = 30, offset = 0, asc } = params; + return anfrageWithLoading({ + url: '/dj/program', + method: 'get', + params: filterUselessKey({ + rid, + limit, + offset: +offset * +limit, + asc, + }), + }); } /** * 电台 - 节目详情 */ export function djProgramDetail(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/dj/program/detail", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/dj/program/detail', + method: 'get', + params, + }); } diff --git a/src/api/homeInfo.ts b/src/api/homeInfo.ts index c482c7d..37105c4 100644 --- a/src/api/homeInfo.ts +++ b/src/api/homeInfo.ts @@ -1,34 +1,37 @@ +/** @format */ -import { anfrageWithLoading } from "@/request"; -import { EMPTY_OBJ, filterUselessKey } from "@utils/index"; +import { anfrageWithLoading } from '@/request'; +import { EMPTY_OBJ, filterUselessKey } from '@utils/index'; /** * 首页-发现-圆形图标入口列表 - * @returns + * @returns * @introduction * 可获取APP首页圆形图标入口列表 */ export function getHomepageDragonBall() { - return anfrageWithLoading({ - url: '/homepage/dragon/ball', - }) + return anfrageWithLoading({ + url: '/homepage/dragon/ball', + }); } /** * 首页-发现 * @param params * refresh: 是否刷新数据,默认为false - * cursor: 上一条数据返回的cursor - * @returns - * @introduction + * cursor: 上一条数据返回的cursor + * @returns + * @introduction * 可获取APP首页信息 */ -export function getHomepageFindings(params: { - refresh?: boolean; - cursor?: any; -} = EMPTY_OBJ) { - return anfrageWithLoading({ - url: '/homepage/block/page', - params: filterUselessKey(params) - }) -} \ No newline at end of file +export function getHomepageFindings( + params: { + refresh?: boolean; + cursor?: any; + } = EMPTY_OBJ, +) { + return anfrageWithLoading({ + url: '/homepage/block/page', + params: filterUselessKey(params), + }); +} diff --git a/src/api/music.ts b/src/api/music.ts index cb38f58..ccf4ef6 100644 --- a/src/api/music.ts +++ b/src/api/music.ts @@ -1,7 +1,9 @@ -import { anfrage, anfrageWithLoading } from "@/request"; -import { filterUselessKey, is } from "@utils/index"; -import { SongInfo, SongsDetail, SongUrlInfo } from "@type/song"; -import { CommentType } from "@/dependency/enum"; +/** @format */ + +import { anfrage, anfrageWithLoading } from '@/request'; +import { filterUselessKey, is } from '@utils/index'; +import { SongInfo, SongsDetail, SongUrlInfo } from '@type/song'; +import { CommentType } from '@/dependency/enum'; /** * 获取音乐 url @@ -10,21 +12,16 @@ import { CommentType } from "@/dependency/enum"; * !!!未登录状态返回试听片段(返回字段包含被截取的正常歌曲的开始时间和结束时间) * @param {string} id - 音乐的 id,例如 id=405998841,33894312 */ -export function getMusic( - params: { - id: number | string; - br?: number | string; - }, -) { - const queryParams = filterUselessKey({ ...params }); - return anfrageWithLoading({ - url: "/song/url", - method: "get", - params: { - ...queryParams, - timestamp: new Date().valueOf(), - }, - }); +export function getMusic(params: { id: number | string; br?: number | string }) { + const queryParams = filterUselessKey({ ...params }); + return anfrageWithLoading({ + url: '/song/url', + method: 'get', + params: { + ...queryParams, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -38,15 +35,12 @@ export function getMusic( * 其他类推 * */ -export function checkMusic(params: { - id: number | string; - br?: number | string; -}) { - return anfrageWithLoading({ - url: "/check/music", - method: "get", - params: filterUselessKey(params), - }); +export function checkMusic(params: { id: number | string; br?: number | string }) { + return anfrageWithLoading({ + url: '/check/music', + method: 'get', + params: filterUselessKey(params), + }); } /** @@ -55,11 +49,11 @@ export function checkMusic(params: { * @returns */ export function getLyric(params: { id: string | number }) { - return anfrageWithLoading({ - url: "/lyric", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/lyric', + method: 'get', + params, + }); } /** @@ -68,14 +62,14 @@ export function getLyric(params: { id: string | number }) { * @returns */ export function getNewExpressMusic(params: { type: number | string }) { - return anfrageWithLoading({ - url: "/top/song", - method: "get", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/top/song', + method: 'get', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -84,75 +78,79 @@ export function getNewExpressMusic(params: { type: number | string }) { * @returns */ export function getMusicComment(params: { - id: number | string; - limit?: number | string; - offset?: number | string; - before?: number | string; + id: number | string; + limit?: number | string; + offset?: number | string; + before?: number | string; }) { - const { limit = 20, offset = 0, before, id } = params; - return anfrageWithLoading({ - url: "/comment/music", - method: "get", - params: filterUselessKey({ - limit, - id, - before, - offset: +limit * +offset, - timestamp: new Date().valueOf() - }), - }); + const { limit = 20, offset = 0, before, id } = params; + return anfrageWithLoading({ + url: '/comment/music', + method: 'get', + params: filterUselessKey({ + limit, + id, + before, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }), + }); } /** - * 歌曲详情 + * 歌曲详情 */ export type GetMusicDetailReturnType = { - songs: SongInfo[]; + songs: SongInfo[]; }; -export function getMusicDetail({ids}: { ids: string | string[] }):Promise { - return anfrageWithLoading({ - url: "/song/detail", - method: "get", - params: { - ids: is.array(ids) ? ids.join(',') : ids, - }, - }); +export function getMusicDetail({ + ids, +}: { + ids: string | string[]; +}): Promise { + return anfrageWithLoading({ + url: '/song/detail', + method: 'get', + params: { + ids: is.array(ids) ? ids.join(',') : ids, + }, + }); } /** - * 获取相似音乐 + * 获取相似音乐 */ export function getMusicSimilar(params: { id: string | number }) { - return anfrageWithLoading({ - url: "/simi/song", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/simi/song', + method: 'get', + params, + }); } /** * 获取每日推荐歌曲 */ export function musicRecommend() { - return anfrageWithLoading({ - url: "/recommend/songs", - method: "get", - }) + return anfrageWithLoading({ + url: '/recommend/songs', + method: 'get', + }); } /** * 推荐新音乐 */ export function newMusicRecommend(params: { limit?: number | string }) { - const { limit = 10 } = params; - return anfrageWithLoading({ - url: "/personalized/newsong", - method: "get", - params: { - limit, - timestamp: new Date().valueOf - }, - }) + const { limit = 10 } = params; + return anfrageWithLoading({ + url: '/personalized/newsong', + method: 'get', + params: { + limit, + timestamp: new Date().valueOf, + }, + }); } /** @@ -165,28 +163,33 @@ export function newMusicRecommend(params: { limit?: number | string }) { * t: 1 | 0; * type: CommentType; * })} {id, cid, t, type} - * @return {*} + * @return {*} */ -export function musicCommentLike({id, cid, t, type}: { - // 资源id,如歌曲、mv等 - id: string | number; - // 评论id - cid: string | number; - // 1为点赞、0为取消点赞 - t: 1 | 0; - // 资源类型 - type: CommentType; +export function musicCommentLike({ + id, + cid, + t, + type, +}: { + // 资源id,如歌曲、mv等 + id: string | number; + // 评论id + cid: string | number; + // 1为点赞、0为取消点赞 + t: 1 | 0; + // 资源类型 + type: CommentType; }) { - const actualParams:PlainObject = { cid, t, type} - // 如果是动态的评论 - if(type === CommentType.dynamic) { - actualParams.threadId = id; - } else { - actualParams.id = id; - } - return anfrage({ - url: "/comment/like", - method: "post", - params: actualParams - }).then(res => res.code === 200); -} \ No newline at end of file + const actualParams: PlainObject = { cid, t, type }; + // 如果是动态的评论 + if (type === CommentType.dynamic) { + actualParams.threadId = id; + } else { + actualParams.id = id; + } + return anfrage({ + url: '/comment/like', + method: 'post', + params: actualParams, + }).then((res) => res.code === 200); +} diff --git a/src/api/mv.ts b/src/api/mv.ts index 433c624..bdcebc1 100644 --- a/src/api/mv.ts +++ b/src/api/mv.ts @@ -1,150 +1,136 @@ -import { anfrageWithLoading } from "@/request"; +/** @format */ + +import { anfrageWithLoading } from '@/request'; export const MvOptions = { - areas: [ - '全部', - '内地', - '港台', - '欧美', - '日本', - '韩国', - ], - types: [ - '全部', - '官方版', - '原生', - '现场版', - '网易出品', - ], - orders: [ - '上升最快', - '最热', - '最新', - ] -} + areas: ['全部', '内地', '港台', '欧美', '日本', '韩国'], + types: ['全部', '官方版', '原生', '现场版', '网易出品'], + orders: ['上升最快', '最热', '最新'], +}; /** * MV评论数 - * @param param + * @param param * - id MV的id * - limit 取出评论数,默认20 * - offset 第几页 * - before 当获取数据超过5000条时需要用到。值为上一页最后一项的time,以得到下一页数据 - * @returns + * @returns */ export function getMVComments(param: { - id: string | number, - limit?: string | number, - offset?: string | number, - before?: string | number, + id: string | number; + limit?: string | number; + offset?: string | number; + before?: string | number; }) { - const { id, limit = 20, offset = 0, before } = param - return anfrageWithLoading({ - url: "/comment/mv", - params: { - id, - limit, - offset: +offset * +limit, - before, - timestamp: new Date().valueOf(), - } - }) + const { id, limit = 20, offset = 0, before } = param; + return anfrageWithLoading({ + url: '/comment/mv', + params: { + id, + limit, + offset: +offset * +limit, + before, + timestamp: new Date().valueOf(), + }, + }); } /** * 所有MV - * @param param + * @param param * - id MV的id * - area 地区,可选值为全部,内地,港台,欧美,日本,韩国 * - order 排列顺序,可选值为上升最快,最热,最新 * - type 出品类型,可选值为全部,官方版,原生,现场版,网易出品 * - limit 取出数量,默认30 - * - offset 第几页 - * @returns + * - offset 第几页 + * @returns */ export function getAllMv(param: { - id: string | number, - area?: string, - order?: string, - type?: string, - limit?: string | number, - offset?: string | number + id: string | number; + area?: string; + order?: string; + type?: string; + limit?: string | number; + offset?: string | number; }) { - const { areas, types, orders } = MvOptions; - const { - id, - area = areas[0], - order = orders[0], - type = types[0], - limit = 30, - offset = 0 - } = param - return anfrageWithLoading({ - url: "/mv/all", - params: { - id, area, type, limit, order, - offset: +offset * +limit, - timestamp: new Date().valueOf(), - } - }) + const { areas, types, orders } = MvOptions; + const { + id, + area = areas[0], + order = orders[0], + type = types[0], + limit = 30, + offset = 0, + } = param; + return anfrageWithLoading({ + url: '/mv/all', + params: { + id, + area, + type, + limit, + order, + offset: +offset * +limit, + timestamp: new Date().valueOf(), + }, + }); } /** * 最新MV - * @param param + * @param param * - id MV的id * - limit 取出数量,默认30 - * @returns + * @returns */ -export function getLatestMV(param: { - id: string | number, - limit?: string | number -}) { - const { id, limit = 30 } = param - return anfrageWithLoading({ - url: "/mv/first", - params: { - id, - limit, - timestamp: new Date().valueOf(), - } - }) +export function getLatestMV(param: { id: string | number; limit?: string | number }) { + const { id, limit = 30 } = param; + return anfrageWithLoading({ + url: '/mv/first', + params: { + id, + limit, + timestamp: new Date().valueOf(), + }, + }); } /** * 网易出品MV - * @param param + * @param param * - id MV的id * - limit 取出数量,默认30 - * - offset 第几页 - * @returns + * - offset 第几页 + * @returns */ export function getNeteaseMV(param: { - id: string | number, - limit?: string | number, - offset?: string | number + id: string | number; + limit?: string | number; + offset?: string | number; }) { - const { id, limit = 30, offset = 0 } = param - return anfrageWithLoading({ - url: "/mv/exclusive/rcmd", - params: { - id, - limit, - offset: +offset * +limit, - timestamp: new Date().valueOf(), - } - }) + const { id, limit = 30, offset = 0 } = param; + return anfrageWithLoading({ + url: '/mv/exclusive/rcmd', + params: { + id, + limit, + offset: +offset * +limit, + timestamp: new Date().valueOf(), + }, + }); } /** * 推荐MV - * @param param + * @param param * - id MV的id - * - limit 取出数量,默认30 - * @returns + * - limit 取出数量,默认30 + * @returns */ export function getRecommendMV() { - return anfrageWithLoading({ - url: "/personalized/mv", - }) -} \ No newline at end of file + return anfrageWithLoading({ + url: '/personalized/mv', + }); +} diff --git a/src/api/other.ts b/src/api/other.ts index b86d611..e94d80a 100644 --- a/src/api/other.ts +++ b/src/api/other.ts @@ -1,5 +1,7 @@ -import { anfrageWithLoading, anfrage } from "@/request"; -import { filterUselessKey } from "@utils/index"; +/** @format */ + +import { anfrageWithLoading, anfrage } from '@/request'; +import { filterUselessKey } from '@utils/index'; /** * 新歌速递 @@ -7,14 +9,14 @@ import { filterUselessKey } from "@utils/index"; * @returns */ export function homepageInfo(params: { refresh?: string }) { - return anfrageWithLoading({ - url: "/homepage/block/page", - method: "get", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/homepage/block/page', + method: 'get', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } /** @@ -23,12 +25,12 @@ export function homepageInfo(params: { refresh?: string }) { * @returns */ export function bannerInfo(params: { type?: number | string }) { - params.type ??= 0; - return anfrageWithLoading({ - url: "/banner", - method: "get", - params: filterUselessKey(params), - }) + params.type ??= 0; + return anfrageWithLoading({ + url: '/banner', + method: 'get', + params: filterUselessKey(params), + }); } /** @@ -37,76 +39,76 @@ export function bannerInfo(params: { type?: number | string }) { * @returns */ export function getPersonalFm() { - return anfrageWithLoading({ - url: "/personal_fm", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/personal_fm', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 所有榜单 */ export function allTopList() { - return anfrageWithLoading({ - url: "/toplist", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/toplist', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 所有榜单内容摘要 */ export function allTopListDetail() { - return anfrageWithLoading({ - url: "/toplist/detail", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/toplist/detail', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 国家编码列表 - * @returns + * @returns */ export function getCountryCode() { - return anfrage({ - url: '/countries/code/list', - }) + return anfrage({ + url: '/countries/code/list', + }); } /** * 点赞(MV、电台、视频) - * @param params + * @param params * type:资源类型,对应以下类型:1: mv、4: 电台、5: 视频、6: 动态 - * t: 操作,1 为点赞,其他未取消点赞 + * t: 操作,1 为点赞,其他未取消点赞 * id: 资源 id - * @returns - * @introduction + * @returns + * @introduction * 注意:如给动态点赞,不需要传入 id,需要传入 threadId, * 可通过 event,/user/event 接口获取,如: /resource/like?t=1&type=6&threadId=A_EV_2_6559519868_32953014 */ export function praiseResource(params: { - id: string | number; - sure: boolean; - type: 1 | 4 | 5 | 6 + id: string | number; + sure: boolean; + type: 1 | 4 | 5 | 6; }) { - const { id, sure, type } = params; - return anfrage({ - url: '/resource/like', - method: 'post', - params: { - id, - type, - t: sure ? 1 : -1, - timestamp: new Date().valueOf(), - } - }); -} \ No newline at end of file + const { id, sure, type } = params; + return anfrage({ + url: '/resource/like', + method: 'post', + params: { + id, + type, + t: sure ? 1 : -1, + timestamp: new Date().valueOf(), + }, + }); +} diff --git a/src/api/playlist.ts b/src/api/playlist.ts index 7b8cba4..ec763a4 100644 --- a/src/api/playlist.ts +++ b/src/api/playlist.ts @@ -1,31 +1,33 @@ -import { anfrageWithLoading } from "@/request"; -import { SubscriberList } from "@/types/songlist"; -import { filterUselessKey } from "@utils/index"; +/** @format */ + +import { anfrageWithLoading } from '@/request'; +import { SubscriberList } from '@/types/songlist'; +import { filterUselessKey } from '@utils/index'; /** * 歌单分类 */ export function playlistCate() { - return anfrageWithLoading({ - url: "/playlist/catlist", - method: "get", - params: { - // timestamp: new Date().valueOf() - }, - }) + return anfrageWithLoading({ + url: '/playlist/catlist', + method: 'get', + params: { + // timestamp: new Date().valueOf() + }, + }); } /** * 热门歌单分类 */ export function hotPlaylistCate() { - return anfrageWithLoading({ - url: "/playlist/hot", - method: "get", - params: { - // timestamp: new Date().valueOf() - }, - }) + return anfrageWithLoading({ + url: '/playlist/hot', + method: 'get', + params: { + // timestamp: new Date().valueOf() + }, + }); } /** @@ -37,35 +39,35 @@ export function hotPlaylistCate() { * - offset: 偏移数量 , 用于分页 , 如 :( 评论页数 -1)*50, 其中 50 为 limit 的值 */ export function topPlaylist(params: { - order?: string; - cat?: string; - limit?: number; - offset?: number; + order?: string; + cat?: string; + limit?: number; + offset?: number; }) { - const { limit = 50, offset = 0, order, cat } = params; - return anfrageWithLoading({ - url: "/top/playlist", - method: "get", - params: filterUselessKey({ - limit, - offset: offset * limit, - order, - cat, - }), - }); + const { limit = 50, offset = 0, order, cat } = params; + return anfrageWithLoading({ + url: '/top/playlist', + method: 'get', + params: filterUselessKey({ + limit, + offset: offset * limit, + order, + cat, + }), + }); } /** * 精品歌单标签列表 */ export function highqualPlaylistTag() { - return anfrageWithLoading({ - url: "/playlist/highquality/tags", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/playlist/highquality/tags', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** @@ -76,15 +78,15 @@ export function highqualPlaylistTag() { * - before: 分页参数,取上一页最后一个歌单的 updateTime 获取下一页数据 */ export function highqualPlaylist(params: { - cat?: string; - limit?: number; - before?: number; + cat?: string; + limit?: number; + before?: number; }) { - return anfrageWithLoading({ - url: "/top/playlist/highquality", - method: "get", - params: filterUselessKey(params), - }); + return anfrageWithLoading({ + url: '/top/playlist/highquality', + method: 'get', + params: filterUselessKey(params), + }); } /** @@ -92,11 +94,11 @@ export function highqualPlaylist(params: { * - id 歌单id */ export function relatedPlaylist(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/related/playlist", - method: "get", - params: filterUselessKey(params), - }) + return anfrageWithLoading({ + url: '/related/playlist', + method: 'get', + params: filterUselessKey(params), + }); } /** @@ -108,18 +110,15 @@ export function relatedPlaylist(params: { id: number | string }) { * tracks 则是不完整的,可拿全部 trackIds 请求一次 song/detail 接口获取所有歌曲的详情 * (https://github.com/Binaryify/NeteaseCloudMusicApi/issues/452) */ -export function playlistDetail(params: { - id: number | string; - s?: string | number; -}) { - return anfrageWithLoading({ - url: "/playlist/detail", - method: "get", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }) +export function playlistDetail(params: { id: number | string; s?: string | number }) { + return anfrageWithLoading({ + url: '/playlist/detail', + method: 'get', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } /** @@ -128,14 +127,14 @@ export function playlistDetail(params: { * 说明 : 调用后可获取歌单详情动态部分,如评论数,是否收藏,播放数 */ export function playlistDetailDynamic(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/playlist/detail/dynamic", - method: "get", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/playlist/detail/dynamic', + method: 'get', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } /** @@ -145,23 +144,23 @@ export function playlistDetailDynamic(params: { id: number | string }) { * */ export function albumComment(params: { - id: number | string; - limit?: number | string; - offset?: number | string; - before?: number | string; + id: number | string; + limit?: number | string; + offset?: number | string; + before?: number | string; }) { - const { limit = 20, offset = 0, before, id } = params; - return anfrageWithLoading({ - url: "/comment/album", - method: "get", - params: filterUselessKey({ - limit, - offset: +offset * +limit, - before, - id, - timestamp: new Date().valueOf(), - }), - }); + const { limit = 20, offset = 0, before, id } = params; + return anfrageWithLoading({ + url: '/comment/album', + method: 'get', + params: filterUselessKey({ + limit, + offset: +offset * +limit, + before, + id, + timestamp: new Date().valueOf(), + }), + }); } /** @@ -171,179 +170,176 @@ export function albumComment(params: { * */ export function playlistComment(params: { - id: number | string; - limit?: number | string; - offset?: number | string; - before?: number | string; + id: number | string; + limit?: number | string; + offset?: number | string; + before?: number | string; }) { - const { limit = 20, offset = 0, before, id } = params; - return anfrageWithLoading({ - url: "/comment/playlist", - method: "get", - params: filterUselessKey({ - limit, - offset: +offset * +limit, - before, - id, - timestamp: new Date().valueOf(), - }), - }); + const { limit = 20, offset = 0, before, id } = params; + return anfrageWithLoading({ + url: '/comment/playlist', + method: 'get', + params: filterUselessKey({ + limit, + offset: +offset * +limit, + before, + id, + timestamp: new Date().valueOf(), + }), + }); } /** * 专辑内容 */ export function albumDetail(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/album", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/album', + method: 'get', + params, + }); } /** * 专辑动态信息 */ export function albumDynamicInfo(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/album/detail/dynamic", - method: "get", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/album/detail/dynamic', + method: 'get', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** * 收藏/取消收藏专辑 */ -export function albumSubscribe(params: { - id: number | string; - sure: boolean; -}) { - const { id, sure } = params; - return anfrageWithLoading({ - url: "/album/sub", - method: "post", - params: { - id, - t: sure ? 1 : -1, - timestamp: new Date().valueOf(), - }, - }); +export function albumSubscribe(params: { id: number | string; sure: boolean }) { + const { id, sure } = params; + return anfrageWithLoading({ + url: '/album/sub', + method: 'post', + params: { + id, + t: sure ? 1 : -1, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取相似歌单 */ export function playlistSimilar(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/simi/playlist", - method: "get", - params, - }) + return anfrageWithLoading({ + url: '/simi/playlist', + method: 'get', + params, + }); } /** * 获取每日推荐歌单 */ export function playlistRecommend() { - return anfrageWithLoading({ - url: "/recommend/resource", - method: "get", - }) + return anfrageWithLoading({ + url: '/recommend/resource', + method: 'get', + }); } /** * 新碟上架 */ export function newAlbumPutOn(params: { - limit?: number | string; - offset?: number | string; - area?: number | string; - type?: number | string; - year?: number | string; - month?: number | string; + limit?: number | string; + offset?: number | string; + area?: number | string; + type?: number | string; + year?: number | string; + month?: number | string; }) { - const { limit = 50, offset = 0, area = "ALL", type = 'new', year, month } = params; - return anfrageWithLoading({ - url: "/top/album", - method: "get", - params: filterUselessKey({ - // limit, - // offset: +offset * +limit, - area, - type, - year, - month, - timestamp: new Date().valueOf(), - }), - }) + const { limit = 50, offset = 0, area = 'ALL', type = 'new', year, month } = params; + return anfrageWithLoading({ + url: '/top/album', + method: 'get', + params: filterUselessKey({ + // limit, + // offset: +offset * +limit, + area, + type, + year, + month, + timestamp: new Date().valueOf(), + }), + }); } /** * 全部新碟 */ export function newAlbum(params: { - limit?: number | string; - offset?: number | string; - area?: number | string; + limit?: number | string; + offset?: number | string; + area?: number | string; }) { - const { limit = 30, offset = 0, area } = params; - return anfrageWithLoading({ - url: "/album/new", - method: "get", - params: filterUselessKey({ - limit, - offset: +offset * +limit, - area, - timestamp: new Date().valueOf(), - }), - }); + const { limit = 30, offset = 0, area } = params; + return anfrageWithLoading({ + url: '/album/new', + method: 'get', + params: filterUselessKey({ + limit, + offset: +offset * +limit, + area, + timestamp: new Date().valueOf(), + }), + }); } /** * 最新专辑 */ export function newestAlbum() { - return anfrageWithLoading({ - url: "/album/newest", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/album/newest', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 推荐歌单(没登录时) */ export function recommendSonglist(params: { limit?: number | string }) { - return anfrageWithLoading({ - url: "/personalized", - method: "get", - params: filterUselessKey(params), - }) + return anfrageWithLoading({ + url: '/personalized', + method: 'get', + params: filterUselessKey(params), + }); } /** * 歌单收藏者 */ export function PlaylistSubscribe(params: { - id: number | string; - limit?: number | string; - offset?: number | string; + id: number | string; + limit?: number | string; + offset?: number | string; }) { - const { limit = 20, id, offset = 0 } = params; - return anfrageWithLoading({ - url: "/playlist/subscribers", - method: "get", - params: { - id, - limit, - offset: +limit * +offset, - timestamp: new Date().valueOf(), - }, - }); + const { limit = 20, id, offset = 0 } = params; + return anfrageWithLoading({ + url: '/playlist/subscribers', + method: 'get', + params: { + id, + limit, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }, + }); } diff --git a/src/api/search.ts b/src/api/search.ts index de9f4eb..66dc413 100644 --- a/src/api/search.ts +++ b/src/api/search.ts @@ -1,5 +1,7 @@ -import { anfrageWithLoading } from "@/request"; -import { filterUselessKey } from "@utils/index"; +/** @format */ + +import { anfrageWithLoading } from '@/request'; +import { filterUselessKey } from '@utils/index'; /** * 搜索 @@ -9,87 +11,87 @@ import { filterUselessKey } from "@utils/index"; * @returns */ export function searchCloud(params: { - keywords: string; - limit?: number | string; - offset?: number | string; - type?: number | string; + keywords: string; + limit?: number | string; + offset?: number | string; + type?: number | string; }) { - const { keywords, limit = 30, offset = 0, type = 1 } = params; - return anfrageWithLoading({ - url: "/cloudsearch", - method: "get", - params: filterUselessKey({ - keywords, - limit, - type, - offset: +limit * +offset, - }), - }); + const { keywords, limit = 30, offset = 0, type = 1 } = params; + return anfrageWithLoading({ + url: '/cloudsearch', + method: 'get', + params: filterUselessKey({ + keywords, + limit, + type, + offset: +limit * +offset, + }), + }); } /** * 默认搜索关键词 */ export function searchDefault() { - return anfrageWithLoading({ - url: "/search/default", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/search/default', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 热搜列表(简略) */ export function hotSearch() { - return anfrageWithLoading({ - url: "/search/hot", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/search/hot', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 热搜列表(详细) */ export function hotSearchDetail() { - return anfrageWithLoading({ - url: "/search/hot/detail", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/search/hot/detail', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 搜索建议 */ export function searchSuggest(params: { keywords: string; type?: string }) { - return anfrageWithLoading({ - url: "/search/suggest", - method: "get", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/search/suggest', + method: 'get', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } /** * 搜索多重匹配 */ export function searchMulMatch(params: { keywords: string }) { - return anfrageWithLoading({ - url: "/search/multimatch", - method: "get", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/search/multimatch', + method: 'get', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } diff --git a/src/api/singer.ts b/src/api/singer.ts index 26420c8..19e5ebd 100644 --- a/src/api/singer.ts +++ b/src/api/singer.ts @@ -1,5 +1,7 @@ -import { anfrage, anfrageWithLoading } from "@/request"; -import { filterUselessKey } from "@utils/index"; +/** @format */ + +import { anfrage, anfrageWithLoading } from '@/request'; +import { filterUselessKey } from '@utils/index'; /** * 歌手分类列表 @@ -20,22 +22,20 @@ import { filterUselessKey } from "@utils/index"; * 0:其他 * */ -type artistParams = "limit" | "offset" | "initial" | "type" | "area"; -export function artistList( - params: Partial> -) { - const { limit = 30, offset = 0, type = -1, area = -1, initial } = params; - return anfrageWithLoading({ - url: "/artist/list", - method: "get", - params: filterUselessKey({ - limit, - offset: +limit * +offset, - type, - area, - initial, - }), - }); +type artistParams = 'limit' | 'offset' | 'initial' | 'type' | 'area'; +export function artistList(params: Partial>) { + const { limit = 30, offset = 0, type = -1, area = -1, initial } = params; + return anfrageWithLoading({ + url: '/artist/list', + method: 'get', + params: filterUselessKey({ + limit, + offset: +limit * +offset, + type, + area, + initial, + }), + }); } /** @@ -44,16 +44,16 @@ export function artistList( * - t 操作,1为收藏,其他为取消收藏 */ export function artistSub(params: { id: number | string; sure: boolean }) { - const { sure, id } = params; - return anfrage({ - url: "/artist/sub", - method: "post", - params: { - id, - t: sure ? 1 : -1, - timestamp: new Date().valueOf(), - }, - }); + const { sure, id } = params; + return anfrage({ + url: '/artist/sub', + method: 'post', + params: { + id, + t: sure ? 1 : -1, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -62,14 +62,14 @@ export function artistSub(params: { id: number | string; sure: boolean }) { * - id 歌手id */ export function artistTopSong(params: { id: number | string }) { - return anfrageWithLoading({ - url: "/artist/top/song", - method: "get", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/artist/top/song', + method: 'get', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -81,57 +81,57 @@ export function artistTopSong(params: { id: number | string }) { * - offset: 偏移数量 , 用于分页 , 如 :( 评论页数 -1)*50, 其中 50 为 limit 的值 */ export function artistSongs(params: { - id: number | string; - order?: string; - limit?: number | string; - offset?: number | string; + id: number | string; + order?: string; + limit?: number | string; + offset?: number | string; }) { - const { limit = 50, offset = 0, order, id } = params; - return anfrageWithLoading({ - url: "/artist/songs", - method: "get", - params: filterUselessKey({ - limit, - offset: +limit * +offset, - order, - id, - }), - }); + const { limit = 50, offset = 0, order, id } = params; + return anfrageWithLoading({ + url: '/artist/songs', + method: 'get', + params: filterUselessKey({ + limit, + offset: +limit * +offset, + order, + id, + }), + }); } /** * 获取歌手单曲 */ export function artistSingalSongs(params: { id: string | number }) { - return anfrageWithLoading({ - url: "/artists", - method: "get", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/artists', + method: 'get', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取歌手专辑 */ export function artistAlbum(params: { - id: string | number; - limit?: number | string; - offset?: number | string; + id: string | number; + limit?: number | string; + offset?: number | string; }) { - const { limit = 50, offset = 0, id } = params; - return anfrageWithLoading({ - url: "/artist/album", - method: "get", - params: { - id, - limit, - offset: +limit * +offset, - timestamp: new Date().valueOf(), - }, - }); + const { limit = 50, offset = 0, id } = params; + return anfrageWithLoading({ + url: '/artist/album', + method: 'get', + params: { + id, + limit, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -139,81 +139,78 @@ export function artistAlbum(params: { * @param id 歌手id */ export function artistMv(params: { - id: string | number; - limit?: number | string; - offset?: number | string; + id: string | number; + limit?: number | string; + offset?: number | string; }) { - const { limit = 30, offset = 0, id } = params; - return anfrageWithLoading({ - url: "/artist/mv", - method: "get", - params: filterUselessKey({ - id, - limit, - offset: +limit * +offset, - timestamp: new Date().valueOf(), - }), - }) + const { limit = 30, offset = 0, id } = params; + return anfrageWithLoading({ + url: '/artist/mv', + method: 'get', + params: filterUselessKey({ + id, + limit, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }), + }); } /** * 获取歌手描述 */ export function artistDesc(params: { id: string | number }) { - return anfrageWithLoading({ - url: "/artist/desc", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/artist/desc', + method: 'get', + params, + }); } /** * 获取歌手详情 */ export function artistDetail(params: { id: string | number }) { - return anfrageWithLoading({ - url: "/artist/detail", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/artist/detail', + method: 'get', + params, + }); } /** * 获取相似歌手 */ export function artistSimilar(params: { id: string | number }) { - return anfrageWithLoading({ - url: "/simi/artist", - method: "get", - params, - }); + return anfrageWithLoading({ + url: '/simi/artist', + method: 'get', + params, + }); } /** * 热门歌手 */ -export function artistHot(params: { - limit?: number | string; - offset?: number | string; -}) { - const { limit = 50, offset = 0 } = params; - return anfrageWithLoading({ - url: "/top/artists", - method: "get", - params: { - limit, - offset: +limit * +offset, - }, - }); +export function artistHot(params: { limit?: number | string; offset?: number | string }) { + const { limit = 50, offset = 0 } = params; + return anfrageWithLoading({ + url: '/top/artists', + method: 'get', + params: { + limit, + offset: +limit * +offset, + }, + }); } /** * 歌手榜 */ export function artistTopList(params: { type?: number | string }) { - return anfrageWithLoading({ - url: "/toplist/artist", - method: "get", - params: filterUselessKey(params), - }); + return anfrageWithLoading({ + url: '/toplist/artist', + method: 'get', + params: filterUselessKey(params), + }); } diff --git a/src/api/user.ts b/src/api/user.ts index d19698c..20b4f6c 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,5 +1,7 @@ -import { anfrage, anfrageWithLoading } from "@/request"; -import { filterUselessKey } from "@utils/index"; +/** @format */ + +import { anfrage, anfrageWithLoading } from '@/request'; +import { filterUselessKey } from '@utils/index'; /** * 获取用户详情 @@ -8,14 +10,14 @@ import { filterUselessKey } from "@utils/index"; * @param {number} uid */ export function userDetail(uid: string | number) { - return anfrageWithLoading({ - url: "/user/detail", - method: "get", - params: { - uid, - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/user/detail', + method: 'get', + params: { + uid, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -23,26 +25,26 @@ export function userDetail(uid: string | number) { * 说明 : 登录后调用此接口 ,可获取用户账号信息 */ export function userAccount() { - return anfrageWithLoading({ - url: "/user/account", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/user/account', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** * 获取用户信息 , 歌单,收藏,mv, dj 数量 */ export function userSubcount() { - return anfrageWithLoading({ - url: "/user/subcount", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/user/subcount', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** @@ -51,14 +53,14 @@ export function userSubcount() { * @param {number} uid */ export function userBinding(uid: string | number) { - return anfrageWithLoading({ - url: "/user/binding", - method: "get", - params: { - uid, - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/user/binding', + method: 'get', + params: { + uid, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -71,21 +73,21 @@ export function userBinding(uid: string | number) { * @param {number} offset */ export function userPlaylist(params: { - uid: string | number; - limit?: string | number; - offset?: string | number; + uid: string | number; + limit?: string | number; + offset?: string | number; }) { - const { uid, limit = 30, offset = 0 } = params; - return anfrageWithLoading({ - url: "/user/playlist", - method: "get", - params: filterUselessKey({ - uid, - limit, - offset: +limit * +offset, - timestamp: new Date().valueOf(), - }), - }) + const { uid, limit = 30, offset = 0 } = params; + return anfrageWithLoading({ + url: '/user/playlist', + method: 'get', + params: filterUselessKey({ + uid, + limit, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }), + }); } /** @@ -94,14 +96,14 @@ export function userPlaylist(params: { * @param {number} uid */ export function userDj(params: { uid: string | number }) { - return anfrageWithLoading({ - url: "/user/dj", - method: "get", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/user/dj', + method: 'get', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -114,21 +116,21 @@ export function userDj(params: { uid: string | number }) { * @param {number} offset */ export function userFollows(params: { - uid: string | number; - limit?: string | number; - offset?: string | number; + uid: string | number; + limit?: string | number; + offset?: string | number; }) { - const { uid, limit = 30, offset = 0 } = params; - return anfrageWithLoading({ - url: "/user/follows", - method: "get", - params: filterUselessKey({ - uid, - limit, - offset: +limit * +offset, - timestamp: new Date().valueOf(), - }), - }); + const { uid, limit = 30, offset = 0 } = params; + return anfrageWithLoading({ + url: '/user/follows', + method: 'get', + params: filterUselessKey({ + uid, + limit, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }), + }); } /** @@ -141,21 +143,21 @@ export function userFollows(params: { * @param {number} offset */ export function userFans(params: { - uid: string | number; - limit?: string | number; - offset?: string | number; + uid: string | number; + limit?: string | number; + offset?: string | number; }) { - const { uid, limit = 30, offset = 0 } = params; - return anfrageWithLoading({ - url: "/user/followeds", - method: "get", - params: filterUselessKey({ - uid, - limit, - offset: +limit * +offset, - timestamp: new Date().valueOf(), - }), - }); + const { uid, limit = 30, offset = 0 } = params; + return anfrageWithLoading({ + url: '/user/followeds', + method: 'get', + params: filterUselessKey({ + uid, + limit, + offset: +limit * +offset, + timestamp: new Date().valueOf(), + }), + }); } /** @@ -166,16 +168,16 @@ export function userFans(params: { * @param {number} t */ export function unOrFocusUser(params: { id: string | number; sure: boolean }) { - const { id, sure } = params; - return anfrage({ - url: "/follow", - method: "post", - params: { - id, - t: sure ? 1 : -1, - timestamp: new Date().valueOf(), - } - }); + const { id, sure } = params; + return anfrage({ + url: '/follow', + method: 'post', + params: { + id, + t: sure ? 1 : -1, + timestamp: new Date().valueOf(), + }, + }); } /** @@ -186,28 +188,28 @@ export function unOrFocusUser(params: { id: string | number; sure: boolean }) { * @param {number} type */ export function userRecord(params: { uid: string | number; type?: 0 | 1 }) { - params.type ??= 1; - return anfrageWithLoading({ - url: "/user/record", - method: "post", - params: { - ...filterUselessKey(params), - timestamp: new Date().valueOf(), - }, - }) + params.type ??= 1; + return anfrageWithLoading({ + url: '/user/record', + method: 'post', + params: { + ...filterUselessKey(params), + timestamp: new Date().valueOf(), + }, + }); } /** - * 收藏的歌手列表 + * 收藏的歌手列表 */ export function userLoveArtist() { - return anfrageWithLoading({ - url: "/artist/sublist", - method: "get", - params: { - timestamp: new Date().valueOf(), - }, - }); + return anfrageWithLoading({ + url: '/artist/sublist', + method: 'get', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** @@ -216,81 +218,72 @@ export function userLoveArtist() { * - t 1为收藏,其他为取消收藏 * */ -export function userVideoCollect(params: { - id: string | number; - sure: boolean; -}) { - const { id, sure } = params; - return anfrage({ - url: "/video/sub", - method: "post", - params: { - id, - t: sure ? 1 : -1, - timestamp: new Date().valueOf(), - }, - }); +export function userVideoCollect(params: { id: string | number; sure: boolean }) { + const { id, sure } = params; + return anfrage({ + url: '/video/sub', + method: 'post', + params: { + id, + t: sure ? 1 : -1, + timestamp: new Date().valueOf(), + }, + }); } /** * 收藏/取消收藏 MV * - mvid mv的id - * - t 1为收藏,其他为取消收藏 + * - t 1为收藏,其他为取消收藏 */ -export function userMvCollect(params: { - id: string | number; - sure: boolean; -}) { - const { id, sure } = params; - return anfrage({ - url: "/mv/sub", - method: "post", - params: { - mvid: id, - t: sure ? 1 : -1, - }, - }); +export function userMvCollect(params: { id: string | number; sure: boolean }) { + const { id, sure } = params; + return anfrage({ + url: '/mv/sub', + method: 'post', + params: { + mvid: id, + t: sure ? 1 : -1, + }, + }); } /** - * 收藏的 MV 列表 + * 收藏的 MV 列表 */ export function userCollectedMv() { - return anfrageWithLoading({ - url: "/mv/sublist", - params: { - timestamp: new Date().valueOf(), - } - }); + return anfrageWithLoading({ + url: '/mv/sublist', + params: { + timestamp: new Date().valueOf(), + }, + }); } /** - * 喜欢音乐列表 + * 喜欢音乐列表 */ export function userLikeList(params: { uid: number | string }) { - return anfrageWithLoading({ - url: "/likelist", - method: "get", - params: { - ...params, - timestamp: new Date().valueOf(), - }, - }) + return anfrageWithLoading({ + url: '/likelist', + method: 'get', + params: { + ...params, + timestamp: new Date().valueOf(), + }, + }); } /** * 喜欢音乐 - * @returns + * @returns */ -export function userLikeMusic(params: { - id: string | number, - like: boolean, -}) { - return anfrage({ - url: '/like', - method: "post", - params: filterUselessKey(params) - }) +export function userLikeMusic(params: { id: string | number; like: boolean }) { + return anfrage({ + url: '/like', + method: 'post', + params: filterUselessKey(params), + }); } /** @@ -298,8 +291,8 @@ export function userLikeMusic(params: { * */ export function userLSetting() { - return anfrageWithLoading({ - url: "/setting", - method: "get", - }) + return anfrageWithLoading({ + url: '/setting', + method: 'get', + }); } diff --git a/src/api/video.ts b/src/api/video.ts index 6d679db..022ccba 100644 --- a/src/api/video.ts +++ b/src/api/video.ts @@ -1,194 +1,189 @@ -import { anfrageWithLoading } from "@/request"; -import { EMPTY_OBJ } from "@/utils"; +/** @format */ + +import { anfrageWithLoading } from '@/request'; +import { EMPTY_OBJ } from '@/utils'; /** * 获取视频标签列表 - * @returns + * @returns */ export function getVideoTagList() { - return anfrageWithLoading({ - url: '/video/group/list' - }) + return anfrageWithLoading({ + url: '/video/group/list', + }); } /** * 获取视频分类列表(需登录) - * @returns + * @returns */ export function getVideoCategoryList() { - return anfrageWithLoading({ - url: '/video/category/list', - }) + return anfrageWithLoading({ + url: '/video/category/list', + }); } /** * 获取视频标签/分类下的视频 - * @param param - * @returns + * @param param + * @returns */ export function getVideos(param: { - id: string | number; //标签/分类id - offset?: number | string //分页偏移量; + id: string | number; //标签/分类id + offset?: number | string; //分页偏移量; }) { - const { offset = 0, id } = param - return anfrageWithLoading({ - url: "/video/group", - params: { - id, - offset, - timestamp: new Date().valueOf(), - } - }) + const { offset = 0, id } = param; + return anfrageWithLoading({ + url: '/video/group', + params: { + id, + offset, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取全部视频列表(需登录) - * @param param - * @returns + * @param param + * @returns */ export function getAllVideoList( - param: { - offset?: number | string; - } = EMPTY_OBJ + param: { + offset?: number | string; + } = EMPTY_OBJ, ) { - const { offset = 0 } = param; - return anfrageWithLoading({ - url: '/video/timeline/all', - params: { - offset, - timestamp: new Date().valueOf(), - } - }) + const { offset = 0 } = param; + return anfrageWithLoading({ + url: '/video/timeline/all', + params: { + offset, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取推荐的视频(需登录) - * @param param - * @returns + * @param param + * @returns */ export function getRecommendVideos( - param: { - offset?: number | string; - } = EMPTY_OBJ + param: { + offset?: number | string; + } = EMPTY_OBJ, ) { - const { offset = 0 } = param - return anfrageWithLoading({ - url: '/video/timeline/recommend', - params: { - offset, - timestamp: new Date().valueOf(), - } - }) + const { offset = 0 } = param; + return anfrageWithLoading({ + url: '/video/timeline/recommend', + params: { + offset, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取相关视频 - * @param param - * @returns + * @param param + * @returns */ export function getRelativeVideos(param: { - id: string | number; //当前视频id + id: string | number; //当前视频id }) { - const { id } = param - return anfrageWithLoading({ - url: '/related/allvideo', - params: { - id, - timestamp: new Date().valueOf(), - } - }) + const { id } = param; + return anfrageWithLoading({ + url: '/related/allvideo', + params: { + id, + timestamp: new Date().valueOf(), + }, + }); } - /** * 获取视频的详情 - * @param param - * @returns + * @param param + * @returns */ -export function getVideoDetail(param: { - id: string | number -}) { - const { id } = param; - return anfrageWithLoading({ - url: "/video/detail", - params: { - id, - timestamp: new Date().valueOf(), - } - }) +export function getVideoDetail(param: { id: string | number }) { + const { id } = param; + return anfrageWithLoading({ + url: '/video/detail', + params: { + id, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取视频点赞、转发、评论数信息 - * @param param - * @returns + * @param param + * @returns */ -export function getVideoRelativeInfo(param: { - vid: string | number -}) { - const { vid } = param - return anfrageWithLoading({ - url: '/video/detail/info', - params: { - vid, - timestamp: new Date().valueOf(), - } - }) +export function getVideoRelativeInfo(param: { vid: string | number }) { + const { vid } = param; + return anfrageWithLoading({ + url: '/video/detail/info', + params: { + vid, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取视频评论数 - * @param param + * @param param * - id 视频id * - limit 取出评论数,默认20 * - offset 第几页 * - before 当获取数据超过5000条时需要用到。值为上一页最后一项的time,以得到下一页数据 - * @returns + * @returns */ export function getVideoComments(param: { - id: string | number, - limit?: string | number, - offset?: string | number, - before?: string | number, + id: string | number; + limit?: string | number; + offset?: string | number; + before?: string | number; }) { - const { id, limit = 20, offset = 0, before } = param - return anfrageWithLoading({ - url: '/comment/video', - params: { - id, - limit, - offset: +offset * +limit, - before, - timestamp: new Date().valueOf(), - } - }) + const { id, limit = 20, offset = 0, before } = param; + return anfrageWithLoading({ + url: '/comment/video', + params: { + id, + limit, + offset: +offset * +limit, + before, + timestamp: new Date().valueOf(), + }, + }); } /** * 获取视频播放源 - * @param param - * @returns + * @param param + * @returns */ -export function getVideoPlaybackSource(param: { - id: string | number -}) { - const { id } = param; - return anfrageWithLoading({ - url: "/video/url", - params: { - id, - } - }) +export function getVideoPlaybackSource(param: { id: string | number }) { + const { id } = param; + return anfrageWithLoading({ + url: '/video/url', + params: { + id, + }, + }); } /** * 获取点赞过的视频 */ export function getPraisedVideos() { - return anfrageWithLoading({ - url: '/playlist/mylike', - params: { - timestamp: new Date().valueOf(), - } - }); -} \ No newline at end of file + return anfrageWithLoading({ + url: '/playlist/mylike', + params: { + timestamp: new Date().valueOf(), + }, + }); +} diff --git a/src/assets/icons/iconfont.css b/src/assets/icons/iconfont.css index e73617e..c509701 100644 --- a/src/assets/icons/iconfont.css +++ b/src/assets/icons/iconfont.css @@ -1,302 +1,303 @@ +/** @format */ + @font-face { - font-family: "iconfont"; /* Project id 2672275 */ - src: url('iconfont.woff2?t=1659771371374') format('woff2'), - url('iconfont.woff?t=1659771371374') format('woff'), - url('iconfont.ttf?t=1659771371374') format('truetype'); + font-family: 'iconfont'; /* Project id 2672275 */ + src: url('iconfont.woff2?t=1659771371374') format('woff2'), + url('iconfont.woff?t=1659771371374') format('woff'), + url('iconfont.ttf?t=1659771371374') format('truetype'); } .iconfont { - font-family: "iconfont" !important; - font-style: normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + font-family: 'iconfont' !important; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } .icon-menu:before { - content: "\e8ce"; + content: '\e8ce'; } .icon-gou:before { - content: "\e622"; + content: '\e622'; } .icon-praise:before { - content: "\e87e"; + content: '\e87e'; } .icon-collect:before { - content: "\eb57"; + content: '\eb57'; } .icon-refresh:before { - content: "\e644"; + content: '\e644'; } .icon-order:before { - content: "\e6af"; + content: '\e6af'; } .icon-loved:before { - content: "\e6c9"; + content: '\e6c9'; } .icon-xihuan1:before { - content: "\e621"; + content: '\e621'; } .icon-loving:before { - content: "\e620"; + content: '\e620'; } .icon-love:before { - content: "\e61f"; + content: '\e61f'; } .icon-bofan-radius:before { - content: "\e61e"; + content: '\e61e'; } .icon-xiajiantou:before { - content: "\e63b"; + content: '\e63b'; } .icon-nextmusic:before { - content: "\e76d"; + content: '\e76d'; } .icon-prevmusic:before { - content: "\e76e"; + content: '\e76e'; } .icon-shanghua:before { - content: "\e61d"; + content: '\e61d'; } .icon-plus:before { - content: "\e626"; + content: '\e626'; } .icon-arrowright:before { - content: "\e639"; + content: '\e639'; } .icon-arrowleft:before { - content: "\e64a"; + content: '\e64a'; } .icon-mute:before { - content: "\e692"; + content: '\e692'; } .icon-random:before { - content: "\e6e2"; + content: '\e6e2'; } .icon-bofan:before { - content: "\e80f"; + content: '\e80f'; } .icon-yinliang:before { - content: "\e87a"; + content: '\e87a'; } .icon-single-loop:before { - content: "\e649"; + content: '\e649'; } .icon-icon--:before { - content: "\e726"; + content: '\e726'; } .icon-yonghu:before { - content: "\e720"; + content: '\e720'; } .icon-shezhi:before { - content: "\e62a"; + content: '\e62a'; } .icon-cancelfullscreen:before { - content: "\eb98"; + content: '\eb98'; } .icon-fullscreen:before { - content: "\eb99"; + content: '\eb99'; } .icon-play:before { - content: "\e631"; + content: '\e631'; } .icon-yinleliebiao:before { - content: "\e674"; + content: '\e674'; } .icon-xiahua:before { - content: "\e770"; + content: '\e770'; } .icon-bofangliebiao:before { - content: "\e608"; + content: '\e608'; } .icon-shoucang-:before { - content: "\e61a"; + content: '\e61a'; } .icon-dingdanzhongxinicon:before { - content: "\e637"; + content: '\e637'; } .icon-xihuan:before { - content: "\e61b"; + content: '\e61b'; } .icon-pinglunqu:before { - content: "\e611"; + content: '\e611'; } .icon-wangdianchaxun:before { - content: "\e612"; + content: '\e612'; } .icon-pinglun:before { - content: "\e73e"; + content: '\e73e'; } .icon-pause:before { - content: "\e67b"; + content: '\e67b'; } .icon-translate:before { - content: "\e66b"; + content: '\e66b'; } .icon-loop:before { - content: "\e613"; + content: '\e613'; } .icon-qingkonglishijilu:before { - content: "\e638"; + content: '\e638'; } .icon-huabanfuben:before { - content: "\eb8f"; + content: '\eb8f'; } .icon-icon-1:before { - content: "\e654"; + content: '\e654'; } .icon-locate:before { - content: "\e666"; + content: '\e666'; } .icon-download:before { - content: "\e678"; + content: '\e678'; } .icon-paixu:before { - content: "\e624"; + content: '\e624'; } .icon-guanyuwomen:before { - content: "\e62b"; + content: '\e62b'; } .icon-shang:before { - content: "\e744"; + content: '\e744'; } .icon-chenggong:before { - content: "\e75a"; + content: '\e75a'; } .icon-dingwei:before { - content: "\e619"; + content: '\e619'; } .icon-dianzan:before { - content: "\e601"; + content: '\e601'; } .icon-gengduo:before { - content: "\e602"; + content: '\e602'; } .icon-pinglun1:before { - content: "\e603"; + content: '\e603'; } .icon-shouji:before { - content: "\e604"; + content: '\e604'; } .icon-shipin:before { - content: "\e605"; + content: '\e605'; } .icon-shoucang:before { - content: "\e606"; + content: '\e606'; } .icon-shouye:before { - content: "\e607"; + content: '\e607'; } .icon-fenxiang:before { - content: "\e609"; + content: '\e609'; } .icon-guanbi:before { - content: "\e60a"; + content: '\e60a'; } .icon-shanchu:before { - content: "\e60b"; + content: '\e60b'; } .icon-lishi:before { - content: "\e60c"; + content: '\e60c'; } .icon-yanzhengma:before { - content: "\e60d"; + content: '\e60d'; } .icon-dianzan2:before { - content: "\e60e"; + content: '\e60e'; } .icon-zuojiantou:before { - content: "\e60f"; + content: '\e60f'; } .icon-sousuo:before { - content: "\e610"; + content: '\e610'; } .icon-wode:before { - content: "\e614"; + content: '\e614'; } .icon-yuedu:before { - content: "\e615"; + content: '\e615'; } .icon-wenda:before { - content: "\e616"; + content: '\e616'; } .icon-youjiantou:before { - content: "\e617"; + content: '\e617'; } .icon-wuwangluo:before { - content: "\e618"; + content: '\e618'; } .icon-zuopin:before { - content: "\e61c"; + content: '\e61c'; } - diff --git a/src/assets/js/colorthief.js b/src/assets/js/colorthief.js index 462912b..7375b93 100644 --- a/src/assets/js/colorthief.js +++ b/src/assets/js/colorthief.js @@ -1 +1,416 @@ -if(!t)var t={map:function(t,r){var n={};return r?t.map(function(t,o){return n.index=o,r.call(n,t)}):t.slice()},naturalOrder:function(t,r){return tr?1:0},sum:function(t,r){var n={};return t.reduce(r?function(t,o,e){return n.index=e,t+r.call(n,o)}:function(t,r){return t+r},0)},max:function(r,n){return Math.max.apply(null,n?t.map(r,n):r)}};var r=function(){var r=5,n=8-r,o=1e3;function e(t,n,o){return(t<<2*r)+(n<f/2){for(e=n.copy(),i=n.copy(),u=(r=a-n[s])<=(o=n[h]-a)?Math.min(n[h]-1,~~(a+o/2)):Math.max(n[s],~~(a-1-r/2));!v[u];)u++;for(c=l[u];!c&&v[u-1];)c=l[--u];return e[h]=u,i[s]=e[h]+1,[e,i]}}(u==o?"r":u==i?"g":"b")}}return u.prototype={volume:function(t){return this._volume&&!t||(this._volume=(this.r2-this.r1+1)*(this.g2-this.g1+1)*(this.b2-this.b1+1)),this._volume},count:function(t){var r=this.histo;if(!this._count_set||t){var n,o,i,u=0;for(n=this.r1;n<=this.r2;n++)for(o=this.g1;o<=this.g2;o++)for(i=this.b1;i<=this.b2;i++)u+=r[e(n,o,i)]||0;this._count=u,this._count_set=!0}return this._count},copy:function(){return new u(this.r1,this.r2,this.g1,this.g2,this.b1,this.b2,this.histo)},avg:function(t){var n=this.histo;if(!this._avg||t){var o,i,u,a,s=0,h=1<<8-r,c=0,f=0,v=0;for(i=this.r1;i<=this.r2;i++)for(u=this.g1;u<=this.g2;u++)for(a=this.b1;a<=this.b2;a++)s+=o=n[e(i,u,a)]||0,c+=o*(i+.5)*h,f+=o*(u+.5)*h,v+=o*(a+.5)*h;this._avg=s?[~~(c/s),~~(f/s),~~(v/s)]:[~~(h*(this.r1+this.r2+1)/2),~~(h*(this.g1+this.g2+1)/2),~~(h*(this.b1+this.b2+1)/2)]}return this._avg},contains:function(t){var r=t[0]>>n;return gval=t[1]>>n,bval=t[2]>>n,r>=this.r1&&r<=this.r2&&gval>=this.g1&&gval<=this.g2&&bval>=this.b1&&bval<=this.b2}},a.prototype={push:function(t){this.vboxes.push({vbox:t,color:t.avg()})},palette:function(){return this.vboxes.map(function(t){return t.color})},size:function(){return this.vboxes.size()},map:function(t){for(var r=this.vboxes,n=0;n251&&e[1]>251&&e[2]>251&&(r[o].color=[255,255,255])}},{quantize:function(h,c){if(!h.length||c<2||c>256)return!1;var f=function(t){var o,i=new Array(1<<3*r);return t.forEach(function(t){o=e(t[0]>>n,t[1]>>n,t[2]>>n),i[o]=(i[o]||0)+1}),i}(h);f.forEach(function(){});var v=function(t,r){var o,e,i,a=1e6,s=0,h=1e6,c=0,f=1e6,v=0;return t.forEach(function(t){(o=t[0]>>n)s&&(s=o),(e=t[1]>>n)c&&(c=e),(i=t[2]>>n)v&&(v=i)}),new u(a,s,h,c,f,v,r)}(h,f),l=new i(function(r,n){return t.naturalOrder(r.count(),n.count())});function g(t,r){for(var n,e=t.size(),i=0;i=r)return;if(i++>o)return;if((n=t.pop()).count()){var u=s(f,n),a=u[0],h=u[1];if(!a)return;t.push(a),h&&(t.push(h),e++)}else t.push(n),i++}}l.push(v),g(l,.75*c);for(var p=new i(function(r,n){return t.naturalOrder(r.count()*r.volume(),n.count()*n.volume())});l.size();)p.push(l.pop());g(p,c);for(var b=new a;p.size();)b.push(p.pop());return b}}}().quantize,n=function(t){this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.width=this.canvas.width=t.naturalWidth,this.height=this.canvas.height=t.naturalHeight,this.context.drawImage(t,0,0,this.width,this.height)};n.prototype.getImageData=function(){return this.context.getImageData(0,0,this.width,this.height)};var o=function(){};o.prototype.getColor=function(t,r){return void 0===r&&(r=10),this.getPalette(t,5,r)[0]},o.prototype.getPalette=function(t,o,e){var i=function(t){var r=t.colorCount,n=t.quality;if(void 0!==r&&Number.isInteger(r)){if(1===r)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");r=Math.max(r,2),r=Math.min(r,20)}else r=10;return(void 0===n||!Number.isInteger(n)||n<1)&&(n=10),{colorCount:r,quality:n}}({colorCount:o,quality:e}),u=new n(t),a=function(t,r,n){for(var o=t,e=[],i=0,u=void 0,a=void 0,s=void 0,h=void 0,c=void 0;i=125)&&(a>250&&s>250&&h>250||e.push([a,s,h]));return e}(u.getImageData().data,u.width*u.height,i.quality),s=r(a,i.colorCount);return s?s.palette():null},o.prototype.getColorFromUrl=function(t,r,n){var o=this,e=document.createElement("img");e.addEventListener("load",function(){var i=o.getPalette(e,5,n);r(i[0],t)}),e.src=t},o.prototype.getImageData=function(t,r){var n=new XMLHttpRequest;n.open("GET",t,!0),n.responseType="arraybuffer",n.onload=function(){if(200==this.status){var t=new Uint8Array(this.response);i=t.length;for(var n=new Array(i),o=0;o r ? 1 : 0; + }, + sum: function (t, r) { + var n = {}; + return t.reduce( + r + ? function (t, o, e) { + return (n.index = e), t + r.call(n, o); + } + : function (t, r) { + return t + r; + }, + 0, + ); + }, + max: function (r, n) { + return Math.max.apply(null, n ? t.map(r, n) : r); + }, + }; +var r = (function () { + var r = 5, + n = 8 - r, + o = 1e3; + function e(t, n, o) { + return (t << (2 * r)) + (n << r) + o; + } + function i(t) { + var r = [], + n = !1; + function o() { + r.sort(t), (n = !0); + } + return { + push: function (t) { + r.push(t), (n = !1); + }, + peek: function (t) { + return n || o(), void 0 === t && (t = r.length - 1), r[t]; + }, + pop: function () { + return n || o(), r.pop(); + }, + size: function () { + return r.length; + }, + map: function (t) { + return r.map(t); + }, + debug: function () { + return n || o(), r; + }, + }; + } + function u(t, r, n, o, e, i, u) { + (this.r1 = t), + (this.r2 = r), + (this.g1 = n), + (this.g2 = o), + (this.b1 = e), + (this.b2 = i), + (this.histo = u); + } + function a() { + this.vboxes = new i(function (r, n) { + return t.naturalOrder( + r.vbox.count() * r.vbox.volume(), + n.vbox.count() * n.vbox.volume(), + ); + }); + } + function s(r, n) { + if (n.count()) { + var o = n.r2 - n.r1 + 1, + i = n.g2 - n.g1 + 1, + u = t.max([o, i, n.b2 - n.b1 + 1]); + if (1 == n.count()) return [n.copy()]; + var a, + s, + h, + c, + f = 0, + v = [], + l = []; + if (u == o) + for (a = n.r1; a <= n.r2; a++) { + for (c = 0, s = n.g1; s <= n.g2; s++) + for (h = n.b1; h <= n.b2; h++) c += r[e(a, s, h)] || 0; + v[a] = f += c; + } + else if (u == i) + for (a = n.g1; a <= n.g2; a++) { + for (c = 0, s = n.r1; s <= n.r2; s++) + for (h = n.b1; h <= n.b2; h++) c += r[e(s, a, h)] || 0; + v[a] = f += c; + } + else + for (a = n.b1; a <= n.b2; a++) { + for (c = 0, s = n.r1; s <= n.r2; s++) + for (h = n.g1; h <= n.g2; h++) c += r[e(s, h, a)] || 0; + v[a] = f += c; + } + return ( + v.forEach(function (t, r) { + l[r] = f - t; + }), + (function (t) { + var r, + o, + e, + i, + u, + s = t + '1', + h = t + '2', + c = 0; + for (a = n[s]; a <= n[h]; a++) + if (v[a] > f / 2) { + for ( + e = n.copy(), + i = n.copy(), + u = + (r = a - n[s]) <= (o = n[h] - a) + ? Math.min(n[h] - 1, ~~(a + o / 2)) + : Math.max(n[s], ~~(a - 1 - r / 2)); + !v[u]; + + ) + u++; + for (c = l[u]; !c && v[u - 1]; ) c = l[--u]; + return (e[h] = u), (i[s] = e[h] + 1), [e, i]; + } + })(u == o ? 'r' : u == i ? 'g' : 'b') + ); + } + } + return ( + (u.prototype = { + volume: function (t) { + return ( + (this._volume && !t) || + (this._volume = + (this.r2 - this.r1 + 1) * + (this.g2 - this.g1 + 1) * + (this.b2 - this.b1 + 1)), + this._volume + ); + }, + count: function (t) { + var r = this.histo; + if (!this._count_set || t) { + var n, + o, + i, + u = 0; + for (n = this.r1; n <= this.r2; n++) + for (o = this.g1; o <= this.g2; o++) + for (i = this.b1; i <= this.b2; i++) u += r[e(n, o, i)] || 0; + (this._count = u), (this._count_set = !0); + } + return this._count; + }, + copy: function () { + return new u(this.r1, this.r2, this.g1, this.g2, this.b1, this.b2, this.histo); + }, + avg: function (t) { + var n = this.histo; + if (!this._avg || t) { + var o, + i, + u, + a, + s = 0, + h = 1 << (8 - r), + c = 0, + f = 0, + v = 0; + for (i = this.r1; i <= this.r2; i++) + for (u = this.g1; u <= this.g2; u++) + for (a = this.b1; a <= this.b2; a++) + (s += o = n[e(i, u, a)] || 0), + (c += o * (i + 0.5) * h), + (f += o * (u + 0.5) * h), + (v += o * (a + 0.5) * h); + this._avg = s + ? [~~(c / s), ~~(f / s), ~~(v / s)] + : [ + ~~((h * (this.r1 + this.r2 + 1)) / 2), + ~~((h * (this.g1 + this.g2 + 1)) / 2), + ~~((h * (this.b1 + this.b2 + 1)) / 2), + ]; + } + return this._avg; + }, + contains: function (t) { + var r = t[0] >> n; + return ( + (gval = t[1] >> n), + (bval = t[2] >> n), + r >= this.r1 && + r <= this.r2 && + gval >= this.g1 && + gval <= this.g2 && + bval >= this.b1 && + bval <= this.b2 + ); + }, + }), + (a.prototype = { + push: function (t) { + this.vboxes.push({ vbox: t, color: t.avg() }); + }, + palette: function () { + return this.vboxes.map(function (t) { + return t.color; + }); + }, + size: function () { + return this.vboxes.size(); + }, + map: function (t) { + for (var r = this.vboxes, n = 0; n < r.size(); n++) + if (r.peek(n).vbox.contains(t)) return r.peek(n).color; + return this.nearest(t); + }, + nearest: function (t) { + for (var r, n, o, e = this.vboxes, i = 0; i < e.size(); i++) + ((n = Math.sqrt( + Math.pow(t[0] - e.peek(i).color[0], 2) + + Math.pow(t[1] - e.peek(i).color[1], 2) + + Math.pow(t[2] - e.peek(i).color[2], 2), + )) < r || + void 0 === r) && + ((r = n), (o = e.peek(i).color)); + return o; + }, + forcebw: function () { + var r = this.vboxes; + r.sort(function (r, n) { + return t.naturalOrder(t.sum(r.color), t.sum(n.color)); + }); + var n = r[0].color; + n[0] < 5 && n[1] < 5 && n[2] < 5 && (r[0].color = [0, 0, 0]); + var o = r.length - 1, + e = r[o].color; + e[0] > 251 && e[1] > 251 && e[2] > 251 && (r[o].color = [255, 255, 255]); + }, + }), + { + quantize: function (h, c) { + if (!h.length || c < 2 || c > 256) return !1; + var f = (function (t) { + var o, + i = new Array(1 << (3 * r)); + return ( + t.forEach(function (t) { + (o = e(t[0] >> n, t[1] >> n, t[2] >> n)), (i[o] = (i[o] || 0) + 1); + }), + i + ); + })(h); + f.forEach(function () {}); + var v = (function (t, r) { + var o, + e, + i, + a = 1e6, + s = 0, + h = 1e6, + c = 0, + f = 1e6, + v = 0; + return ( + t.forEach(function (t) { + (o = t[0] >> n) < a ? (a = o) : o > s && (s = o), + (e = t[1] >> n) < h ? (h = e) : e > c && (c = e), + (i = t[2] >> n) < f ? (f = i) : i > v && (v = i); + }), + new u(a, s, h, c, f, v, r) + ); + })(h, f), + l = new i(function (r, n) { + return t.naturalOrder(r.count(), n.count()); + }); + function g(t, r) { + for (var n, e = t.size(), i = 0; i < o; ) { + if (e >= r) return; + if (i++ > o) return; + if ((n = t.pop()).count()) { + var u = s(f, n), + a = u[0], + h = u[1]; + if (!a) return; + t.push(a), h && (t.push(h), e++); + } else t.push(n), i++; + } + } + l.push(v), g(l, 0.75 * c); + for ( + var p = new i(function (r, n) { + return t.naturalOrder(r.count() * r.volume(), n.count() * n.volume()); + }); + l.size(); + + ) + p.push(l.pop()); + g(p, c); + for (var b = new a(); p.size(); ) b.push(p.pop()); + return b; + }, + } + ); + })().quantize, + n = function (t) { + (this.canvas = document.createElement('canvas')), + (this.context = this.canvas.getContext('2d')), + (this.width = this.canvas.width = t.naturalWidth), + (this.height = this.canvas.height = t.naturalHeight), + this.context.drawImage(t, 0, 0, this.width, this.height); + }; +n.prototype.getImageData = function () { + return this.context.getImageData(0, 0, this.width, this.height); +}; +var o = function () {}; +(o.prototype.getColor = function (t, r) { + return void 0 === r && (r = 10), this.getPalette(t, 5, r)[0]; +}), + (o.prototype.getPalette = function (t, o, e) { + var i = (function (t) { + var r = t.colorCount, + n = t.quality; + if (void 0 !== r && Number.isInteger(r)) { + if (1 === r) + throw new Error( + 'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()', + ); + (r = Math.max(r, 2)), (r = Math.min(r, 20)); + } else r = 10; + return ( + (void 0 === n || !Number.isInteger(n) || n < 1) && (n = 10), + { colorCount: r, quality: n } + ); + })({ colorCount: o, quality: e }), + u = new n(t), + a = (function (t, r, n) { + for ( + var o = t, + e = [], + i = 0, + u = void 0, + a = void 0, + s = void 0, + h = void 0, + c = void 0; + i < r; + i += n + ) + (a = o[0 + (u = 4 * i)]), + (s = o[u + 1]), + (h = o[u + 2]), + (void 0 === (c = o[u + 3]) || c >= 125) && + ((a > 250 && s > 250 && h > 250) || e.push([a, s, h])); + return e; + })(u.getImageData().data, u.width * u.height, i.quality), + s = r(a, i.colorCount); + return s ? s.palette() : null; + }), + (o.prototype.getColorFromUrl = function (t, r, n) { + var o = this, + e = document.createElement('img'); + e.addEventListener('load', function () { + var i = o.getPalette(e, 5, n); + r(i[0], t); + }), + (e.src = t); + }), + (o.prototype.getImageData = function (t, r) { + var n = new XMLHttpRequest(); + n.open('GET', t, !0), + (n.responseType = 'arraybuffer'), + (n.onload = function () { + if (200 == this.status) { + var t = new Uint8Array(this.response); + i = t.length; + for (var n = new Array(i), o = 0; o < t.length; o++) + n[o] = String.fromCharCode(t[o]); + var e = n.join(''), + u = window.btoa(e); + r('data:image/png;base64,' + u); + } + }), + n.send(); + }), + (o.prototype.getColorAsync = function (t, r, n) { + var o = this; + this.getImageData(t, function (t) { + var e = document.createElement('img'); + e.addEventListener('load', function () { + var t = o.getPalette(e, 5, n); + r(t[0], this); + }), + (e.src = t); + }); + }); +export default o; diff --git a/src/components/album/index.scss b/src/components/album/index.scss index e69de29..1a1f0eb 100644 --- a/src/components/album/index.scss +++ b/src/components/album/index.scss @@ -0,0 +1 @@ +/** @format */ diff --git a/src/components/album/index.tsx b/src/components/album/index.tsx index e142f10..d8c0e01 100644 --- a/src/components/album/index.tsx +++ b/src/components/album/index.tsx @@ -1,17 +1,16 @@ -import { defineComponent, } from "vue"; -import { useRoute, useRouter } from "vue-router"; -import "./index.scss"; +/** @format */ -export default defineComponent({ - name: "Album", - setup(props, context) { - const router = useRouter(); +import { defineComponent } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import './index.scss'; - return () => { +export default defineComponent({ + name: 'Album', + setup(props, context) { + const router = useRouter(); - return ( -
album
- ); - }; - }, + return () => { + return
album
; + }; + }, }); diff --git a/src/components/artist/album/index.scss b/src/components/artist/album/index.scss index 7f24041..5b9005e 100644 --- a/src/components/artist/album/index.scss +++ b/src/components/artist/album/index.scss @@ -1,43 +1,45 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .album-wrap { - @include flex(); - flex-wrap: wrap; - min-height: 400px; - padding-top: 1em; - - .album-item { - width: 15%; - transition: transform 0.2s, filter 0.14s; - cursor: pointer; - - &:hover { - filter: drop-shadow(0 0 30px rgba(100, 100, 100, 0.3)); - transform: translateY(-6px); - } - - img { - width: 100%; - border-radius: 8px; - } - - &:not(:nth-of-type(6n)) { - margin-right: 2%; - } - &:nth-of-type(n + 7) { - margin-bottom: 2%; - } - - h6 { - color: #444; - padding: 1em 0; - text-align: justify; - line-height: 1.4; - } - - p { - color: #666; - padding-bottom: 1em; - } - } + @include flex(); + flex-wrap: wrap; + min-height: 400px; + padding-top: 1em; + + .album-item { + width: 15%; + transition: transform 0.2s, filter 0.14s; + cursor: pointer; + + &:hover { + filter: drop-shadow(0 0 30px rgba(100, 100, 100, 0.3)); + transform: translateY(-6px); + } + + img { + width: 100%; + border-radius: 8px; + } + + &:not(:nth-of-type(6n)) { + margin-right: 2%; + } + &:nth-of-type(n + 7) { + margin-bottom: 2%; + } + + h6 { + color: #444; + padding: 1em 0; + text-align: justify; + line-height: 1.4; + } + + p { + color: #666; + padding-bottom: 1em; + } + } } diff --git a/src/components/artist/album/index.tsx b/src/components/artist/album/index.tsx index 339c146..eff74f7 100644 --- a/src/components/artist/album/index.tsx +++ b/src/components/artist/album/index.tsx @@ -1,70 +1,68 @@ -import { reactive, shallowReactive, onActivated, defineComponent, ref } from "vue"; -import { - useRouter, - onBeforeRouteUpdate, - RouteLocationNormalized, -} from "vue-router"; -import "./index.scss"; +/** @format */ -import { getLocaleDate, padPicCrop, EMPTY_OBJ, freeze } from "@utils/index"; -import { artistAlbum } from "@api/singer"; -import { AlbumInfo } from "@/types/album"; -import RoutePagination from "@/widgets/route-pagination"; -import AlbumList from "@/widgets/album-list"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { reactive, shallowReactive, onActivated, defineComponent, ref } from 'vue'; +import { useRouter, onBeforeRouteUpdate, RouteLocationNormalized } from 'vue-router'; +import './index.scss'; + +import { getLocaleDate, padPicCrop, EMPTY_OBJ, freeze } from '@utils/index'; +import { artistAlbum } from '@api/singer'; +import { AlbumInfo } from '@/types/album'; +import RoutePagination from '@/widgets/route-pagination'; +import AlbumList from '@/widgets/album-list'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; const defaultSingerALbumLimit = PAGE_SIZE[COMPONENT_NAME.ARTIST_ALBUM]; export interface RealAlbumInfo { - albumList: AlbumInfo[]; - hasMore: boolean; + albumList: AlbumInfo[]; + hasMore: boolean; } export default defineComponent({ - name: COMPONENT_NAME.ARTIST_ALBUM, - setup(props, { slots, emit }) { - const router = useRouter(); - const albumInfo = shallowReactive({ - albumList: [], - hasMore: true, - }); - - const getAlbum = async (route: RouteLocationNormalized) => { - const { id, limit = defaultSingerALbumLimit, offset } = route.query as PlainObject; - const { hotAlbums = [], more } = await artistAlbum({ - id, - limit, - offset, - }); - albumInfo.hasMore = more; - hotAlbums.forEach((item: AlbumInfo) => { - item.publishTimeStr = getLocaleDate(item.publishTime, { - delimiter: "-", - divide: "day", - }); - }); - albumInfo.albumList = freeze(hotAlbums); - }; + name: COMPONENT_NAME.ARTIST_ALBUM, + setup(props, { slots, emit }) { + const router = useRouter(); + const albumInfo = shallowReactive({ + albumList: [], + hasMore: true, + }); + + const getAlbum = async (route: RouteLocationNormalized) => { + const { id, limit = defaultSingerALbumLimit, offset } = route.query as PlainObject; + const { hotAlbums = [], more } = await artistAlbum({ + id, + limit, + offset, + }); + albumInfo.hasMore = more; + hotAlbums.forEach((item: AlbumInfo) => { + item.publishTimeStr = getLocaleDate(item.publishTime, { + delimiter: '-', + divide: 'day', + }); + }); + albumInfo.albumList = freeze(hotAlbums); + }; + + onActivated(() => { + getAlbum(router.currentRoute.value); + }); - onActivated(() => { - getAlbum(router.currentRoute.value); - }); + onFilteredBeforeRouteUpdate((to) => { + getAlbum(to); + }); - onFilteredBeforeRouteUpdate((to) => { - getAlbum(to); - }); - - return () => ( - <> -
- -
- - ); - }, + return () => ( + <> +
+ +
+ + ); + }, }); diff --git a/src/components/artist/allSongs/index.scss b/src/components/artist/allSongs/index.scss index 8da8872..464420f 100644 --- a/src/components/artist/allSongs/index.scss +++ b/src/components/artist/allSongs/index.scss @@ -1,10 +1,12 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .allSongs-header { - @include flexHe(); - padding-bottom: 20px; + @include flexHe(); + padding-bottom: 20px; } .allSongs-wrap { - min-height: 20em; -} \ No newline at end of file + min-height: 20em; +} diff --git a/src/components/artist/allSongs/index.tsx b/src/components/artist/allSongs/index.tsx index 77a4e2b..c9d7640 100644 --- a/src/components/artist/allSongs/index.tsx +++ b/src/components/artist/allSongs/index.tsx @@ -1,128 +1,124 @@ -import { shallowReactive, toRefs, onActivated, defineComponent, watch, WatchStopHandle, onDeactivated } from "vue"; +/** @format */ + +import { + shallowReactive, + toRefs, + onActivated, + defineComponent, + watch, + WatchStopHandle, + onDeactivated, +} from 'vue'; import { - useRouter, - useRoute, - onBeforeRouteUpdate, - RouteLocationNormalized, - LocationQuery, - onBeforeRouteLeave, -} from "vue-router"; + useRouter, + useRoute, + onBeforeRouteUpdate, + RouteLocationNormalized, + LocationQuery, + onBeforeRouteLeave, +} from 'vue-router'; -import SongTable from "@/widgets/song-table"; -import RoutePagination from "@widgets/route-pagination"; -import { EMPTY_OBJ, freeze } from "@/utils"; -import { SongInfo } from "@/types/song"; -import { artistSongs } from "@api/singer"; -import "./index.scss"; -import { NRadioGroup, NSpace, NRadio } from "naive-ui"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import SongTable from '@/widgets/song-table'; +import RoutePagination from '@widgets/route-pagination'; +import { EMPTY_OBJ, freeze } from '@/utils'; +import { SongInfo } from '@/types/song'; +import { artistSongs } from '@api/singer'; +import './index.scss'; +import { NRadioGroup, NSpace, NRadio } from 'naive-ui'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; const orders = [ - { text: "最新", key: "time" }, - { text: "热门", key: "hot" }, + { text: '最新', key: 'time' }, + { text: '热门', key: 'hot' }, ]; const defaultAllSongs = { - order: orders[0].key, - limit: PAGE_SIZE[COMPONENT_NAME.ARTIST_ALLSONGS], - offset: 0, + order: orders[0].key, + limit: PAGE_SIZE[COMPONENT_NAME.ARTIST_ALLSONGS], + offset: 0, }; export default defineComponent({ - name: COMPONENT_NAME.ARTIST_ALLSONGS, - setup(props, context) { - const router = useRouter(); - const route = useRoute(); + name: COMPONENT_NAME.ARTIST_ALLSONGS, + setup(props, context) { + const router = useRouter(); + const route = useRoute(); + + const { order: dftOrder, limit: dftLimit, offset: dftOffset } = defaultAllSongs; - const { - order: dftOrder, - limit: dftLimit, - offset: dftOffset, - } = defaultAllSongs; + const songPagiInfo = shallowReactive({ + limit: dftLimit, + offset: dftOffset, + order: dftOrder, + total: 0, + sizeArr: Array(3) + .fill(0) + .map((v, i) => dftLimit * (i + 1)), + }); - const songPagiInfo = shallowReactive({ - limit: dftLimit, - offset: dftOffset, - order: dftOrder, - total: 0, - sizeArr: Array(3) - .fill(0) - .map((v, i) => dftLimit * (i + 1)), - }); + const songsData = shallowReactive({ + songList: [] as SongInfo[], + }); - const songsData = shallowReactive({ - songList: [] as SongInfo[], - }); + const getAllSongs = async (query: LocationQuery) => { + const { id, limit = dftLimit, offset = dftOffset, order = dftOrder } = query as any; + const { songs = [], total } = await artistSongs({ + id, + limit, + offset, + order, + }); + songPagiInfo.order = order; + songPagiInfo.limit = limit; + songPagiInfo.offset = offset; + songPagiInfo.total = total; + songsData.songList = freeze(songs); + }; - const getAllSongs = async (query: LocationQuery) => { - const { - id, - limit = dftLimit, - offset = dftOffset, - order = dftOrder, - } = query as any; - const { songs = [], total } = await artistSongs({ - id, - limit, - offset, - order, - }); - songPagiInfo.order = order; - songPagiInfo.limit = limit; - songPagiInfo.offset = offset; - songPagiInfo.total = total; - songsData.songList = freeze(songs); - }; + onActivated(() => { + getAllSongs(route.query); + }); - onActivated(() => { - getAllSongs(route.query); - }); - - onFilteredBeforeRouteUpdate((to) => { - getAllSongs(to.query); - }); + onFilteredBeforeRouteUpdate((to) => { + getAllSongs(to.query); + }); - const handleOrderChange = (order: string | number) => { - router.push({ - path: route.path, - query: { - ...route.query, - order - } - }); - } + const handleOrderChange = (order: string | number) => { + router.push({ + path: route.path, + query: { + ...route.query, + order, + }, + }); + }; - return () => { - const { songList } = toRefs(songsData); - return ( -
-
-
- - - { - orders.map((item, i) => ( - - {item.text} - - )) - } - - -
-
-
- -
-
- -
-
- ); - }; - }, + return () => { + const { songList } = toRefs(songsData); + return ( +
+
+
+ + + {orders.map((item, i) => ( + + {item.text} + + ))} + + +
+
+
+ +
+
+ +
+
+ ); + }; + }, }); diff --git a/src/components/artist/description/index.scss b/src/components/artist/description/index.scss index 058ddf1..0eb9cee 100644 --- a/src/components/artist/description/index.scss +++ b/src/components/artist/description/index.scss @@ -1,23 +1,24 @@ +/** @format */ -@import "@/scss/variable"; +@import '@/scss/variable'; .yplayer-artist-desc { - .artist-info { - padding-top: 20px; - h6 { - border-left: 3px solid var(--theme); - color: #555; - font-size: 15px; - font-weight: 600; - text-indent: 1em; - } + .artist-info { + padding-top: 20px; + h6 { + border-left: 3px solid var(--theme); + color: #555; + font-size: 15px; + font-weight: 600; + text-indent: 1em; + } - p { - padding-left: 2em; - padding-top: 14px; - color: #666; - font-size: 14px; - line-height: 1.7; - } - } -} \ No newline at end of file + p { + padding-left: 2em; + padding-top: 14px; + color: #666; + font-size: 14px; + line-height: 1.7; + } + } +} diff --git a/src/components/artist/description/index.tsx b/src/components/artist/description/index.tsx index 59f44df..7651d71 100644 --- a/src/components/artist/description/index.tsx +++ b/src/components/artist/description/index.tsx @@ -1,56 +1,56 @@ +/** @format */ + import { - markRaw, - getCurrentInstance, - onActivated, - defineComponent, - ComponentInternalInstance, -} from "vue"; -import { useRouter, useRoute, onBeforeRouteLeave } from "vue-router"; -import { artistDesc } from "@api/singer"; -import "./index.scss"; + markRaw, + getCurrentInstance, + onActivated, + defineComponent, + ComponentInternalInstance, +} from 'vue'; +import { useRouter, useRoute, onBeforeRouteLeave } from 'vue-router'; +import { artistDesc } from '@api/singer'; +import './index.scss'; export default defineComponent({ - name: "ArtistDesc", - setup(props, context) { - const vm = getCurrentInstance()!; - const route = useRoute(); + name: 'ArtistDesc', + setup(props, context) { + const vm = getCurrentInstance()!; + const route = useRoute(); - const artistDescInfo = markRaw({ - briefDesc: "", - introduction: [ - { - ti: "", - txt: "", - }, - ], - }); + const artistDescInfo = markRaw({ + briefDesc: '', + introduction: [ + { + ti: '', + txt: '', + }, + ], + }); - onActivated(async () => { - const { id } = route.query as PlainObject; - const { briefDesc = "", introduction = [] } = await artistDesc({ id }); - artistDescInfo.briefDesc = briefDesc; - artistDescInfo.introduction = introduction; - vm.proxy!.$forceUpdate(); - }); + onActivated(async () => { + const { id } = route.query as PlainObject; + const { briefDesc = '', introduction = [] } = await artistDesc({ id }); + artistDescInfo.briefDesc = briefDesc; + artistDescInfo.introduction = introduction; + vm.proxy!.$forceUpdate(); + }); - return () => { - const { introduction, briefDesc } = artistDescInfo; - return ( -
-
-
简介:
-

{briefDesc || "暂无"}

-
- { - introduction.map((item, i) => -
-
{item.ti}
-

{item.txt}

-
- ) - } -
- ); - }; - }, + return () => { + const { introduction, briefDesc } = artistDescInfo; + return ( +
+
+
简介:
+

{briefDesc || '暂无'}

+
+ {introduction.map((item, i) => ( +
+
{item.ti}
+

{item.txt}

+
+ ))} +
+ ); + }; + }, }); diff --git a/src/components/artist/featured/index.scss b/src/components/artist/featured/index.scss index 1ea9a6c..83a1a9e 100644 --- a/src/components/artist/featured/index.scss +++ b/src/components/artist/featured/index.scss @@ -1,5 +1,6 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .featured-songs { - -} \ No newline at end of file +} diff --git a/src/components/artist/featured/index.tsx b/src/components/artist/featured/index.tsx index 0a97120..40321c9 100644 --- a/src/components/artist/featured/index.tsx +++ b/src/components/artist/featured/index.tsx @@ -1,36 +1,38 @@ -import { shallowReactive, onActivated, defineComponent } from "vue"; -import { useRouter, useRoute, useLink, LocationQueryValue } from "vue-router"; -import MusicList from "@/widgets/music-list"; -import { artistTopSong } from "@api/singer"; -import { SongInfo } from "@/types/song"; -import "./index.scss"; -import { freeze } from "@/utils"; +/** @format */ + +import { shallowReactive, onActivated, defineComponent } from 'vue'; +import { useRouter, useRoute, useLink, LocationQueryValue } from 'vue-router'; +import MusicList from '@/widgets/music-list'; +import { artistTopSong } from '@api/singer'; +import { SongInfo } from '@/types/song'; +import './index.scss'; +import { freeze } from '@/utils'; export default defineComponent({ - name: "ArtistFeatured", - setup(props, { emit, slots }) { - const router = useRouter(); - const route = useRoute(); + name: 'ArtistFeatured', + setup(props, { emit, slots }) { + const router = useRouter(); + const route = useRoute(); - const featuredData = shallowReactive({ - hotSongs: [] as SongInfo[], - }); + const featuredData = shallowReactive({ + hotSongs: [] as SongInfo[], + }); - onActivated(async () => { - const { songs = [] } = await artistTopSong({ - id: route.query.id as string, - }); - featuredData.hotSongs = songs; - }); + onActivated(async () => { + const { songs = [] } = await artistTopSong({ + id: route.query.id as string, + }); + featuredData.hotSongs = songs; + }); - return () => ( - - ); - }, + return () => ( + + ); + }, }); diff --git a/src/components/artist/index.scss b/src/components/artist/index.scss index 6fea0b7..dae127f 100644 --- a/src/components/artist/index.scss +++ b/src/components/artist/index.scss @@ -1,74 +1,74 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .yplayer-artist { - height: 100%; + height: 100%; } .artist-container { - height: 100%; + height: 100%; } -.artist-top { - - &-left { - padding: 0 28px; - text-align: center; +.artist-top { + &-left { + padding: 0 28px; + text-align: center; - .artist-avatar { - img { - width: 100%; - border-radius: 100%; - } - } - } + .artist-avatar { + img { + width: 100%; + border-radius: 100%; + } + } + } - &-right { - .artist-name { - padding: 18px 0 14px 0; - .artist-real-name { - color: #444; - font-size: 26px; - font-weight: 600; - } + &-right { + .artist-name { + padding: 18px 0 14px 0; + .artist-real-name { + color: #444; + font-size: 26px; + font-weight: 600; + } - .artist-alias { - margin-left: 1em; - color: #999; - font-size: 13px; - } - } + .artist-alias { + margin-left: 1em; + color: #999; + font-size: 13px; + } + } - .artist-operate { - @include flexVc(); - padding: 10px 0; - } + .artist-operate { + @include flexVc(); + padding: 10px 0; + } - .artist-info { - @include flexVc(); - padding-top: 20px; - h6 { - padding-bottom: 10px; - padding-right: 10px; - color: #333; - font-size: 14px; - font-weight: 600; - } + .artist-info { + @include flexVc(); + padding-top: 20px; + h6 { + padding-bottom: 10px; + padding-right: 10px; + color: #333; + font-size: 14px; + font-weight: 600; + } - p { - color: #666; - padding-bottom: 10px; - font-size: 14px; - text-align: justify; - line-height: 1.6; - } - } - } + p { + color: #666; + padding-bottom: 10px; + font-size: 14px; + text-align: justify; + line-height: 1.6; + } + } + } } .artist-main { - padding: 0 20px 40px 20px; + padding: 0 20px 40px 20px; } .artist-menu { - } diff --git a/src/components/artist/index.tsx b/src/components/artist/index.tsx index 324bbbe..fe51b3e 100644 --- a/src/components/artist/index.tsx +++ b/src/components/artist/index.tsx @@ -1,224 +1,219 @@ +/** @format */ + import { - markRaw, - onMounted, - reactive, - watch, - shallowReactive, - computed, - toRefs, - defineComponent, - h, - Suspense, - defineAsyncComponent, - DefineComponent, -} from "vue"; -import { - useRouter, - useRoute, - onBeforeRouteUpdate, - RouteLocationNormalized, - RouteComponent, - RouterView, -} from "vue-router"; + markRaw, + onMounted, + reactive, + watch, + shallowReactive, + computed, + toRefs, + defineComponent, + h, + Suspense, + defineAsyncComponent, + DefineComponent, +} from 'vue'; import { - NMenu, - NGrid, - NGridItem, - NSkeleton, - NSpace -} from 'naive-ui' - -import FollowButton, { FollowType } from "@widgets/follow-button"; -import CommonRouterList from "@/widgets/common-router-list"; - -import { artistSingalSongs } from "@api/singer"; -import { deepCopy, extend, objToQuery, padPicCrop } from "@/utils"; -import "./index.scss"; -import { EMPTY_OBJ, freeze } from "@/utils"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; - + useRouter, + useRoute, + onBeforeRouteUpdate, + RouteLocationNormalized, + RouteComponent, + RouterView, +} from 'vue-router'; +import { NMenu, NGrid, NGridItem, NSkeleton, NSpace } from 'naive-ui'; + +import FollowButton, { FollowType } from '@widgets/follow-button'; +import CommonRouterList from '@/widgets/common-router-list'; + +import { artistSingalSongs } from '@api/singer'; +import { deepCopy, extend, objToQuery, padPicCrop } from '@/utils'; +import './index.scss'; +import { EMPTY_OBJ, freeze } from '@/utils'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export type Artist = { - alias: string[]; - briefDesc: string; - followed: boolean; - picUrl: string; - publishTime: number; - name: string; - id: string | number; + alias: string[]; + briefDesc: string; + followed: boolean; + picUrl: string; + publishTime: number; + name: string; + id: string | number; }; export type ArtistMenu = { - text: string; - key: string; + text: string; + key: string; }; export interface MenuList extends ArtistMenu { - to: string + to: string; } const artistMenu: readonly ArtistMenu[] = freeze([ - { text: "热门歌曲", key: "featured" }, - { text: "所有歌曲", key: "allSongs" }, - { text: "专辑", key: "album" }, - { text: "MV", key: "mv" }, - { text: "相似歌手", key: "similarSinger" }, - { text: "详情", key: "description" }, + { text: '热门歌曲', key: 'featured' }, + { text: '所有歌曲', key: 'allSongs' }, + { text: '专辑', key: 'album' }, + { text: 'MV', key: 'mv' }, + { text: '相似歌手', key: 'similarSinger' }, + { text: '详情', key: 'description' }, ]); - const ArtistTop = defineComponent({ - async setup() { - const route = useRoute(); - - const information = reactive<{ - artist: Artist; - }>({ - artist: { - alias: [], - briefDesc: "", - followed: false, - picUrl: "", - publishTime: 0, - id: '', - name: "", - }, - }); - - const getArtistDetail = async (to: RouteLocationNormalized, from: RouteLocationNormalized = EMPTY_OBJ) => { - const { query: toQuery } = to; - const { query: fromQuery = EMPTY_OBJ } = from; - if (toQuery.id !== fromQuery.id) { - const { artist: artistInfo = {} } = await artistSingalSongs({ id: String(toQuery.id) }); - information.artist = artistInfo; - } - }; - - onFilteredBeforeRouteUpdate((to, from) => { - getArtistDetail(to, from); - }); - - const followChangeHandler = (isFollow: boolean) => { - information.artist.followed = isFollow; - } - - await getArtistDetail(route); - - return () => { - - const { artist } = information; - return - -
-
- -
-
-
- - -
-

- {artist.name} - {artist.alias.join("、")} -

-
- -
-
-
-
- - } - - } -}) + async setup() { + const route = useRoute(); + + const information = reactive<{ + artist: Artist; + }>({ + artist: { + alias: [], + briefDesc: '', + followed: false, + picUrl: '', + publishTime: 0, + id: '', + name: '', + }, + }); + + const getArtistDetail = async ( + to: RouteLocationNormalized, + from: RouteLocationNormalized = EMPTY_OBJ, + ) => { + const { query: toQuery } = to; + const { query: fromQuery = EMPTY_OBJ } = from; + if (toQuery.id !== fromQuery.id) { + const { artist: artistInfo = {} } = await artistSingalSongs({ + id: String(toQuery.id), + }); + information.artist = artistInfo; + } + }; + + onFilteredBeforeRouteUpdate((to, from) => { + getArtistDetail(to, from); + }); + + const followChangeHandler = (isFollow: boolean) => { + information.artist.followed = isFollow; + }; + + await getArtistDetail(route); + + return () => { + const { artist } = information; + return ( + + +
+
+ +
+
+
+ + +
+

+ {artist.name} + {artist.alias.join('、')} +

+
+ +
+
+
+
+ ); + }; + }, +}); const ArtistTopLoading = defineComponent({ - setup() { - return () => { - return ( - - - - - - ) - } - } -}) - + setup() { + return () => { + return ( + + + + + + ); + }; + }, +}); export default defineComponent({ - name: "Artist", - setup(props, { emit, slots }) { - - const router = useRouter(); - const route = useRoute(); - - //菜单列表 - const menuList = reactive( - artistMenu.reduce((total, { text, key }, i) => { - const item = {} as MenuList; - item.text = text; - item.key = key - item.to = '' - total.push(item); - return total; - }, [] as MenuList[]) - ); - - const syncMenuList = async (to: RouteLocationNormalized, from: RouteLocationNormalized = EMPTY_OBJ) => { - const { query: toQuery } = to; - const { query: fromQuery = EMPTY_OBJ } = from; - if (toQuery.id !== fromQuery.id) { - menuList.forEach((item, i) => { - item.to = `/artist/${item.key}${objToQuery(toQuery, true)}`; - }); - } - }; - - syncMenuList(route); - - onFilteredBeforeRouteUpdate((to, from) => { - syncMenuList(to, from); - }); - - - return () => { - return ( -
- - }, - fallback() { - return - } - }}> - -
-
- -
- { - renderKeepAliveRouterView() - } -
-
- ); - }; - }, + name: 'Artist', + setup(props, { emit, slots }) { + const router = useRouter(); + const route = useRoute(); + + //菜单列表 + const menuList = reactive( + artistMenu.reduce((total, { text, key }, i) => { + const item = {} as MenuList; + item.text = text; + item.key = key; + item.to = ''; + total.push(item); + return total; + }, [] as MenuList[]), + ); + + const syncMenuList = async ( + to: RouteLocationNormalized, + from: RouteLocationNormalized = EMPTY_OBJ, + ) => { + const { query: toQuery } = to; + const { query: fromQuery = EMPTY_OBJ } = from; + if (toQuery.id !== fromQuery.id) { + menuList.forEach((item, i) => { + item.to = `/artist/${item.key}${objToQuery(toQuery, true)}`; + }); + } + }; + + syncMenuList(route); + + onFilteredBeforeRouteUpdate((to, from) => { + syncMenuList(to, from); + }); + + return () => { + return ( +
+ ; + }, + fallback() { + return ; + }, + }} + > +
+
+ +
+ {renderKeepAliveRouterView()} +
+
+ ); + }; + }, }); diff --git a/src/components/artist/mv/index.scss b/src/components/artist/mv/index.scss index f7234ab..e63b877 100644 --- a/src/components/artist/mv/index.scss +++ b/src/components/artist/mv/index.scss @@ -1,2 +1,3 @@ -@import "@/scss/variable"; - \ No newline at end of file +/** @format */ + +@import '@/scss/variable'; diff --git a/src/components/artist/mv/index.tsx b/src/components/artist/mv/index.tsx index ea6edfd..d0f4ab6 100644 --- a/src/components/artist/mv/index.tsx +++ b/src/components/artist/mv/index.tsx @@ -1,58 +1,66 @@ -import { onActivated, defineComponent, ref, reactive } from "vue"; +/** @format */ + +import { onActivated, defineComponent, ref, reactive } from 'vue'; import { - useRouter, - useRoute, - RouteLocationNormalized, - onBeforeRouteUpdate, -} from "vue-router"; - -import { freeze } from "@utils/index"; -import { artistMv } from "@api/singer"; -import { Mv } from "@/types/mv"; -import "./index.scss"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; + useRouter, + useRoute, + RouteLocationNormalized, + onBeforeRouteUpdate, +} from 'vue-router'; + +import { freeze } from '@utils/index'; +import { artistMv } from '@api/singer'; +import { Mv } from '@/types/mv'; +import './index.scss'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; import MvList from '@widgets/mv-list'; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; const defaultMvslimit = PAGE_SIZE[COMPONENT_NAME.ARTIST_MV]; export interface RealMvInfo { - mvList: Mv[]; + mvList: Mv[]; } export default defineComponent({ - name: COMPONENT_NAME.ARTIST_MV, - setup(props, context) { - const router = useRouter(); - const route = useRoute(); - const mvInfo = reactive({ - mvList: [], - }); - const hasMoreRef = ref(true); - - const getArtistMvs = async (route: RouteLocationNormalized) => { - const { id, limit = defaultMvslimit, offset } = route.query as PlainObject; - const { mvs = [], hasMore } = await artistMv({ - id, - limit, - offset, - }); - hasMoreRef.value = hasMore; - mvInfo.mvList = freeze(mvs); - }; - - onActivated(() => { - getArtistMvs(route); - }); - - onFilteredBeforeRouteUpdate((to) => { - getArtistMvs(to); - }); - - return () => { - const { mvList } = mvInfo as any; - return ( - - ); - }; - }, + name: COMPONENT_NAME.ARTIST_MV, + setup(props, context) { + const router = useRouter(); + const route = useRoute(); + const mvInfo = reactive({ + mvList: [], + }); + const hasMoreRef = ref(true); + + const getArtistMvs = async (route: RouteLocationNormalized) => { + const { id, limit = defaultMvslimit, offset } = route.query as PlainObject; + const { mvs = [], hasMore } = await artistMv({ + id, + limit, + offset, + }); + hasMoreRef.value = hasMore; + mvInfo.mvList = freeze(mvs); + }; + + onActivated(() => { + getArtistMvs(route); + }); + + onFilteredBeforeRouteUpdate((to) => { + getArtistMvs(to); + }); + + return () => { + const { mvList } = mvInfo as any; + return ( + + ); + }; + }, }); diff --git a/src/components/artist/similarSinger/index.scss b/src/components/artist/similarSinger/index.scss index ed09936..e63b877 100644 --- a/src/components/artist/similarSinger/index.scss +++ b/src/components/artist/similarSinger/index.scss @@ -1 +1,3 @@ -@import "@/scss/variable"; \ No newline at end of file +/** @format */ + +@import '@/scss/variable'; diff --git a/src/components/artist/similarSinger/index.tsx b/src/components/artist/similarSinger/index.tsx index 17a902b..26551c1 100644 --- a/src/components/artist/similarSinger/index.tsx +++ b/src/components/artist/similarSinger/index.tsx @@ -1,44 +1,43 @@ -import { shallowReactive, computed, onActivated, defineComponent } from "vue"; -import { useRouter, useRoute, useLink } from "vue-router"; +/** @format */ -import { artistSimilar } from "@api/singer"; +import { shallowReactive, computed, onActivated, defineComponent } from 'vue'; +import { useRouter, useRoute, useLink } from 'vue-router'; -import ArtistList from "@/widgets/artist-list"; -import { SingerInfo } from "@/types/singer"; -import "./index.scss"; -import { freeze } from "@/utils"; +import { artistSimilar } from '@api/singer'; + +import ArtistList from '@/widgets/artist-list'; +import { SingerInfo } from '@/types/singer'; +import './index.scss'; +import { freeze } from '@/utils'; export default defineComponent({ - name: "ArtistSimilar", - setup(props, { slots, emit }) { - const router = useRouter(); - const route = useRoute(); - - const information = shallowReactive<{ - similarSingers: SingerInfo[]; - }>({ - similarSingers: [], - }); - - const getSimilarArtists = async () => { - const id = route.query.id as string; - const { artists = [] } = await artistSimilar({ id }); - information.similarSingers = freeze(artists); - }; - - onActivated(() => { - getSimilarArtists(); - }); - - return () => { - return ( -
- -
- ); - }; - }, + name: 'ArtistSimilar', + setup(props, { slots, emit }) { + const router = useRouter(); + const route = useRoute(); + + const information = shallowReactive<{ + similarSingers: SingerInfo[]; + }>({ + similarSingers: [], + }); + + const getSimilarArtists = async () => { + const id = route.query.id as string; + const { artists = [] } = await artistSimilar({ id }); + information.similarSingers = freeze(artists); + }; + + onActivated(() => { + getSimilarArtists(); + }); + + return () => { + return ( +
+ +
+ ); + }; + }, }); diff --git a/src/components/home-header/index.scss b/src/components/home-header/index.scss index 1137494..8ac5619 100644 --- a/src/components/home-header/index.scss +++ b/src/components/home-header/index.scss @@ -1,64 +1,62 @@ -@import "@scss/variable"; +/** @format */ -.main-header { - - .header-left, - .header-right { - @include flexVc(); - } - - .page-turn { - @include flexVc(); - margin-right: 30px; - color: #777; - font-size: 24px; - cursor: pointer; - - &[disabled="true"] { - cursor: initial; - color: rgba(180,180,180); - } - - &:hover { - color: var(--theme); - } - - } - - .music-search-input { - input { - min-width: 220px; - max-width: 280px; - // border: none; - // background-color: rgba(140, 140, 140, 0.12); - // border-radius: 20px; - &::-webkit-input-placeholder { - color: #888; - } - } - } - - .header-fullscreen { - position: relative; - width: 25px; - height: 25px; - color: #555; - cursor: pointer; - transition: color .12s; - - &:hover { - color: var(--theme); - } - - .icon-fullscreen, - .icon-cancelfullscreen { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - font-size: 24px; - - } - } +@import '@scss/variable'; +.main-header { + .header-left, + .header-right { + @include flexVc(); + } + + .page-turn { + @include flexVc(); + margin-right: 30px; + color: #777; + font-size: 24px; + cursor: pointer; + + &[disabled='true'] { + cursor: initial; + color: rgba(180, 180, 180); + } + + &:hover { + color: var(--theme); + } + } + + .music-search-input { + input { + min-width: 220px; + max-width: 280px; + // border: none; + // background-color: rgba(140, 140, 140, 0.12); + // border-radius: 20px; + &::-webkit-input-placeholder { + color: #888; + } + } + } + + .header-fullscreen { + position: relative; + width: 25px; + height: 25px; + color: #555; + cursor: pointer; + transition: color 0.12s; + + &:hover { + color: var(--theme); + } + + .icon-fullscreen, + .icon-cancelfullscreen { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 24px; + } + } } diff --git a/src/components/home-header/index.tsx b/src/components/home-header/index.tsx index 40b39fe..9fa5d85 100644 --- a/src/components/home-header/index.tsx +++ b/src/components/home-header/index.tsx @@ -1,85 +1,85 @@ -import { computed, defineComponent, ref, shallowReactive, Suspense, watch } from "vue"; -import { useRoute, useRouter } from "vue-router"; -import HeaderSetting from "@/widgets/header-setting"; -import Battery from "@/widgets/battery"; -import UserLogin from "@widgets/user-login"; -import { NMention, NInput, NGrid, NGridItem } from "naive-ui"; -import { useBetterFullscreen } from "@/hooks"; -import "./index.scss"; +/** @format */ + +import { computed, defineComponent, ref, shallowReactive, Suspense, watch } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import HeaderSetting from '@/widgets/header-setting'; +import Battery from '@/widgets/battery'; +import UserLogin from '@widgets/user-login'; +import { NMention, NInput, NGrid, NGridItem } from 'naive-ui'; +import { useBetterFullscreen } from '@/hooks'; +import './index.scss'; export default defineComponent({ - name: "HomeHeader", - setup(props, context) { - const searchWord = ref(""); - const route = useRoute(); - const router = useRouter(); - const routeBack = () => router.back() - const routeNext = () => router.forward() + name: 'HomeHeader', + setup(props, context) { + const searchWord = ref(''); + const route = useRoute(); + const router = useRouter(); + const routeBack = () => router.back(); + const routeNext = () => router.forward(); - const updateSearchValue = (keywords: string) => { - searchWord.value = keywords; - }; - const searchChange = () => { - router.push({ - path: "/search", - query: { - keywords: searchWord.value, - }, - }); - }; + const updateSearchValue = (keywords: string) => { + searchWord.value = keywords; + }; + const searchChange = () => { + router.push({ + path: '/search', + query: { + keywords: searchWord.value, + }, + }); + }; - const { isSupported, toggle, isFullscreen } = useBetterFullscreen(); - if (!isSupported) { - return - } - const renderFullScreen = () => ( -
- - -
- ) + const { isSupported, toggle, isFullscreen } = useBetterFullscreen(); + if (!isSupported) { + return; + } + const renderFullScreen = () => ( +
+ + +
+ ); - return () => { - return ( - <> -
-
- -
+ return () => { + return ( + <> +
+
+ +
-
- -
+
+ +
- -
+ +
-
- - - - { - renderFullScreen() - } -
- - ); - }; - }, +
+ + + + {renderFullScreen()} +
+ + ); + }; + }, }); diff --git a/src/components/home-left/index.scss b/src/components/home-left/index.scss index fb1e5a6..cd9ed1a 100644 --- a/src/components/home-left/index.scss +++ b/src/components/home-left/index.scss @@ -1,32 +1,34 @@ -@import "@scss/variable"; +/** @format */ -.home-left { - height: 100vh; - background-color: $listBgc; +@import '@scss/variable'; - .home-logo { - @include flexVc(); - height: $homeTopHeaderHeight; - padding-left: 30px; - padding-right: 16px; +.home-left { + height: 100vh; + background-color: $listBgc; - .site-logo { - display: block; - width: 100px; - height: 40px; - background-color: var(--theme); - -webkit-mask: url("@assets/img/guoxiaoyou.png") no-repeat center / 100%; - cursor: pointer; - // background-repeat: no-repeat; - // filter: invert(49%) sepia(8%) saturate(3293%) hue-rotate(313deg) - // brightness(119%) contrast(100%); - } - } + .home-logo { + @include flexVc(); + height: $homeTopHeaderHeight; + padding-left: 30px; + padding-right: 16px; - .home-category { - width: 100%; - height: calc(100% - #{$homeTopHeaderHeight}); - padding-left: 20px; - padding-right: 20px; - } + .site-logo { + display: block; + width: 100px; + height: 40px; + background-color: var(--theme); + -webkit-mask: url('@assets/img/guoxiaoyou.png') no-repeat center / 100%; + cursor: pointer; + // background-repeat: no-repeat; + // filter: invert(49%) sepia(8%) saturate(3293%) hue-rotate(313deg) + // brightness(119%) contrast(100%); + } + } + + .home-category { + width: 100%; + height: calc(100% - #{$homeTopHeaderHeight}); + padding-left: 20px; + padding-right: 20px; + } } diff --git a/src/components/home-left/index.tsx b/src/components/home-left/index.tsx index 91046f3..7edfeba 100644 --- a/src/components/home-left/index.tsx +++ b/src/components/home-left/index.tsx @@ -1,110 +1,108 @@ +/** @format */ + import { - computed, - defineComponent, - markRaw, - onMounted, - reactive, - readonly, - ref, -} from "vue"; -import AsideRouterList from "@/widgets/aside-route-list"; -import { playlistCate } from "@api/playlist"; -import "./index.scss"; -import useUserStore from "@/stores/user"; + computed, + defineComponent, + markRaw, + onMounted, + reactive, + readonly, + ref, +} from 'vue'; +import AsideRouterList from '@/widgets/aside-route-list'; +import { playlistCate } from '@api/playlist'; +import './index.scss'; +import useUserStore from '@/stores/user'; // import guoxiaoyouLogo from "@assets/img/guoxiaoyou.png"; export default defineComponent({ - name: "HomeLeft", - setup(props, { slots, emit }) { - - const userStore = useUserStore(); - - // const logo = ref(guoxiaoyouLogo); + name: 'HomeLeft', + setup(props, { slots, emit }) { + const userStore = useUserStore(); - const onlineMusic = computed(() => { - const list = [ - { text: "音乐馆", to: { path: "/musichall" } }, - { text: "电台", to: { path: "/musicradio" } }, - { text: "视频", to: { path: "/onlinevideo" } }, - ] - userStore.isLogin && list.push({ text: "个性推荐", to: { path: "/personalRecommend" } }); - return { - title: "在线音乐", - list - } - }) + // const logo = ref(guoxiaoyouLogo); - const createdPlaylist = computed(() => { - const list = userStore.playlist.myCreated.map(({ name, id }) => { - return { - text: name, - to: { - path: '/songlist/:id', - name: 'songlist', - params: { id } - } - } - }) - return { - title: '创建的歌单', - list, - } - }) + const onlineMusic = computed(() => { + const list = [ + { text: '音乐馆', to: { path: '/musichall' } }, + { text: '电台', to: { path: '/musicradio' } }, + { text: '视频', to: { path: '/onlinevideo' } }, + ]; + userStore.isLogin && + list.push({ text: '个性推荐', to: { path: '/personalRecommend' } }); + return { + title: '在线音乐', + list, + }; + }); - const collectedPlaylist = computed(() => { - const list = userStore.playlist.myCollection.map(({ name, id }) => { - return { - text: name, - to: { - path: '/songlist/:id', - name: 'songlist', - params: { id } - } - } - }) - return { - title: '收藏的歌单', - list, - } - }) + const createdPlaylist = computed(() => { + const list = userStore.playlist.myCreated.map(({ name, id }) => { + return { + text: name, + to: { + path: '/songlist/:id', + name: 'songlist', + params: { id }, + }, + }; + }); + return { + title: '创建的歌单', + list, + }; + }); - return () => { + const collectedPlaylist = computed(() => { + const list = userStore.playlist.myCollection.map(({ name, id }) => { + return { + text: name, + to: { + path: '/songlist/:id', + name: 'songlist', + params: { id }, + }, + }; + }); + return { + title: '收藏的歌单', + list, + }; + }); - const { list: onlineMusicList, title: onlineMusicTitle } = onlineMusic.value; - const { list: createdList, title: createdTitle } = createdPlaylist.value; - const { list: collectionList, title: collectionTitle } = collectedPlaylist.value; + return () => { + const { list: onlineMusicList, title: onlineMusicTitle } = onlineMusic.value; + const { list: createdList, title: createdTitle } = createdPlaylist.value; + const { list: collectionList, title: collectionTitle } = collectedPlaylist.value; - const listRenderArr = [ - - ] + const listRenderArr = [ + , + ]; - if (userStore.isLogin) { - listRenderArr.push( - , - - ) - } - // + if (userStore.isLogin) { + listRenderArr.push( + , + , + ); + } + // - return ( - - ); - }; - }, + return ( + + ); + }; + }, }); diff --git a/src/components/lyric/index.scss b/src/components/lyric/index.scss index 66086d6..b083350 100644 --- a/src/components/lyric/index.scss +++ b/src/components/lyric/index.scss @@ -1,167 +1,168 @@ -@use "sass:math"; -@import "@scss/variable"; +/** @format */ + +@use 'sass:math'; +@import '@scss/variable'; .player-lyric { - position: relative; - height: 100%; - overflow: hidden; - - &-exsit { - position: relative; - height: 100%; - padding-bottom: 40px; - overflow: hidden; - - .exsit-operator { - position: relative; - height: 100%; - overflow: hidden; - } - - .operator-textarea { - position: absolute; - left: 0; - right: 0; - width: 50%; - max-width: 380px; - min-width: 280px; - height: 100%; - margin: 0 auto; - scroll-behavior: smooth; - } - - .textarea-suspension { - position: absolute; - top: 50%; - z-index: 99999; - transform: translateY(-50%); - white-space: nowrap; - opacity: 0.7; - - $suspenDotSize: 4px; - .suspension-dots { - display: inline-block; - width: $suspenDotSize; - height: $suspenDotSize; - vertical-align: middle; - border-radius: 100%; - } - } - - .suspension-left { - right: calc(100% + #{math.div($oneLyricItemHeight, 2)}); - - .suspension-time { - margin-right: 10px; - font-size: 15px; - vertical-align: middle; - } - - .suspension-dots { - margin-left: 60px; - background-color: rgba(255, 255, 255, 0.2); - box-shadow: -12px 0 0 1px rgba(255, 255, 255, 0.3), - -24px 0 0 1.2px rgba(255, 255, 255, 0.4), - -36px 0 0 1.4px rgba(255, 255, 255, 0.6), - -52px 0 0 1.6px rgba(255, 255, 255, 0.8); - } - } - - .suspension-right { - @include flexHcVc(); - left: calc(100% + #{math.div($oneLyricItemHeight, 2)}); - cursor: pointer; - transition: 0.12s; - - &:hover { - opacity: 1; - filter: brightness(110%); - } - - .suspension-icon { - @include flexInline; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - margin-left: 10px; - background-color: #fff; - color: var(--theme); - font-size: 20px; - border-radius: 100%; - } - - .suspension-dots { - margin-right: 60px; - background-color: rgba(255, 255, 255, 0.2); - box-shadow: 12px 0 0 1px rgba(255, 255, 255, 0.3), - 24px 0 0 1.2px rgba(255, 255, 255, 0.4), - 36px 0 0 1.4px rgba(255, 255, 255, 0.6), - 52px 0 0 1.6px rgba(255, 255, 255, 0.8); - } - } - - .textarea-contents { - position: absolute; - top: 50%; - left: 0; - width: 100%; - font-size: 16px; - cursor: pointer; - } - - .content-lyric { - padding: 12px 0; - color: $colorbase; - text-align: center; - line-height: 1.4; - transition: color 0.23s; - &[active="true"] { - .lyric-text { - color: var(--theme); - } - } - } - } - - &[translated="true"] { - .content-lyric { - div.lyric-translation { - padding-top: 6px; - } - } - } - - &-empty { - min-height: inherit; - .empty-main { - @include flexHcVc; - height: $lyricContainerMaxheight; - min-height: inherit; - font-size: 18px; - opacity: 0.9; - } - } - - &-translate { - position: absolute; - left: 50%; - bottom: 0; - transform: translateX(-50%); - opacity: 0.8; - color: $colorbase; - font-size: 1.4rem; - line-height: 1; - font-weight: 200; - cursor: pointer; - transition: color 0.12s; - - &:hover { - opacity: 1; - } - - &[active="true"] { - color: var(--theme); - } - } + position: relative; + height: 100%; + overflow: hidden; + + &-exsit { + position: relative; + height: 100%; + padding-bottom: 40px; + overflow: hidden; + + .exsit-operator { + position: relative; + height: 100%; + overflow: hidden; + } + + .operator-textarea { + position: absolute; + left: 0; + right: 0; + width: 50%; + max-width: 380px; + min-width: 280px; + height: 100%; + margin: 0 auto; + scroll-behavior: smooth; + } + + .textarea-suspension { + position: absolute; + top: 50%; + z-index: 99999; + transform: translateY(-50%); + white-space: nowrap; + opacity: 0.7; + + $suspenDotSize: 4px; + .suspension-dots { + display: inline-block; + width: $suspenDotSize; + height: $suspenDotSize; + vertical-align: middle; + border-radius: 100%; + } + } + + .suspension-left { + right: calc(100% + #{math.div($oneLyricItemHeight, 2)}); + + .suspension-time { + margin-right: 10px; + font-size: 15px; + vertical-align: middle; + } + + .suspension-dots { + margin-left: 60px; + background-color: rgba(255, 255, 255, 0.2); + box-shadow: -12px 0 0 1px rgba(255, 255, 255, 0.3), + -24px 0 0 1.2px rgba(255, 255, 255, 0.4), + -36px 0 0 1.4px rgba(255, 255, 255, 0.6), + -52px 0 0 1.6px rgba(255, 255, 255, 0.8); + } + } + + .suspension-right { + @include flexHcVc(); + left: calc(100% + #{math.div($oneLyricItemHeight, 2)}); + cursor: pointer; + transition: 0.12s; + + &:hover { + opacity: 1; + filter: brightness(110%); + } + + .suspension-icon { + @include flexInline; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + margin-left: 10px; + background-color: #fff; + color: var(--theme); + font-size: 20px; + border-radius: 100%; + } + + .suspension-dots { + margin-right: 60px; + background-color: rgba(255, 255, 255, 0.2); + box-shadow: 12px 0 0 1px rgba(255, 255, 255, 0.3), + 24px 0 0 1.2px rgba(255, 255, 255, 0.4), 36px 0 0 1.4px rgba(255, 255, 255, 0.6), + 52px 0 0 1.6px rgba(255, 255, 255, 0.8); + } + } + + .textarea-contents { + position: absolute; + top: 50%; + left: 0; + width: 100%; + font-size: 16px; + cursor: pointer; + } + + .content-lyric { + padding: 12px 0; + color: $colorbase; + text-align: center; + line-height: 1.4; + transition: color 0.23s; + &[active='true'] { + .lyric-text { + color: var(--theme); + } + } + } + } + + &[translated='true'] { + .content-lyric { + div.lyric-translation { + padding-top: 6px; + } + } + } + + &-empty { + min-height: inherit; + .empty-main { + @include flexHcVc; + height: $lyricContainerMaxheight; + min-height: inherit; + font-size: 18px; + opacity: 0.9; + } + } + + &-translate { + position: absolute; + left: 50%; + bottom: 0; + transform: translateX(-50%); + opacity: 0.8; + color: $colorbase; + font-size: 1.4rem; + line-height: 1; + font-weight: 200; + cursor: pointer; + transition: color 0.12s; + + &:hover { + opacity: 1; + } + + &[active='true'] { + color: var(--theme); + } + } } diff --git a/src/components/lyric/index.tsx b/src/components/lyric/index.tsx index 6255771..9919fc4 100644 --- a/src/components/lyric/index.tsx +++ b/src/components/lyric/index.tsx @@ -1,483 +1,474 @@ +/** @format */ + +import { + defineComponent, + toRefs, + onMounted, + isReactive, + nextTick, + toRaw, + ref, + shallowRef, + shallowReactive, + watch, + reactive, + onBeforeUnmount, + markRaw, +} from 'vue'; +import { computedStyle, NOOP, UNICODE_CHAR } from '@utils/index'; +import './index.scss'; +import usePlayerStore from '@/stores/player'; import { - defineComponent, - toRefs, - onMounted, - isReactive, - nextTick, - toRaw, - ref, - shallowRef, - shallowReactive, - watch, - reactive, - onBeforeUnmount, - markRaw, -} from "vue"; -import { computedStyle, NOOP, UNICODE_CHAR } from "@utils/index"; -import "./index.scss"; -import usePlayerStore from "@/stores/player"; -import { currentTimeRefGlobal, nextSeekTimeRefGlobal, playingRefGlobal } from "@/stores/audio"; -import { useEventListener } from "@vueuse/core"; + currentTimeRefGlobal, + nextSeekTimeRefGlobal, + playingRefGlobal, +} from '@/stores/audio'; +import { useEventListener } from '@vueuse/core'; //歌词移动时的悬浮条 export type Suspension = { - isShow: boolean; //是否显示悬浮条 - time: string; //悬浮条左侧的时间 - interval: number; //等待多少秒隐藏悬浮框 - tarIndex: number; //目标播放索引 - hiddenTimer: ReturnType; //隐藏悬浮条的定时器 + isShow: boolean; //是否显示悬浮条 + time: string; //悬浮条左侧的时间 + interval: number; //等待多少秒隐藏悬浮框 + tarIndex: number; //目标播放索引 + hiddenTimer: ReturnType; //隐藏悬浮条的定时器 }; //歌词容器 export type LrcWrap = { - height: number; //高度 - // isWheel: boolean; //是否在用鼠标滚轮/笔记本触控板滑动 - isMouseDown: boolean; //是否按下 - canMouseMove: boolean; //是否可以移动 - mouseMoveRateFactor: number; //鼠标移动速率 - wheelRateFactor: number; //鼠标滚轮滑动速率 - transition: { - defaultVal: string; //默认过渡时长 - value: string; //当前过渡时长 - }; //过渡效果 - translateY: { - current: number; //当前偏移值 - min: number; //偏移值边界——最小 - max: number; //偏移值边界——最大 - }; //垂直方向偏移值 + height: number; //高度 + // isWheel: boolean; //是否在用鼠标滚轮/笔记本触控板滑动 + isMouseDown: boolean; //是否按下 + canMouseMove: boolean; //是否可以移动 + mouseMoveRateFactor: number; //鼠标移动速率 + wheelRateFactor: number; //鼠标滚轮滑动速率 + transition: { + defaultVal: string; //默认过渡时长 + value: string; //当前过渡时长 + }; //过渡效果 + translateY: { + current: number; //当前偏移值 + min: number; //偏移值边界——最小 + max: number; //偏移值边界——最大 + }; //垂直方向偏移值 }; //歌词元素 export type LrcItemInfo = { - heightArr: number[]; //每条歌词元素的高度数组 - lastHeight: number; //最后一条歌词元素的高度 - firstHeight: number; //第一条歌词元素的高度 + heightArr: number[]; //每条歌词元素的高度数组 + lastHeight: number; //最后一条歌词元素的高度 + firstHeight: number; //第一条歌词元素的高度 }; export default defineComponent({ - name: "PlayerLyric", - setup(props, { slots, emit }) { - const playerStore = usePlayerStore(); - - //悬浮控制播放区 - const suspension = shallowReactive({ - isShow: false, - time: "00:00", - interval: 5000, - hiddenTimer: setTimeout(NOOP), - tarIndex: 0, - }); - - //歌词容器 - const lrcWrap = reactive({ - height: 0, - isMouseDown: false, - canMouseMove: false, - mouseMoveRateFactor: 1, - wheelRateFactor: 1, - transition: { - defaultVal: "0.28s", - value: "0.28s", - }, - translateY: { - current: 0, - min: 0, - max: 0, - }, - }); - - //歌词元素 - const lrcItemInfo = markRaw({ - heightArr: [], - lastHeight: 0, - firstHeight: 0, - }); - - //当前显示歌词的index索引 - const currentIndex = ref(0); - //是否显示翻译图标 - const showTranslation = ref(false); - - //歌词为空时的文本 - const emptyText = ref("小果音乐,让生活充满音乐~"); - - //歌词区相对定位父级元素 - const lrcContainerRef = ref(); - - //监听滚轮事件 - const wheelHandler = async (ev: WheelEvent) => { - //让suspension悬浮区显示 - setSuspensionShow(true); - //清空隐藏suspension悬浮区的定时器 - clearSuspensionHiddenTimer(); - //更新suspension悬浮区相关信息 - updateSuspensionPlayInfo(); - const { - translateY: { current }, - wheelRateFactor, - } = lrcWrap; - //1表示向下滑动,-1表示向上滑动 - const polarity = ev.deltaY > 0 ? 1 : -1; - //之所以要异步执行,是因为updateSuspensionPlayInfo中异步更新了suspension.tarIndex - await nextTick(); - const tarTranslateY = - current + - lrcItemInfo.heightArr[suspension.tarIndex] * - -polarity * - wheelRateFactor; - setTranslateY(tarTranslateY); - //重新设置隐藏suspension悬浮区的定时器 - setSuspensionHiddenTimer(); - }; - useEventListener(lrcContainerRef, "wheel", wheelHandler); - - watch( - currentTimeRefGlobal, - (val) => { - const exist = playerStore.lyricParsed.exist; - //如果当前显示着播放悬浮框或者没有歌词就return - if (suspension.isShow || !exist) return; - const index = getCurItemIndexByTime(val); - setCurItemIndex(index); - setTranslateYByDynamicIndex(index); - } - ); - - watch( - () => suspension.isShow, - (val) => { - //浮动播放条显示时 - setLrcWrapTransition(!val); - } - ); - - //歌词列表变动时 - watch( - () => playerStore.lyricParsed, - async (lyricParsed, oldLyricParsed) => { - //如果没有歌词 - if (!lyricParsed.exist) { - //重置本歌词组件的一些数据 - resetLrcData(); - return; - } - //重置播放悬浮框状态(如清除定时器) - resetSuspension(); - await nextTick(); - //DOM更新后,再更新歌词容器等相关数据(如宽高等) - updatedLrcData(); - } - ); - - //翻译图标显隐变动时 - watch(showTranslation, async (val) => { - await nextTick(); - //如果播放悬浮窗是显示的,就设置为tarIndex,否则为当前播放的歌词的索引 - const willReachIndex = suspension.isShow - ? suspension.tarIndex - : currentIndex.value; - updatedLrcData(willReachIndex); - }); - - onMounted(() => { - useEventListener(document, "mousemove", lyricMove); - useEventListener(document, "mouseup", lyricUp); - }); - - //更新将要播放的位置 - const setWillPlayTime = (time: number) => { - nextSeekTimeRefGlobal.value = time; - }; - - //设置歌词容器过渡时长 - const setLrcWrapTransition = (isTransition: boolean) => { - const transition = lrcWrap.transition; - transition.value = isTransition ? transition.defaultVal : "0s"; - }; - - //设置translateY值 - const setTranslateY = (value: number) => { - const { min, max } = lrcWrap.translateY; - if (value > max) { - value = max; - } else if (value < min) { - value = min; - } - lrcWrap.translateY.current = value; - }; - - //根据动态target索引设置当前trnslateY值 - const setTranslateYByDynamicIndex = (index: number) => { - const tarTranslateY = getTarTranslateY(index); - setTranslateY(tarTranslateY); - }; - - //根据tarIndex目标索引获取translateY值 - const getTarTranslateY = (index: number) => { - const { heightArr } = lrcItemInfo; - return ( - -heightArr.slice(0, index + 1).reduce((total, now) => total + now) + - heightArr[index] / 2 - ); - }; - - //根据translateY值获取tarIndex目标索引 - const getTarByTranslateY = () => { - const curTranslateY = lrcWrap.translateY.current; - const { heightArr } = lrcItemInfo; - const { length } = heightArr; - const offsetY = Math.abs(curTranslateY - lrcWrap.translateY.max); - let mutipleDis = 0; - let index = 0; - for (let i = 0; i < length; i++) { - const curItemh = heightArr[i]; - if (offsetY <= (mutipleDis += curItemh)) { - if (Math.abs(offsetY - mutipleDis) >= curItemh / 2) { - index = i; - } else { - index = i + 1; - } - break; - } - } - const { time, timeStr } = playerStore.lyricParsed.lrcArr[index]; - return { - time, - timeStr, - index, - }; - }; - - //根据当前播放时间获取歌词列表的index - const getCurItemIndexByTime = (time: number) => { - const lrcArr = playerStore.lyricParsed.lrcArr; - const lastIndex = lrcArr.length - 1; - let index = 0; - //如果当前时间大于第一条歌词的时间 - if (time > lrcArr[0].time) { - for (let i = 0; i < lastIndex; i++) { - if (time >= lrcArr[i].time && time < lrcArr[i + 1].time) { - index = i; - break; - } else { - index = lastIndex; - } - } - } - return index; - }; - - //切换翻译状态 - const switchTranslation = () => { - showTranslation.value = !showTranslation.value; - }; - - //重置悬浮框 - const resetSuspension = () => { - setSuspensionShow(false); - clearSuspensionHiddenTimer(); - setSuspensionTarIndex(0); - }; - - //悬浮条播放按钮的点击事件 - const suspensionPlayClick = () => { - const tarIndex = suspension.tarIndex; - setSuspensionShow(false); - clearSuspensionHiddenTimer(); - setCurItemIndex(tarIndex); - setTranslateYByDynamicIndex(tarIndex); - setWillPlayTime(playerStore.lyricParsed.lrcArr[tarIndex].time); - playingRefGlobal.value = true; - }; - - // 清除悬浮条的隐藏的timer计时器 - const clearSuspensionHiddenTimer = () => { - clearTimeout(suspension.hiddenTimer); - }; - - //设置悬浮条的隐藏的timer计时器 - const setSuspensionHiddenTimer = ( - interval: number = suspension.interval - ) => { - suspension.hiddenTimer = setTimeout(() => { - setSuspensionShow(false); - //设置lrcWraptranslateY - setTranslateYByDynamicIndex(currentIndex.value); - //定时器结束后将拖动的目标index与curIndex同步,以防不断点击翻译按钮出错。 - setSuspensionTarIndex(currentIndex.value); - }, interval); - }; - - //设置悬浮条控制的目标索引 - const setSuspensionTarIndex = (index: number) => { - suspension.tarIndex = index; - }; - - //控制悬浮条显隐 - const setSuspensionShow = (isShow: boolean) => { - suspension.isShow = isShow; - }; - - //设置悬浮条的目标播放时间 - const setSuspensionTime = (timeStr: string) => { - suspension.time = timeStr.replace(/\.\d+/, ""); - }; - - //设置当前歌词的index索引 - const setCurItemIndex = (i: number) => { - currentIndex.value = i; - }; - - //重置歌词数据 - const resetLrcData = () => { - emptyText.value = `小右没有发现歌词喔~~${UNICODE_CHAR.pensive}`; - lrcItemInfo.heightArr = []; - lrcItemInfo.lastHeight = 0; - lrcItemInfo.firstHeight = 0; - lrcWrap.height = 0; - lrcWrap.translateY = { - min: 0, - max: 0, - current: 0, - }; - }; - - const lrcContentsRef = ref(); - //更新歌词相关数据 - const updatedLrcData = (curIdx = 0) => { - const el = lrcContentsRef.value!; - const lrcWrapHeight = Number.parseFloat(computedStyle(el, "height")); - const lrcItemsHeightArr = Array.from(el.children).map((childEl) => - Number.parseFloat(computedStyle(childEl, "height")) - ); - const lrcLastItemHeight = lrcItemsHeightArr[lrcItemsHeightArr.length - 1]; - const lrcfirstItemHeight = lrcItemsHeightArr[0]; - - lrcItemInfo.heightArr = lrcItemsHeightArr; - lrcItemInfo.lastHeight = lrcLastItemHeight; - lrcItemInfo.firstHeight = lrcfirstItemHeight; - lrcWrap.height = lrcWrapHeight; - lrcWrap.translateY.min = -(lrcWrapHeight - lrcLastItemHeight / 2); - lrcWrap.translateY.max = -lrcfirstItemHeight / 2; - //根据当前播放的歌词索引来确定translateY值 - setTranslateYByDynamicIndex(curIdx); - }; - - //更新歌词悬浮条信息 - const updateSuspensionPlayInfo = () => { - const curTimeInfo = getTarByTranslateY(); - setSuspensionTime(curTimeInfo.timeStr); - setSuspensionTarIndex(curTimeInfo.index); - }; - - const lyricDown = (ev: MouseEvent) => { - if (ev.button !== 0) return; - lrcWrap.isMouseDown = true; - lrcWrap.canMouseMove = true; - setSuspensionShow(true); - clearSuspensionHiddenTimer(); - updateSuspensionPlayInfo(); - }; - - const lyricMove = (ev: MouseEvent) => { - const { - canMouseMove, - translateY: lrcWrapTranslateY, - mouseMoveRateFactor, - } = toRefs(lrcWrap); - if (!canMouseMove.value) return; - const { current } = lrcWrapTranslateY.value; - const tarTranslateY = current + ev.movementY * mouseMoveRateFactor.value; - setTranslateY(tarTranslateY); - updateSuspensionPlayInfo(); - }; - - const lyricUp = (ev: MouseEvent) => { - if (!lrcWrap.isMouseDown) return; - lrcWrap.isMouseDown = false; - lrcWrap.canMouseMove = false; - setSuspensionHiddenTimer(); - }; - - return () => { - const { exist, canTranslate, lrcArr } = playerStore.lyricParsed; - const { isShow, time } = suspension; - const showtransIcon = showTranslation.value; - const emptyTextVal = emptyText.value; - const currentIndexValue = currentIndex.value; - - return ( -
- - -
- ); - }; - }, + name: 'PlayerLyric', + setup(props, { slots, emit }) { + const playerStore = usePlayerStore(); + + //悬浮控制播放区 + const suspension = shallowReactive({ + isShow: false, + time: '00:00', + interval: 5000, + hiddenTimer: setTimeout(NOOP), + tarIndex: 0, + }); + + //歌词容器 + const lrcWrap = reactive({ + height: 0, + isMouseDown: false, + canMouseMove: false, + mouseMoveRateFactor: 1, + wheelRateFactor: 1, + transition: { + defaultVal: '0.28s', + value: '0.28s', + }, + translateY: { + current: 0, + min: 0, + max: 0, + }, + }); + + //歌词元素 + const lrcItemInfo = markRaw({ + heightArr: [], + lastHeight: 0, + firstHeight: 0, + }); + + //当前显示歌词的index索引 + const currentIndex = ref(0); + //是否显示翻译图标 + const showTranslation = ref(false); + + //歌词为空时的文本 + const emptyText = ref('小果音乐,让生活充满音乐~'); + + //歌词区相对定位父级元素 + const lrcContainerRef = ref(); + + //监听滚轮事件 + const wheelHandler = async (ev: WheelEvent) => { + //让suspension悬浮区显示 + setSuspensionShow(true); + //清空隐藏suspension悬浮区的定时器 + clearSuspensionHiddenTimer(); + //更新suspension悬浮区相关信息 + updateSuspensionPlayInfo(); + const { + translateY: { current }, + wheelRateFactor, + } = lrcWrap; + //1表示向下滑动,-1表示向上滑动 + const polarity = ev.deltaY > 0 ? 1 : -1; + //之所以要异步执行,是因为updateSuspensionPlayInfo中异步更新了suspension.tarIndex + await nextTick(); + const tarTranslateY = + current + + lrcItemInfo.heightArr[suspension.tarIndex] * -polarity * wheelRateFactor; + setTranslateY(tarTranslateY); + //重新设置隐藏suspension悬浮区的定时器 + setSuspensionHiddenTimer(); + }; + useEventListener(lrcContainerRef, 'wheel', wheelHandler); + + watch(currentTimeRefGlobal, (val) => { + const exist = playerStore.lyricParsed.exist; + //如果当前显示着播放悬浮框或者没有歌词就return + if (suspension.isShow || !exist) return; + const index = getCurItemIndexByTime(val); + setCurItemIndex(index); + setTranslateYByDynamicIndex(index); + }); + + watch( + () => suspension.isShow, + (val) => { + //浮动播放条显示时 + setLrcWrapTransition(!val); + }, + ); + + //歌词列表变动时 + watch( + () => playerStore.lyricParsed, + async (lyricParsed, oldLyricParsed) => { + //如果没有歌词 + if (!lyricParsed.exist) { + //重置本歌词组件的一些数据 + resetLrcData(); + return; + } + //重置播放悬浮框状态(如清除定时器) + resetSuspension(); + await nextTick(); + //DOM更新后,再更新歌词容器等相关数据(如宽高等) + updatedLrcData(); + }, + ); + + //翻译图标显隐变动时 + watch(showTranslation, async (val) => { + await nextTick(); + //如果播放悬浮窗是显示的,就设置为tarIndex,否则为当前播放的歌词的索引 + const willReachIndex = suspension.isShow ? suspension.tarIndex : currentIndex.value; + updatedLrcData(willReachIndex); + }); + + onMounted(() => { + useEventListener(document, 'mousemove', lyricMove); + useEventListener(document, 'mouseup', lyricUp); + }); + + //更新将要播放的位置 + const setWillPlayTime = (time: number) => { + nextSeekTimeRefGlobal.value = time; + }; + + //设置歌词容器过渡时长 + const setLrcWrapTransition = (isTransition: boolean) => { + const transition = lrcWrap.transition; + transition.value = isTransition ? transition.defaultVal : '0s'; + }; + + //设置translateY值 + const setTranslateY = (value: number) => { + const { min, max } = lrcWrap.translateY; + if (value > max) { + value = max; + } else if (value < min) { + value = min; + } + lrcWrap.translateY.current = value; + }; + + //根据动态target索引设置当前trnslateY值 + const setTranslateYByDynamicIndex = (index: number) => { + const tarTranslateY = getTarTranslateY(index); + setTranslateY(tarTranslateY); + }; + + //根据tarIndex目标索引获取translateY值 + const getTarTranslateY = (index: number) => { + const { heightArr } = lrcItemInfo; + return ( + -heightArr.slice(0, index + 1).reduce((total, now) => total + now) + + heightArr[index] / 2 + ); + }; + + //根据translateY值获取tarIndex目标索引 + const getTarByTranslateY = () => { + const curTranslateY = lrcWrap.translateY.current; + const { heightArr } = lrcItemInfo; + const { length } = heightArr; + const offsetY = Math.abs(curTranslateY - lrcWrap.translateY.max); + let mutipleDis = 0; + let index = 0; + for (let i = 0; i < length; i++) { + const curItemh = heightArr[i]; + if (offsetY <= (mutipleDis += curItemh)) { + if (Math.abs(offsetY - mutipleDis) >= curItemh / 2) { + index = i; + } else { + index = i + 1; + } + break; + } + } + const { time, timeStr } = playerStore.lyricParsed.lrcArr[index]; + return { + time, + timeStr, + index, + }; + }; + + //根据当前播放时间获取歌词列表的index + const getCurItemIndexByTime = (time: number) => { + const lrcArr = playerStore.lyricParsed.lrcArr; + const lastIndex = lrcArr.length - 1; + let index = 0; + //如果当前时间大于第一条歌词的时间 + if (time > lrcArr[0].time) { + for (let i = 0; i < lastIndex; i++) { + if (time >= lrcArr[i].time && time < lrcArr[i + 1].time) { + index = i; + break; + } else { + index = lastIndex; + } + } + } + return index; + }; + + //切换翻译状态 + const switchTranslation = () => { + showTranslation.value = !showTranslation.value; + }; + + //重置悬浮框 + const resetSuspension = () => { + setSuspensionShow(false); + clearSuspensionHiddenTimer(); + setSuspensionTarIndex(0); + }; + + //悬浮条播放按钮的点击事件 + const suspensionPlayClick = () => { + const tarIndex = suspension.tarIndex; + setSuspensionShow(false); + clearSuspensionHiddenTimer(); + setCurItemIndex(tarIndex); + setTranslateYByDynamicIndex(tarIndex); + setWillPlayTime(playerStore.lyricParsed.lrcArr[tarIndex].time); + playingRefGlobal.value = true; + }; + + // 清除悬浮条的隐藏的timer计时器 + const clearSuspensionHiddenTimer = () => { + clearTimeout(suspension.hiddenTimer); + }; + + //设置悬浮条的隐藏的timer计时器 + const setSuspensionHiddenTimer = (interval: number = suspension.interval) => { + suspension.hiddenTimer = setTimeout(() => { + setSuspensionShow(false); + //设置lrcWraptranslateY + setTranslateYByDynamicIndex(currentIndex.value); + //定时器结束后将拖动的目标index与curIndex同步,以防不断点击翻译按钮出错。 + setSuspensionTarIndex(currentIndex.value); + }, interval); + }; + + //设置悬浮条控制的目标索引 + const setSuspensionTarIndex = (index: number) => { + suspension.tarIndex = index; + }; + + //控制悬浮条显隐 + const setSuspensionShow = (isShow: boolean) => { + suspension.isShow = isShow; + }; + + //设置悬浮条的目标播放时间 + const setSuspensionTime = (timeStr: string) => { + suspension.time = timeStr.replace(/\.\d+/, ''); + }; + + //设置当前歌词的index索引 + const setCurItemIndex = (i: number) => { + currentIndex.value = i; + }; + + //重置歌词数据 + const resetLrcData = () => { + emptyText.value = `小右没有发现歌词喔~~${UNICODE_CHAR.pensive}`; + lrcItemInfo.heightArr = []; + lrcItemInfo.lastHeight = 0; + lrcItemInfo.firstHeight = 0; + lrcWrap.height = 0; + lrcWrap.translateY = { + min: 0, + max: 0, + current: 0, + }; + }; + + const lrcContentsRef = ref(); + //更新歌词相关数据 + const updatedLrcData = (curIdx = 0) => { + const el = lrcContentsRef.value!; + const lrcWrapHeight = Number.parseFloat(computedStyle(el, 'height')); + const lrcItemsHeightArr = Array.from(el.children).map((childEl) => + Number.parseFloat(computedStyle(childEl, 'height')), + ); + const lrcLastItemHeight = lrcItemsHeightArr[lrcItemsHeightArr.length - 1]; + const lrcfirstItemHeight = lrcItemsHeightArr[0]; + + lrcItemInfo.heightArr = lrcItemsHeightArr; + lrcItemInfo.lastHeight = lrcLastItemHeight; + lrcItemInfo.firstHeight = lrcfirstItemHeight; + lrcWrap.height = lrcWrapHeight; + lrcWrap.translateY.min = -(lrcWrapHeight - lrcLastItemHeight / 2); + lrcWrap.translateY.max = -lrcfirstItemHeight / 2; + //根据当前播放的歌词索引来确定translateY值 + setTranslateYByDynamicIndex(curIdx); + }; + + //更新歌词悬浮条信息 + const updateSuspensionPlayInfo = () => { + const curTimeInfo = getTarByTranslateY(); + setSuspensionTime(curTimeInfo.timeStr); + setSuspensionTarIndex(curTimeInfo.index); + }; + + const lyricDown = (ev: MouseEvent) => { + if (ev.button !== 0) return; + lrcWrap.isMouseDown = true; + lrcWrap.canMouseMove = true; + setSuspensionShow(true); + clearSuspensionHiddenTimer(); + updateSuspensionPlayInfo(); + }; + + const lyricMove = (ev: MouseEvent) => { + const { + canMouseMove, + translateY: lrcWrapTranslateY, + mouseMoveRateFactor, + } = toRefs(lrcWrap); + if (!canMouseMove.value) return; + const { current } = lrcWrapTranslateY.value; + const tarTranslateY = current + ev.movementY * mouseMoveRateFactor.value; + setTranslateY(tarTranslateY); + updateSuspensionPlayInfo(); + }; + + const lyricUp = (ev: MouseEvent) => { + if (!lrcWrap.isMouseDown) return; + lrcWrap.isMouseDown = false; + lrcWrap.canMouseMove = false; + setSuspensionHiddenTimer(); + }; + + return () => { + const { exist, canTranslate, lrcArr } = playerStore.lyricParsed; + const { isShow, time } = suspension; + const showtransIcon = showTranslation.value; + const emptyTextVal = emptyText.value; + const currentIndexValue = currentIndex.value; + + return ( +
+ + +
+ ); + }; + }, }); diff --git a/src/components/music-hall/artist/index.scss b/src/components/music-hall/artist/index.scss index 0e03581..90bfc61 100644 --- a/src/components/music-hall/artist/index.scss +++ b/src/components/music-hall/artist/index.scss @@ -1,5 +1,7 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .singer-layer { - margin-bottom: 20px; -} \ No newline at end of file + margin-bottom: 20px; +} diff --git a/src/components/music-hall/artist/index.tsx b/src/components/music-hall/artist/index.tsx index f7f770b..511f4f5 100644 --- a/src/components/music-hall/artist/index.tsx +++ b/src/components/music-hall/artist/index.tsx @@ -1,214 +1,204 @@ -import { shallowReactive, defineComponent, ref } from "vue"; +/** @format */ + +import { shallowReactive, defineComponent, ref } from 'vue'; import { - useRouter, - useRoute, - useLink, - LocationQueryValue, - onBeforeRouteLeave, - onBeforeRouteUpdate, -} from "vue-router"; - -import { artistList } from "@api/singer"; -import ArtistList from "@widgets/artist-list"; -import RoutePagination from "@widgets/route-pagination"; -import { freeze } from "@/utils"; -import { SingerInfo } from "@/types/singer"; -import "./index.scss"; -import { NRadio, NRadioButton, NRadioGroup, NSpace } from "naive-ui"; -import singer from "../../search/singer"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; + useRouter, + useRoute, + useLink, + LocationQueryValue, + onBeforeRouteLeave, + onBeforeRouteUpdate, +} from 'vue-router'; + +import { artistList } from '@api/singer'; +import ArtistList from '@widgets/artist-list'; +import RoutePagination from '@widgets/route-pagination'; +import { freeze } from '@/utils'; +import { SingerInfo } from '@/types/singer'; +import './index.scss'; +import { NRadio, NRadioButton, NRadioGroup, NSpace } from 'naive-ui'; +import singer from '../../search/singer'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; type SingerListInfo = { - limit: number; - offset: number; - initial?: number; - type?: number; - area?: number; - sizeArr: number[]; + limit: number; + offset: number; + initial?: number; + type?: number; + area?: number; + sizeArr: number[]; }; const catList = [ - { text: "全部", key: -1 }, - { text: "男歌手", key: 1 }, - { text: "女歌手", key: 2 }, - { text: "乐队", key: 3 }, + { text: '全部', key: -1 }, + { text: '男歌手', key: 1 }, + { text: '女歌手', key: 2 }, + { text: '乐队', key: 3 }, ]; const areaList = [ - { text: "全部", key: -1 }, - { text: "华语", key: 7 }, - { text: "欧美", key: 96 }, - { text: "日本", key: 8 }, - { text: "韩国", key: 16 }, - { text: "其它", key: 0 }, + { text: '全部', key: -1 }, + { text: '华语', key: 7 }, + { text: '欧美', key: 96 }, + { text: '日本', key: 8 }, + { text: '韩国', key: 16 }, + { text: '其它', key: 0 }, ]; const initialList = [ - { text: "全部", key: "All" }, - ...Array(90 - 65 + 1) - .fill("") - .reduce((total, now, i) => { - const char = String.fromCharCode(i + 65); - total.push({ text: char, key: char }); - return total; - }, []), + { text: '全部', key: 'All' }, + ...Array(90 - 65 + 1) + .fill('') + .reduce((total, now, i) => { + const char = String.fromCharCode(i + 65); + total.push({ text: char, key: char }); + return total; + }, []), ]; const defaultSingerInfo = { - initial: initialList[0].key, - type: catList[0].key, - area: areaList[0].key, - limit: PAGE_SIZE[COMPONENT_NAME.MUSICHALL_ARTIST], - offset: 0, + initial: initialList[0].key, + type: catList[0].key, + area: areaList[0].key, + limit: PAGE_SIZE[COMPONENT_NAME.MUSICHALL_ARTIST], + offset: 0, }; export default defineComponent({ - name: COMPONENT_NAME.MUSICHALL_ARTIST, - setup(props, context) { - const router = useRouter(); - const route = useRoute(); - const hasMore = ref(true); - - const { - limit: dftLimit, - type: dftType, - area: dftArea, - initial: dftInitial, - offset: dftOffset, - } = defaultSingerInfo; - - const singerListInfo = shallowReactive({ - ...defaultSingerInfo, - sizeArr: Array(3) - .fill(0) - .map((v, i) => dftLimit * (i + 1)), - }); - - const singers = shallowReactive({ - singerList: [] as SingerInfo[], - }); - - const getArtistsInfo = async ({ - type = dftType, - offset = dftOffset, - initial = dftInitial, - limit = dftLimit, - area = dftArea, - }) => { - const { artists, more } = await artistList({ - type, - offset, - area, - initial: initial === dftInitial ? "" : initial, - limit, - }); - singers.singerList = freeze(artists); - singerListInfo.limit = +limit; - singerListInfo.offset = +offset; - singerListInfo.area = +area; - singerListInfo.type = +type; - singerListInfo.initial = initial; - hasMore.value = more; - }; - getArtistsInfo(route.query); - - onFilteredBeforeRouteUpdate((to) => { - getArtistsInfo(to.query); - }); - - const areaChange = (areaKey: string | number) => { - router.push({ - query: { - ...route.query, - area: areaKey - }, - }); - } - - const typeChange = (typeKey: string | number) => - router.push({ - query: { - ...route.query, - type: typeKey - }, - }); - - const initialChange = (initialKey: string | number) => - router.push({ - query: { - ...route.query, - initial: initialKey - }, - }); - - return () => { - return ( - <> -
- - { - areaList.map(item => - - {item.text} - - ) - } - -
- -
- - { - catList.map(item => - - {item.text} - - ) - } - -
- -
- - { - initialList.map(item => - - {item.text} - - ) - } - -
- - - -
- -
- - ); - }; - }, + name: COMPONENT_NAME.MUSICHALL_ARTIST, + setup(props, context) { + const router = useRouter(); + const route = useRoute(); + const hasMore = ref(true); + + const { + limit: dftLimit, + type: dftType, + area: dftArea, + initial: dftInitial, + offset: dftOffset, + } = defaultSingerInfo; + + const singerListInfo = shallowReactive({ + ...defaultSingerInfo, + sizeArr: Array(3) + .fill(0) + .map((v, i) => dftLimit * (i + 1)), + }); + + const singers = shallowReactive({ + singerList: [] as SingerInfo[], + }); + + const getArtistsInfo = async ({ + type = dftType, + offset = dftOffset, + initial = dftInitial, + limit = dftLimit, + area = dftArea, + }) => { + const { artists, more } = await artistList({ + type, + offset, + area, + initial: initial === dftInitial ? '' : initial, + limit, + }); + singers.singerList = freeze(artists); + singerListInfo.limit = +limit; + singerListInfo.offset = +offset; + singerListInfo.area = +area; + singerListInfo.type = +type; + singerListInfo.initial = initial; + hasMore.value = more; + }; + getArtistsInfo(route.query); + + onFilteredBeforeRouteUpdate((to) => { + getArtistsInfo(to.query); + }); + + const areaChange = (areaKey: string | number) => { + router.push({ + query: { + ...route.query, + area: areaKey, + }, + }); + }; + + const typeChange = (typeKey: string | number) => + router.push({ + query: { + ...route.query, + type: typeKey, + }, + }); + + const initialChange = (initialKey: string | number) => + router.push({ + query: { + ...route.query, + initial: initialKey, + }, + }); + + return () => { + return ( + <> +
+ + {areaList.map((item) => ( + + {item.text} + + ))} + +
+ +
+ + {catList.map((item) => ( + + {item.text} + + ))} + +
+ +
+ + {initialList.map((item) => ( + + {item.text} + + ))} + +
+ + + +
+ +
+ + ); + }; + }, }); diff --git a/src/components/music-hall/featured/banner/index.scss b/src/components/music-hall/featured/banner/index.scss index fe9e0c2..d3cdf96 100644 --- a/src/components/music-hall/featured/banner/index.scss +++ b/src/components/music-hall/featured/banner/index.scss @@ -1,78 +1,81 @@ -@import "@scss/variable.scss"; +/** @format */ + +@import '@scss/variable.scss'; .recommend-banner { - position: relative; - @include flexVHe(); - & &-title { - position: absolute; - right: 20px; - top: 10px; - padding: 4px 12px; - background-color: rgba(10, 10, 10, 0.4); - color: #eee; - font-size: 12px; - border-radius: 8px; - backdrop-filter: saturate(160%) blur(4px); - } - & &-item { - width: 33.3%; - } - & &-img { - display: block; - width: 100%; - border-radius: 10px; - } + position: relative; + width: 100%; + @include flexVHe(); + & &-title { + position: absolute; + right: 20px; + top: 10px; + padding: 4px 12px; + background-color: rgba(10, 10, 10, 0.4); + color: #eee; + font-size: 12px; + border-radius: 8px; + backdrop-filter: saturate(160%) blur(4px); + } + & &-item { + width: 33.3%; + } + & &-img { + display: block; + width: 100%; + border-radius: 10px; + } - .banner-pagination { - padding: 10px 0; - text-align: center; - .pagination-bullet { - display: inline-block; - width: 38px; - height: 5px; - margin: 10px; - background-color: rgba(207, 194, 194, 0.4); - border-radius: 2.5px; - cursor: pointer; - transition: background-color 0.2s, opacity 0.18s; - &-active { - background-color: var(--theme); - } - } - } + .banner-pagination { + padding: 10px 0; + text-align: center; + .pagination-bullet { + display: inline-block; + width: 38px; + height: 5px; + margin: 10px; + background-color: rgba(207, 194, 194, 0.4); + border-radius: 2.5px; + cursor: pointer; + transition: background-color 0.2s, opacity 0.18s; + &-active { + background-color: var(--theme); + } + } + } - .banner-swiper { - } + .banner-swiper { + } - .banner-swiper:hover { - .next-button, - .prev-button { - opacity: 1; - } - } + .banner-swiper:hover { + .next-button, + .prev-button { + opacity: 1; + } + } - .next-button, - .prev-button { - opacity: 0; - @include flexHcVc(); - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 38px; - height: 38px; - border-radius: 100%; - background-color: rgba(31, 29, 29, 0.3); - color: #fff; - font-size: 22px; - cursor: pointer; - z-index: 100; - } + .next-button, + .prev-button { + opacity: 0; + @include flexHcVc(); + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 38px; + height: 38px; + border-radius: 100%; + background-color: rgba(31, 29, 29, 0.3); + color: #fff; + font-size: 22px; + cursor: pointer; + z-index: 100; + } - .next-button { - right: 2rem; - } + .next-button { + right: 2rem; + } - .prev-button { - left: 2rem; - } + .prev-button { + left: 2rem; + } } diff --git a/src/components/music-hall/featured/banner/index.tsx b/src/components/music-hall/featured/banner/index.tsx index fdb6462..ed00758 100644 --- a/src/components/music-hall/featured/banner/index.tsx +++ b/src/components/music-hall/featured/banner/index.tsx @@ -1,95 +1,98 @@ -import { defineComponent, getCurrentInstance, nextTick, onMounted, PropType, ref, watch } from "vue"; -import { Swiper, SwiperSlide } from "swiper/vue"; -import { padPicCrop, is } from "@/utils"; -import "./index.scss"; +/** @format */ -export default defineComponent({ - name: "RecommentBanner", - props: { - bannerList: { - type: Array as PropType, - required: true, - }, - }, - setup(props, { slots, emit }) { +import { + defineComponent, + getCurrentInstance, + nextTick, + onMounted, + PropType, + ref, + watch, +} from 'vue'; +import { Swiper, SwiperSlide } from 'swiper/vue'; +import { padPicCrop, is } from '@/utils'; +import './index.scss'; - return () => { - const bannerList = props.bannerList; - if (is.emptyArray(bannerList)) { - return - } - return ( -
-
-
- -
- { - bannerList.map((item) => - - ) - } -
-
-
-
- ); - }; - }, +export default defineComponent({ + name: 'RecommentBanner', + props: { + bannerList: { + type: Array as PropType, + required: true, + }, + }, + setup(props, { slots, emit }) { + return () => { + const bannerList = props.bannerList; + if (is.emptyArray(bannerList)) { + return; + } + return ( +
+
+ +
+ {bannerList.map((item) => ( + + ))} +
+
+
+ ); + }; + }, }); diff --git a/src/components/music-hall/featured/index.scss b/src/components/music-hall/featured/index.scss index 6d1284d..bff33b5 100644 --- a/src/components/music-hall/featured/index.scss +++ b/src/components/music-hall/featured/index.scss @@ -1,67 +1,69 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .layer { - h6 { - padding: 20px 0; - border-bottom: 1px solid #eee; - color: #555; - font-size: 20px; - font-weight: 600; - } + h6 { + padding: 20px 0; + border-bottom: 1px solid #eee; + color: #555; + font-size: 20px; + font-weight: 600; + } - .item-wrap { - // @include flex(); - // flex-wrap: wrap; - padding: 40px 20px; + .item-wrap { + // @include flex(); + // flex-wrap: wrap; + padding: 40px 20px; - .item { - position: relative; - cursor: pointer; + .item { + position: relative; + cursor: pointer; - &:hover { - .item-playbill { - transform: scale(1.06); - filter: drop-shadow(12px 14px 22px rgba(80, 80, 80, 0.22)); - } + &:hover { + .item-playbill { + transform: scale(1.06); + filter: drop-shadow(12px 14px 22px rgba(80, 80, 80, 0.22)); + } - .item-desc { - transform: translateY(0); - } - } + .item-desc { + transform: translateY(0); + } + } - &-playbill { - position: inherit; - border-radius: 16px; - transition: transform 0.15s, filter ease-in-out 0.13s; - overflow: hidden; + &-playbill { + position: inherit; + border-radius: 16px; + transition: transform 0.15s, filter ease-in-out 0.13s; + overflow: hidden; - img { - border-radius: inherit; - } - } + img { + border-radius: inherit; + } + } - &-name { - padding-top: 22px; - color: #555; - font-size: 15px; - font-weight: 500; - line-height: 1.4; - } + &-name { + padding-top: 22px; + color: #555; + font-size: 15px; + font-weight: 500; + line-height: 1.4; + } - &-desc { - position: absolute; - left: 0; - top: 0; - width: 100%; - padding: 12px 20px; - background-color: rgba(30, 30, 30, 0.2); - color: #fff; - line-height: 1.2; - transform: translateY(-100%); - border-radius: 16px 16px 0 0; - backdrop-filter: saturate(180%) blur(5px); - transition: transform 0.14s; - } - } - } + &-desc { + position: absolute; + left: 0; + top: 0; + width: 100%; + padding: 12px 20px; + background-color: rgba(30, 30, 30, 0.2); + color: #fff; + line-height: 1.2; + transform: translateY(-100%); + border-radius: 16px 16px 0 0; + backdrop-filter: saturate(180%) blur(5px); + transition: transform 0.14s; + } + } + } } diff --git a/src/components/music-hall/featured/index.tsx b/src/components/music-hall/featured/index.tsx index a028734..1ad9730 100644 --- a/src/components/music-hall/featured/index.tsx +++ b/src/components/music-hall/featured/index.tsx @@ -1,101 +1,101 @@ +/** @format */ + import { - markRaw, - getCurrentInstance, - shallowReactive, - reactive, - defineComponent, - ComponentInternalInstance, - ref, - watch, -} from "vue"; -import { - useRouter -} from 'vue-router'; -import { recommendSonglist } from "@api/playlist"; -import { bannerInfo } from "@api/other"; -import "./index.scss"; -import { is, padPicCrop } from "@/utils"; -import RecommendBanner from "./banner"; -import { NGrid, NGridItem } from "naive-ui"; -import useUserStore from "@/stores/user"; + markRaw, + getCurrentInstance, + shallowReactive, + reactive, + defineComponent, + ComponentInternalInstance, + ref, + watch, +} from 'vue'; +import { useRouter } from 'vue-router'; +import { recommendSonglist } from '@api/playlist'; +import { bannerInfo } from '@api/other'; +import './index.scss'; +import { is, padPicCrop } from '@/utils'; +import RecommendBanner from './banner'; +import { NGrid, NGridItem } from 'naive-ui'; +import useUserStore from '@/stores/user'; export type FeaturedItem = { - copywriter: string; - id: number; - name: string; - picUrl: string; - playCount: number; - trackCount: number; - trackNumberUpdateTime: number; + copywriter: string; + id: number; + name: string; + picUrl: string; + playCount: number; + trackCount: number; + trackNumberUpdateTime: number; }; export default defineComponent({ - name: "MusicHallFeatured", - setup(props, { slots, emit }) { - const userStore = useUserStore(); - const router = useRouter(); - const featuredData = reactive({ - bannerlist: [] as any[], - songlist: [] as any[], - personalSonglist: [] as any[], - }); - - const limit = 10; - const vm = getCurrentInstance()!; - - const getBanner = async () => { - const { banners = [] } = await bannerInfo({ type: 0 }); - featuredData.bannerlist = banners; - }; - getBanner(); - const getFeatruedSonglist = async () => { - const { result = [] } = await recommendSonglist({ limit }); - featuredData.songlist = result; - }; - getFeatruedSonglist(); + name: 'MusicHallFeatured', + setup(props, { slots, emit }) { + const userStore = useUserStore(); + const router = useRouter(); + const featuredData = reactive({ + bannerlist: [] as any[], + songlist: [] as any[], + personalSonglist: [] as any[], + }); - const toSonglistDetailPage = (id: number | string) => { - router.push({ - path: "/songlist/:id", - name: 'songlist', - params: { - id - } - }) - } + const limit = 10; + const vm = getCurrentInstance()!; - return () => { - const { bannerlist, songlist } = featuredData - return ( - - ); - }; - }, + return () => { + const { bannerlist, songlist } = featuredData; + return ( + + ); + }; + }, }); diff --git a/src/components/music-hall/index.scss b/src/components/music-hall/index.scss index 05d6b6b..01ea172 100644 --- a/src/components/music-hall/index.scss +++ b/src/components/music-hall/index.scss @@ -1,11 +1,11 @@ -@import "@scss/variable"; +/** @format */ -.music-hall { - - h2 { - font-size: 30px; - font-weight: 600; - color: #333; - } +@import '@scss/variable'; +.music-hall { + h2 { + font-size: 30px; + font-weight: 600; + color: #333; + } } diff --git a/src/components/music-hall/index.tsx b/src/components/music-hall/index.tsx index 99f2442..a9fb604 100644 --- a/src/components/music-hall/index.tsx +++ b/src/components/music-hall/index.tsx @@ -1,34 +1,32 @@ -import { - defineComponent, -} from "vue"; -import CommonRouterList from "@widgets/common-router-list"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import "./index.scss"; +/** @format */ + +import { defineComponent } from 'vue'; +import CommonRouterList from '@widgets/common-router-list'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import './index.scss'; export default defineComponent({ - name: "MusicHall", - setup(props, context) { - const musichallCate = [ - { text: "精选", to: { path: "/musichall/featrued" } }, - { text: "排行榜", to: { path: "/musichall/top" } }, - { text: "新歌速递", to: { path: "/musichall/newestmusic" } }, - { text: "新碟上架", to: { path: "/musichall/newestdisc" } }, - { text: "歌单", to: { path: "/musichall/songlist" } }, - { text: "歌手", to: { path: "/musichall/artist" } }, - ]; + name: 'MusicHall', + setup(props, context) { + const musichallCate = [ + { text: '精选', to: { path: '/musichall/featrued' } }, + { text: '排行榜', to: { path: '/musichall/top' } }, + { text: '新歌速递', to: { path: '/musichall/newestmusic' } }, + { text: '新碟上架', to: { path: '/musichall/newestdisc' } }, + { text: '歌单', to: { path: '/musichall/songlist' } }, + { text: '歌手', to: { path: '/musichall/artist' } }, + ]; - return () => { - return ( -
-

音乐馆

-
- -
- { - renderKeepAliveRouterView() - } -
- ); - }; - }, + return () => { + return ( +
+

音乐馆

+
+ +
+ {renderKeepAliveRouterView()} +
+ ); + }; + }, }); diff --git a/src/components/music-hall/newestdisc/index.scss b/src/components/music-hall/newestdisc/index.scss index 7426f5d..6f1ebb2 100644 --- a/src/components/music-hall/newestdisc/index.scss +++ b/src/components/music-hall/newestdisc/index.scss @@ -1,14 +1,14 @@ -.newestdisc { - - &-layer { - margin-bottom: 24px; - } +/** @format */ - .disc-title { - color: #555; - font-weight: 600; - font-size: 20px; - padding: 18px 0 34px 0; - } +.newestdisc { + &-layer { + margin-bottom: 24px; + } -} \ No newline at end of file + .disc-title { + color: #555; + font-weight: 600; + font-size: 20px; + padding: 18px 0 34px 0; + } +} diff --git a/src/components/music-hall/newestdisc/index.tsx b/src/components/music-hall/newestdisc/index.tsx index 9c14002..5a0e47f 100644 --- a/src/components/music-hall/newestdisc/index.tsx +++ b/src/components/music-hall/newestdisc/index.tsx @@ -1,222 +1,201 @@ +/** @format */ + import { - markRaw, - onMounted, - readonly, - ref, - defineComponent, - shallowReactive, - reactive, - WatchStopHandle, - onActivated, - watch, -} from "vue"; -import "./index.scss"; -import { onBeforeRouteLeave, useRoute, useRouter } from "vue-router"; -import { newAlbumPutOn } from "@/api/playlist"; -import { NRadioButton, NRadioGroup } from "naive-ui"; -import { EMPTY_OBJ } from "@/utils"; -import AlbumList from "@/widgets/album-list"; -import { onFilteredBeforeRouteUpdate, RouteHookNames } from "@/hooks/onRouteHook"; + markRaw, + onMounted, + readonly, + ref, + defineComponent, + shallowReactive, + reactive, + WatchStopHandle, + onActivated, + watch, +} from 'vue'; +import './index.scss'; +import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'; +import { newAlbumPutOn } from '@/api/playlist'; +import { NRadioButton, NRadioGroup } from 'naive-ui'; +import { EMPTY_OBJ } from '@/utils'; +import AlbumList from '@/widgets/album-list'; +import { onFilteredBeforeRouteUpdate, RouteHookNames } from '@/hooks/onRouteHook'; export const areaKeyList = [ - { key: 'ALL', area: '全部' }, - { key: 'ZH', area: '华语' }, - { key: 'EA', area: '欧美' }, - { key: 'KR', area: '韩国' }, - { key: 'JP', area: '日本' }, -] + { key: 'ALL', area: '全部' }, + { key: 'ZH', area: '华语' }, + { key: 'EA', area: '欧美' }, + { key: 'KR', area: '韩国' }, + { key: 'JP', area: '日本' }, +]; const typeKeyList = [ - { key: 'new', type: '最新' }, - { key: 'hot', type: '热门' } -] + { key: 'new', type: '最新' }, + { key: 'hot', type: '热门' }, +]; const timeRangeList = [ - { key: 'week', type: "本周新碟", dataKey: 'weekData', }, - { key: 'month', type: "本月新碟", dataKey: 'monthData', }, -] + { key: 'week', type: '本周新碟', dataKey: 'weekData' }, + { key: 'month', type: '本月新碟', dataKey: 'monthData' }, +]; const defaultDiscInfos = { - area: areaKeyList[0].key, - type: typeKeyList[0].key, - timerange: timeRangeList[0].key, -} - -export type ListMap = PlainObject<{ - weekData: any[], - monthData: any[], -} | undefined> + area: areaKeyList[0].key, + type: typeKeyList[0].key, + timerange: timeRangeList[0].key, +}; + +export type ListMap = PlainObject< + | { + weekData: any[]; + monthData: any[]; + } + | undefined +>; export default defineComponent({ - name: "MusicHallNewdisc", - setup(props, context) { - - const route = useRoute(); - const router = useRouter(); - - const { - area: defaultAreaKey, - type: defaultTypeKey, - timerange: defaultTimerangeKey - } = defaultDiscInfos; - - const listMap = areaKeyList.reduce((map, { key }) => { - map[key] = void 0; - return map; - }, {}); - - const newDiscInfos = reactive({ - listMap, - hasMore: false, - }); - - const routeUpdateHandler = async ( - toQuery: PlainObject, - fromQuery?: PlainObject, - ) => { - const { - area = defaultAreaKey, - type = defaultTypeKey, - limit, - year, - month, - offset, - } = toQuery; - const { type: oldType = defaultTypeKey } = fromQuery || EMPTY_OBJ; - - if (type !== oldType || !newDiscInfos.listMap[area]) { - const { hasMore, weekData, monthData } = await newAlbumPutOn({ - area, limit, type, year, month, offset - }); - newDiscInfos.hasMore = hasMore; - newDiscInfos.listMap[area] = { - weekData, monthData - } - } - } - routeUpdateHandler(route.query); - onFilteredBeforeRouteUpdate((to, from) => { - routeUpdateHandler(to.query, from.query); - }); - - const areaChange = (areaKey: string | number) => { - router.push({ - path: route.path, - query: { - ...route.query, - area: areaKey - } - }) - } - - const typeChange = (typeKey: string | number) => { - router.push({ - path: route.path, - query: { - ...route.query, - type: typeKey - } - }) - } - - const timerangeChange = (timerangeKey: string) => { - router.push({ - path: route.path, - query: { - ...route.query, - timerange: timerangeKey - } - }) - } - - return () => { - const { - area = defaultAreaKey, - type = defaultTypeKey, - timerange = defaultTimerangeKey, - } = route.query as any; - - const { listMap, hasMore } = newDiscInfos; - const targetList = timeRangeList.find(({key}) => timerange === key) || {} as typeof timeRangeList[number]; - const { dataKey , type:targetType } = targetList; - const targetData = - dataKey - ? (listMap[area]! || EMPTY_OBJ)[dataKey as keyof typeof listMap[string]] || [] - : []; - - return ( -
-
- - { - typeKeyList.map(item => - - {item.type} - - ) - } - -
- -
- - { - areaKeyList.map(item => - - {item.area} - - ) - } - -
- -
- - { - timeRangeList.map(item => - - {item.type} - - ) - } - -
- -
-

- {targetType} -

- -
-
- ); - }; - }, + name: 'MusicHallNewdisc', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); + + const { + area: defaultAreaKey, + type: defaultTypeKey, + timerange: defaultTimerangeKey, + } = defaultDiscInfos; + + const listMap = areaKeyList.reduce((map, { key }) => { + map[key] = void 0; + return map; + }, {}); + + const newDiscInfos = reactive({ + listMap, + hasMore: false, + }); + + const routeUpdateHandler = async (toQuery: PlainObject, fromQuery?: PlainObject) => { + const { + area = defaultAreaKey, + type = defaultTypeKey, + limit, + year, + month, + offset, + } = toQuery; + const { type: oldType = defaultTypeKey } = fromQuery || EMPTY_OBJ; + + if (type !== oldType || !newDiscInfos.listMap[area]) { + const { hasMore, weekData, monthData } = await newAlbumPutOn({ + area, + limit, + type, + year, + month, + offset, + }); + newDiscInfos.hasMore = hasMore; + newDiscInfos.listMap[area] = { + weekData, + monthData, + }; + } + }; + routeUpdateHandler(route.query); + onFilteredBeforeRouteUpdate((to, from) => { + routeUpdateHandler(to.query, from.query); + }); + + const areaChange = (areaKey: string | number) => { + router.push({ + path: route.path, + query: { + ...route.query, + area: areaKey, + }, + }); + }; + + const typeChange = (typeKey: string | number) => { + router.push({ + path: route.path, + query: { + ...route.query, + type: typeKey, + }, + }); + }; + + const timerangeChange = (timerangeKey: string) => { + router.push({ + path: route.path, + query: { + ...route.query, + timerange: timerangeKey, + }, + }); + }; + + return () => { + const { + area = defaultAreaKey, + type = defaultTypeKey, + timerange = defaultTimerangeKey, + } = route.query as any; + + const { listMap, hasMore } = newDiscInfos; + const targetList = + timeRangeList.find(({ key }) => timerange === key) || + ({} as typeof timeRangeList[number]); + const { dataKey, type: targetType } = targetList; + const targetData = dataKey + ? (listMap[area]! || EMPTY_OBJ)[dataKey as keyof typeof listMap[string]] || [] + : []; + + return ( +
+
+ + {typeKeyList.map((item) => ( + + {item.type} + + ))} + +
+ +
+ + {areaKeyList.map((item) => ( + + {item.area} + + ))} + +
+ +
+ + {timeRangeList.map((item) => ( + + {item.type} + + ))} + +
+ +
+

{targetType}

+ +
+
+ ); + }; + }, }); diff --git a/src/components/music-hall/newestmusic/index.scss b/src/components/music-hall/newestmusic/index.scss index d4fa257..4bf47c6 100644 --- a/src/components/music-hall/newestmusic/index.scss +++ b/src/components/music-hall/newestmusic/index.scss @@ -1,7 +1,9 @@ -@import "@scss/variable"; +/** @format */ -.music-hall-newestmusic { - .newestmusic-layer { - margin: 24px 0; - } +@import '@scss/variable'; + +.music-hall-newestmusic { + .newestmusic-layer { + margin: 24px 0; + } } diff --git a/src/components/music-hall/newestmusic/index.tsx b/src/components/music-hall/newestmusic/index.tsx index 8c02bfa..18d83d6 100644 --- a/src/components/music-hall/newestmusic/index.tsx +++ b/src/components/music-hall/newestmusic/index.tsx @@ -1,109 +1,90 @@ -import { - defineComponent, - watch, - WatchStopHandle, - onActivated, - reactive, - ref, -} from "vue"; -import MusicList from "@widgets/music-list"; -import { getNewExpressMusic } from "@api/music"; -import { NewestSongInfo } from "@/types/song"; -import { onBeforeRouteLeave, useRoute, useRouter } from "vue-router"; -import { NRadioButton, NRadioGroup } from "naive-ui"; +/** @format */ + +import { defineComponent, watch, WatchStopHandle, onActivated, reactive, ref } from 'vue'; +import MusicList from '@widgets/music-list'; +import { getNewExpressMusic } from '@api/music'; +import { NewestSongInfo } from '@/types/song'; +import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'; +import { NRadioButton, NRadioGroup } from 'naive-ui'; import './index.scss'; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; import YuanInfinityScroll from '@widgets/infinity-scroll/infinity-scroll'; export const NewMusicAreaList = [ - { key: 0, area: '全部', }, - { key: 7, area: '华语', }, - { key: 8, area: '日本', }, - { key: 16, area: '韩国', }, - { key: 96, area: '欧美', }, -] + { key: 0, area: '全部' }, + { key: 7, area: '华语' }, + { key: 8, area: '日本' }, + { key: 16, area: '韩国' }, + { key: 96, area: '欧美' }, +]; export enum NewMusicAreaMap { - '全部' = 0, - '华语' = 7, - '日本' = 8, - '韩国' = 16, - '欧美' = 96, + '全部' = 0, + '华语' = 7, + '日本' = 8, + '韩国' = 16, + '欧美' = 96, } export default defineComponent({ - name: "MusicHallNewestmusic", - setup(props, context) { - const defaultAreaKey = NewMusicAreaList[0].key; - const router = useRouter(); - const route = useRoute(); - - const initialMusicListMap = NewMusicAreaList.reduce((data, { key, area }) => { - data[key as NewMusicAreaMap] = void 0; - return data; - }, {} as Record); + name: 'MusicHallNewestmusic', + setup(props, context) { + const defaultAreaKey = NewMusicAreaList[0].key; + const router = useRouter(); + const route = useRoute(); - const newestMusicInfo = reactive({ - listMap: initialMusicListMap, - currentAreaKey: defaultAreaKey as NewMusicAreaMap - }); + const initialMusicListMap = NewMusicAreaList.reduce((data, { key, area }) => { + data[key as NewMusicAreaMap] = void 0; + return data; + }, {} as Record); - const routeUpdateHandler = async ({ query: { area = defaultAreaKey } }) => { - newestMusicInfo.currentAreaKey = +area; - newestMusicInfo.listMap[newestMusicInfo.currentAreaKey] ??= - (await getNewExpressMusic({ type: area })).data; - }; - routeUpdateHandler(route); + const newestMusicInfo = reactive({ + listMap: initialMusicListMap, + currentAreaKey: defaultAreaKey as NewMusicAreaMap, + }); - onFilteredBeforeRouteUpdate((to) => { - routeUpdateHandler(to); - }); + const routeUpdateHandler = async ({ query: { area = defaultAreaKey } }) => { + newestMusicInfo.currentAreaKey = +area; + newestMusicInfo.listMap[newestMusicInfo.currentAreaKey] ??= ( + await getNewExpressMusic({ type: area }) + ).data; + }; + routeUpdateHandler(route); - const areaChange = (areaKey: string | number) => { - router.push({ - path: route.path, - query: { - area: areaKey - } - }) - } + onFilteredBeforeRouteUpdate((to) => { + routeUpdateHandler(to); + }); - return () => { - const { currentAreaKey, listMap } = newestMusicInfo; - const willShowMusicList = listMap[currentAreaKey]; - return ( -
-
- - { - NewMusicAreaList.map(item => - - {item.area} - - ) - } - -
-
- { - willShowMusicList && ( - - ) - } -
-
- ) + const areaChange = (areaKey: string | number) => { + router.push({ + path: route.path, + query: { + area: areaKey, + }, + }); + }; - } - }, + return () => { + const { currentAreaKey, listMap } = newestMusicInfo; + const willShowMusicList = listMap[currentAreaKey]; + return ( +
+
+ + {NewMusicAreaList.map((item) => ( + + {item.area} + + ))} + +
+
+ {willShowMusicList && ( + + )} +
+
+ ); + }; + }, }); diff --git a/src/components/music-hall/songlist/index.scss b/src/components/music-hall/songlist/index.scss index 9cc280a..60d6f71 100644 --- a/src/components/music-hall/songlist/index.scss +++ b/src/components/music-hall/songlist/index.scss @@ -1,46 +1,48 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .music-hall-songlist { - .songlist-all-link { - @include flexInline(); - padding: 6px 16px; - background-color: rgba(90, 90, 90, 0.15); - color: #222; - font-weight: 500; - border-radius: 4px; - } + .songlist-all-link { + @include flexInline(); + padding: 6px 16px; + background-color: rgba(90, 90, 90, 0.15); + color: #222; + font-weight: 500; + border-radius: 4px; + } - .songlist-category { - min-height: 300px; - padding: 20px 0; - } + .songlist-category { + min-height: 300px; + padding: 20px 0; + } - .cate-layer { - margin-top: 10px; - @include flex(); + .cate-layer { + margin-top: 10px; + @include flex(); - .cate-sub-item { - padding: 0 12px 8px 12px; - color: #555; - font-size: 14px; - &:hover { - color: var(--theme); - } - } + .cate-sub-item { + padding: 0 12px 8px 12px; + color: #555; + font-size: 14px; + &:hover { + color: var(--theme); + } + } - .cate-name { - padding: 0 20px; - color: #444; - font-weight: 500; - white-space: nowrap; - } - .cate-sub { - @include flex(); - flex-wrap: wrap; + .cate-name { + padding: 0 20px; + color: #444; + font-weight: 500; + white-space: nowrap; + } + .cate-sub { + @include flex(); + flex-wrap: wrap; - &-item { - margin: 0 10px 10px 0; - } - } - } + &-item { + margin: 0 10px 10px 0; + } + } + } } diff --git a/src/components/music-hall/songlist/index.tsx b/src/components/music-hall/songlist/index.tsx index afc7f2a..205c2d2 100644 --- a/src/components/music-hall/songlist/index.tsx +++ b/src/components/music-hall/songlist/index.tsx @@ -1,189 +1,162 @@ -import { - shallowReactive, - getCurrentInstance, - defineComponent, -} from "vue"; -import { - useRouter, - useRoute, - RouterLink, -} from "vue-router"; -import { CatListSub } from "@/types/songlist"; -import { playlistCate, topPlaylist } from "@api/playlist"; -import { EMPTY_OBJ, freeze } from "@/utils/constants"; -import Songlist from "@widgets/song-list"; -import "./index.scss"; -import { - NGrid, - NGridItem, - NDropdown, - NButton, - NxButton, - NIcon, -} from "naive-ui"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +/** @format */ + +import { shallowReactive, getCurrentInstance, defineComponent } from 'vue'; +import { useRouter, useRoute, RouterLink } from 'vue-router'; +import { CatListSub } from '@/types/songlist'; +import { playlistCate, topPlaylist } from '@api/playlist'; +import { EMPTY_OBJ, freeze } from '@/utils/constants'; +import Songlist from '@widgets/song-list'; +import './index.scss'; +import { NGrid, NGridItem, NDropdown, NButton, NxButton, NIcon } from 'naive-ui'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; import YuanInfinityScroll from '@widgets/infinity-scroll/infinity-scroll'; const obtainCategory = (data: any) => { - const { categories, sub } = data; - const cats: string[] = Object.values(categories); - const catsArr = Array(cats.length) - .fill("") - .map(() => [] as CatListSub[]); - - sub.forEach((item: CatListSub) => { - catsArr[item.category].push(item); - }); - return { - cats: cats as readonly any[], - catsArr: catsArr as readonly any[], - }; + const { categories, sub } = data; + const cats: string[] = Object.values(categories); + const catsArr = Array(cats.length) + .fill('') + .map(() => [] as CatListSub[]); + + sub.forEach((item: CatListSub) => { + catsArr[item.category].push(item); + }); + return { + cats: cats as readonly any[], + catsArr: catsArr as readonly any[], + }; }; const orders = [ - { text: "热门", key: "hot" }, - { text: "最新", key: "new" }, + { text: '热门', key: 'hot' }, + { text: '最新', key: 'new' }, ]; export default defineComponent({ - name: COMPONENT_NAME.MUSICHALL_SONGLIST, - setup(props, context) { - const router = useRouter(); - const route = useRoute(); - - const vm = getCurrentInstance()!; - - const defaultOrder = orders[0].text; - - //额外的信息 - const listExtraInfo = shallowReactive({ - order: defaultOrder, - cat: "", - defaultLimit: PAGE_SIZE[COMPONENT_NAME.MUSICHALL_SONGLIST], - }); - - const showingList = shallowReactive({ - playlists: [], - total: 0, - }); - - const getSonglist = async ({ - cat = void 0, - offset = 0, - order = defaultOrder, - limit = listExtraInfo.defaultLimit, - }) => { - const { playlists, total } = await topPlaylist({ - cat, - offset, - order, - limit, - }); - playlists && (showingList.playlists = playlists); - showingList.total = total ?? 0; - listExtraInfo.order = - orders.find(({ key }) => key === order)?.text || defaultOrder; - listExtraInfo.cat = cat || ""; - }; - getSonglist(route.query); - - onFilteredBeforeRouteUpdate((to) => { - getSonglist(to.query); - }); - - const categoryData = shallowReactive>({ - cats: [], - catsArr: [], - }); - const getPlaylistCate = async () => { - const res = await playlistCate(); - const { cats, catsArr } = obtainCategory(res); - categoryData.cats = freeze(cats); - categoryData.catsArr = freeze(catsArr); - }; - getPlaylistCate(); - - const changeCatRoute = (cat: string) => - router.push({ query: { ...route.query, cat } }); - - const handleOrderCmd = (order: string) => - router.push({ query: { ...route.query, order } }); - - return () => { - const catDropdownOptions = orders.map(({ key, text: label }) => ({ - key, - label, - })); - return ( -
-
- - - - 全部 - - - - - - {{ - default: () => listExtraInfo.order, - icon: () => ( - - ), - }} - - - - -
- -
- { - categoryData.cats.map((item, i) => -
- {item} -
- { - categoryData.catsArr[i].map((subItem: any) => - changeCatRoute(subItem.name)} - > - {subItem.name} - - ) - } -
-
- ) - } -
- - - -
- ); - }; - }, + name: COMPONENT_NAME.MUSICHALL_SONGLIST, + setup(props, context) { + const router = useRouter(); + const route = useRoute(); + + const vm = getCurrentInstance()!; + + const defaultOrder = orders[0].text; + + //额外的信息 + const listExtraInfo = shallowReactive({ + order: defaultOrder, + cat: '', + defaultLimit: PAGE_SIZE[COMPONENT_NAME.MUSICHALL_SONGLIST], + }); + + const showingList = shallowReactive({ + playlists: [], + total: 0, + }); + + const getSonglist = async ({ + cat = void 0, + offset = 0, + order = defaultOrder, + limit = listExtraInfo.defaultLimit, + }) => { + const { playlists, total } = await topPlaylist({ + cat, + offset, + order, + limit, + }); + playlists && (showingList.playlists = playlists); + showingList.total = total ?? 0; + listExtraInfo.order = orders.find(({ key }) => key === order)?.text || defaultOrder; + listExtraInfo.cat = cat || ''; + }; + getSonglist(route.query); + + onFilteredBeforeRouteUpdate((to) => { + getSonglist(to.query); + }); + + const categoryData = shallowReactive>({ + cats: [], + catsArr: [], + }); + const getPlaylistCate = async () => { + const res = await playlistCate(); + const { cats, catsArr } = obtainCategory(res); + categoryData.cats = freeze(cats); + categoryData.catsArr = freeze(catsArr); + }; + getPlaylistCate(); + + const changeCatRoute = (cat: string) => + router.push({ query: { ...route.query, cat } }); + + const handleOrderCmd = (order: string) => + router.push({ query: { ...route.query, order } }); + + return () => { + const catDropdownOptions = orders.map(({ key, text: label }) => ({ + key, + label, + })); + return ( +
+
+ + + + 全部 + + + + + + {{ + default: () => listExtraInfo.order, + icon: () => , + }} + + + + +
+ +
+ {categoryData.cats.map((item, i) => ( +
+ {item} +
+ {categoryData.catsArr[i].map((subItem: any) => ( + changeCatRoute(subItem.name)} + > + {subItem.name} + + ))} +
+
+ ))} +
+ + +
+ ); + }; + }, }); diff --git a/src/components/music-hall/top/index.scss b/src/components/music-hall/top/index.scss index 6d90003..ec5ca82 100644 --- a/src/components/music-hall/top/index.scss +++ b/src/components/music-hall/top/index.scss @@ -1,130 +1,128 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; $hotItemHeight: 180px; .toplist-layer { - h5 { - padding-bottom: 30px; - color: #445; - font-size: 22px; - font-weight: 600; - } - - &:not(:first-of-type) { - padding-top: 40px; - } - + h5 { + padding-bottom: 30px; + color: #445; + font-size: 22px; + font-weight: 600; + } + + &:not(:first-of-type) { + padding-top: 40px; + } } - -.toplist-wrap { - - .toplist-item { - position: relative; - transition: transform 0.2s, filter 0.18s; - - &:hover { - transform: translateY(-8px); - filter: drop-shadow(10px 18px 22px rgba(120, 110, 120, 0.2)); - } - - .list-wrapper { - display: flow-root; - height: 100%; - overflow: hidden; - } - - .list-cover { - border-radius: 1rem; - img { - border-radius: inherit; - transition: filter 0.18s; - } - } - } + +.toplist-wrap { + .toplist-item { + position: relative; + transition: transform 0.2s, filter 0.18s; + + &:hover { + transform: translateY(-8px); + filter: drop-shadow(10px 18px 22px rgba(120, 110, 120, 0.2)); + } + + .list-wrapper { + display: flow-root; + height: 100%; + overflow: hidden; + } + + .list-cover { + border-radius: 1rem; + img { + border-radius: inherit; + transition: filter 0.18s; + } + } + } } .toplist-wrap-hot { - - min-height: $hotItemHeight * 2; - - .toplist-hot-item { - height: $hotItemHeight; - - .hot-cover { - float: left; - height: 100%; - } - - .list-body { - float: left; - width: calc(100% - #{$hotItemHeight}); - height: 100%; - padding: 28px; - background-color: rgba(163, 102, 92, 0.1); - border-radius: 1rem; - - h6 { - font-size: 18px; - font-weight: 600; - padding-bottom: 20px; - color: #444; - } - - p { - color: #566; - font-size: 14px; - line-height: 2; - } - } - } + min-height: $hotItemHeight * 2; + + .toplist-hot-item { + height: $hotItemHeight; + + .hot-cover { + float: left; + height: 100%; + } + + .list-body { + float: left; + width: calc(100% - #{$hotItemHeight}); + height: 100%; + padding: 28px; + background-color: rgba(163, 102, 92, 0.1); + border-radius: 1rem; + + h6 { + font-size: 18px; + font-weight: 600; + padding-bottom: 20px; + color: #444; + } + + p { + color: #566; + font-size: 14px; + line-height: 2; + } + } + } } .toplist-global-item { - - .play-count { - position: absolute; - right: 10px; - top: 10px; - padding: 2px 10px; - background-color: rgba(200, 200, 200, 0.2); - color: #fff; - font-size: 14px; - border-radius: 6px; - backdrop-filter: blur(4px); - } - - &:hover { - img { - filter: brightness(80%); - } - - .play-icon { - opacity: 1; - } - } - - .play-icon { - opacity: 0; - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - color: hsla(0,0%,100%,.95); - font-size: 60px; - transition: 0.16s; - cursor: pointer; - } - - h6 { - padding-bottom: 8px; - color: #555; - font-size: 17px; - font-weight: 600; - line-height: 1.8; - } - - p { - color: #777; - font-size: 14px; - } + .play-count { + position: absolute; + right: 10px; + top: 10px; + padding: 2px 10px; + background-color: rgba(200, 200, 200, 0.2); + color: #fff; + font-size: 14px; + border-radius: 6px; + backdrop-filter: blur(4px); + } + + &:hover { + img { + filter: brightness(80%); + } + + .play-icon { + opacity: 1; + } + } + + .play-icon { + opacity: 0; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + color: hsla(0, 0%, 100%, 0.95); + font-size: 60px; + transition: 0.16s; + cursor: pointer; + } + + h6 { + padding-bottom: 8px; + color: #555; + font-size: 17px; + font-weight: 600; + line-height: 1.8; + } + + p { + color: #777; + font-size: 14px; + } } diff --git a/src/components/music-hall/top/index.tsx b/src/components/music-hall/top/index.tsx index 2d1137c..f552e7a 100644 --- a/src/components/music-hall/top/index.tsx +++ b/src/components/music-hall/top/index.tsx @@ -1,134 +1,132 @@ +/** @format */ + import { - markRaw, - getCurrentInstance, - toRefs, - defineComponent, - ComponentInternalInstance, -} from "vue"; -import { useRouter } from "vue-router"; -import { allTopList, allTopListDetail } from "@api/other"; -import { getLocaleCount, is, padPicCrop } from "@utils/index"; -import { NGrid, NGridItem, NIcon } from "naive-ui"; -import "./index.scss"; + markRaw, + getCurrentInstance, + toRefs, + defineComponent, + ComponentInternalInstance, +} from 'vue'; +import { useRouter } from 'vue-router'; +import { allTopList, allTopListDetail } from '@api/other'; +import { getLocaleCount, is, padPicCrop } from '@utils/index'; +import { NGrid, NGridItem, NIcon } from 'naive-ui'; +import './index.scss'; const subDivideTopList = (topList: any[]) => { - const hotList: any[] = [], - commonList: any[] = []; - for (const item of topList) { - (is.emptyArray(item.tracks) ? commonList : hotList).push(item); - } - commonList.forEach((_) => (_.playCount = getLocaleCount(_.playCount))); - return { - hotList, - commonList, - }; + const hotList: any[] = [], + commonList: any[] = []; + for (const item of topList) { + (is.emptyArray(item.tracks) ? commonList : hotList).push(item); + } + commonList.forEach((_) => (_.playCount = getLocaleCount(_.playCount))); + return { + hotList, + commonList, + }; }; export default defineComponent({ - name: "MusicHallTop", - setup(props, { slots, emit }) { - const router = useRouter(); - const vm = getCurrentInstance()!; + name: 'MusicHallTop', + setup(props, { slots, emit }) { + const router = useRouter(); + const vm = getCurrentInstance()!; - const toplistData = markRaw({ - hotList: [] as any[], - commonList: [] as any[], - }); + const toplistData = markRaw({ + hotList: [] as any[], + commonList: [] as any[], + }); - const getAllTopListDetail = async () => { - const { list } = await allTopListDetail(); - const { hotList: hots, commonList: commons } = subDivideTopList(list); - toplistData.hotList = hots; - toplistData.commonList = commons; - vm.proxy!.$forceUpdate(); - }; - getAllTopListDetail(); + const getAllTopListDetail = async () => { + const { list } = await allTopListDetail(); + const { hotList: hots, commonList: commons } = subDivideTopList(list); + toplistData.hotList = hots; + toplistData.commonList = commons; + vm.proxy!.$forceUpdate(); + }; + getAllTopListDetail(); - const toSongListDetailPage = (list: any) => { - router.push({ - path: "/songlist/:id", - name: 'songlist', - params: { id: list.id } - }); - }; + const toSongListDetailPage = (list: any) => { + router.push({ + path: '/songlist/:id', + name: 'songlist', + params: { id: list.id }, + }); + }; - const renderSpecialList = () => { - const columnCount = 3; - const gap = 36; - return ( -
-
云音乐特色榜
-
- - { - toplistData.hotList.map((item) => - -
toSongListDetailPage(item)}> -
- -
-
-
{item.name}
- {item.tracks.map((track: any, i: number) => ( -

- {++i} {Object.values(track).join(" - ")} -

- ))} -
-
-
- ) - } -
-
-
- ) - } - const renderGlobalList = () => { - const columnCount = 7; - const gap = 30; - return ( -
-
全球媒体榜
-
- - { - toplistData.commonList.map((item) => - -
toSongListDetailPage(item)} > -
- -
{item.playCount}
-
- -
-
-
{item.name}
-

{item.updateFrequency}

-
-
- ) - } -
-
-
- ) - } + const renderSpecialList = () => { + const columnCount = 3; + const gap = 36; + return ( +
+
云音乐特色榜
+
+ + {toplistData.hotList.map((item) => ( + +
toSongListDetailPage(item)}> +
+ +
+
+
{item.name}
+ {item.tracks.map((track: any, i: number) => ( +

+ {++i} {Object.values(track).join(' - ')} +

+ ))} +
+
+
+ ))} +
+
+
+ ); + }; + const renderGlobalList = () => { + const columnCount = 7; + const gap = 30; + return ( +
+
全球媒体榜
+
+ + {toplistData.commonList.map((item) => ( + +
toSongListDetailPage(item)}> +
+ +
{item.playCount}
+
+ +
+
+
{item.name}
+

{item.updateFrequency}

+
+
+ ))} +
+
+
+ ); + }; - return () => { - return ( -
- {renderSpecialList()} - {renderGlobalList()} -
- ); - }; - }, + return () => { + return ( +
+ {renderSpecialList()} + {renderGlobalList()} +
+ ); + }; + }, }); diff --git a/src/components/music-radio/index.scss b/src/components/music-radio/index.scss index c32c1d9..55fdbe5 100644 --- a/src/components/music-radio/index.scss +++ b/src/components/music-radio/index.scss @@ -1 +1,3 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; diff --git a/src/components/music-radio/index.tsx b/src/components/music-radio/index.tsx index 9652a91..646dee4 100644 --- a/src/components/music-radio/index.tsx +++ b/src/components/music-radio/index.tsx @@ -1,11 +1,13 @@ -import { defineComponent, markRaw, onMounted, readonly, ref } from "vue"; -import "./index.scss"; +/** @format */ + +import { defineComponent, markRaw, onMounted, readonly, ref } from 'vue'; +import './index.scss'; export default defineComponent({ - name: "musicRadio", - setup(props, context) { - return () => { - return
音乐电台
; - }; - }, + name: 'musicRadio', + setup(props, context) { + return () => { + return
音乐电台
; + }; + }, }); diff --git a/src/components/mv/index.scss b/src/components/mv/index.scss index e69de29..1a1f0eb 100644 --- a/src/components/mv/index.scss +++ b/src/components/mv/index.scss @@ -0,0 +1 @@ +/** @format */ diff --git a/src/components/mv/index.tsx b/src/components/mv/index.tsx index d879cfc..1f215a6 100644 --- a/src/components/mv/index.tsx +++ b/src/components/mv/index.tsx @@ -1,17 +1,13 @@ -import { defineComponent } from "vue"; +/** @format */ + +import { defineComponent } from 'vue'; import './index.scss'; export default defineComponent({ - name: "Mv", - setup(props, { slots, emit }) { - - return () => { - return ( -
- mv -
- ) - } - - } -}) \ No newline at end of file + name: 'Mv', + setup(props, { slots, emit }) { + return () => { + return
mv
; + }; + }, +}); diff --git a/src/components/my-page/index.scss b/src/components/my-page/index.scss index 326a49b..7040c03 100644 --- a/src/components/my-page/index.scss +++ b/src/components/my-page/index.scss @@ -1,61 +1,59 @@ -@use "sass:math"; +/** @format */ -.my-page { - - .my-header { - display: flow-root; - margin-bottom: 24px; - - &-left { - float: left; - width: math.div(1, 7) * 100%; - height: 100%; - .my-avatar { - border-radius: 100%; - } - } - - &-right { - float: right; - width: math.div(6 , 7) * 100%; - height: 100%; - padding-left: 32px; - - .my-nickname { - padding: 16px 0 32px 0; - color: #333; - font-size: 28px; - font-weight: 700; - } +@use 'sass:math'; - .header-layer { - line-height: 2; - span { - display: inline-block; - width: 6rem; - color: #677; - } - em { - color: #555; - } - } - - } - - - } - - .my-layer { - .title { - padding: 24px 24px 24px 0; - color: #555; - font-size: 20px; - line-height: 1.4; - font-weight: 600; - i { - font-size: 14px; - vertical-align: top; - } - } - } -} \ No newline at end of file +.my-page { + .my-header { + display: flow-root; + margin-bottom: 24px; + + &-left { + float: left; + width: math.div(1, 7) * 100%; + height: 100%; + .my-avatar { + border-radius: 100%; + } + } + + &-right { + float: right; + width: math.div(6, 7) * 100%; + height: 100%; + padding-left: 32px; + + .my-nickname { + padding: 16px 0 32px 0; + color: #333; + font-size: 28px; + font-weight: 700; + } + + .header-layer { + line-height: 2; + span { + display: inline-block; + width: 6rem; + color: #677; + } + em { + color: #555; + } + } + } + } + + .my-layer { + .title { + padding: 24px 24px 24px 0; + color: #555; + font-size: 20px; + line-height: 1.4; + font-weight: 600; + i { + font-size: 14px; + vertical-align: top; + } + } + } +} diff --git a/src/components/my-page/index.tsx b/src/components/my-page/index.tsx index 9062a54..1621aa4 100644 --- a/src/components/my-page/index.tsx +++ b/src/components/my-page/index.tsx @@ -1,114 +1,105 @@ -import { userPlaylist, userRecord } from "@/api/user"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; -import useUserStore from "@/stores/user"; -import { getDate, getLocaleDate, phoneVerifyPatt, UNICODE_CHAR } from "@/utils"; -import MusicList from "@/widgets/music-list"; -import SongList from "@/widgets/song-list"; -import { NEmpty } from "naive-ui"; -import { - computed, - defineComponent, - nextTick, - reactive, - watch, - watchEffect, -} from "vue"; -import { onBeforeRouteLeave, useRoute, useRouter } from "vue-router"; +/** @format */ + +import { userPlaylist, userRecord } from '@/api/user'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; +import useUserStore from '@/stores/user'; +import { getDate, getLocaleDate, phoneVerifyPatt, UNICODE_CHAR } from '@/utils'; +import MusicList from '@/widgets/music-list'; +import SongList from '@/widgets/song-list'; +import { NEmpty } from 'naive-ui'; +import { computed, defineComponent, nextTick, reactive, watch, watchEffect } from 'vue'; +import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'; import './index.scss'; export default defineComponent({ - name: "MyPage", - setup(props, context) { - const userStore = useUserStore(); - const route = useRoute(); - - const myData = reactive({ - playRecord: [], - }); - - const relativeInfos = computed(() => { - const profile = userStore.detail.profile; - const infos = []; - infos.push({ name: `村龄${UNICODE_CHAR.smile}`, content: `${new Date().getFullYear() - getDate(profile.createTime).year}年(${getLocaleDate(profile.createTime)})` }); - infos.push({ name: '个人签名', content: profile.signature }); - return infos; - }); - - const routeUpdateHandler = async({query: { id }} : PlainObject) => { - if (!id) return; - const { weekData } = await userRecord({ uid: id }); - weekData && ( - myData.playRecord = weekData.map(({ song }: PlainObject) => song) - ); - }; - routeUpdateHandler(route); - - onFilteredBeforeRouteUpdate((to) => { - routeUpdateHandler(to); - }); - - const renderRegisterSign = () => {UNICODE_CHAR.registed} - - return () => { - - if (!userStore.isLogin) { - return - } - const { playRecord } = myData; - const { playlist: { myCreated, myCollection } } = userStore; - - return ( -
- -
- -
-
- -
-
-
-

- {userStore.detail.profile.nickname} -

- - { - relativeInfos.value.map(({ name, content }) => { - return ( -

- {name}: - {content} -

- ) - }) - } - -
- -
- -
-

- 我的播放记录{renderRegisterSign()}({playRecord.length}) -

- -
- -
-

- 我创建的{renderRegisterSign()}({myCreated.length}) -

- -
- -
-

- 我收藏的{renderRegisterSign()}({myCollection.length}) -

- -
-
- ) - } - }, + name: 'MyPage', + setup(props, context) { + const userStore = useUserStore(); + const route = useRoute(); + + const myData = reactive({ + playRecord: [], + }); + + const relativeInfos = computed(() => { + const profile = userStore.detail.profile; + const infos = []; + infos.push({ + name: `村龄${UNICODE_CHAR.smile}`, + content: `${ + new Date().getFullYear() - getDate(profile.createTime).year + }年(${getLocaleDate(profile.createTime)})`, + }); + infos.push({ name: '个人签名', content: profile.signature }); + return infos; + }); + + const routeUpdateHandler = async ({ query: { id } }: PlainObject) => { + if (!id) return; + const { weekData } = await userRecord({ uid: id }); + weekData && (myData.playRecord = weekData.map(({ song }: PlainObject) => song)); + }; + routeUpdateHandler(route); + + onFilteredBeforeRouteUpdate((to) => { + routeUpdateHandler(to); + }); + + const renderRegisterSign = () => {UNICODE_CHAR.registed}; + + return () => { + if (!userStore.isLogin) { + return ; + } + const { playRecord } = myData; + const { + playlist: { myCreated, myCollection }, + } = userStore; + + return ( +
+
+
+
+ +
+
+
+

{userStore.detail.profile.nickname}

+ + {relativeInfos.value.map(({ name, content }) => { + return ( +

+ {name}: + {content} +

+ ); + })} +
+
+ +
+

+ 我的播放记录{renderRegisterSign()}({playRecord.length}) +

+ +
+ +
+

+ 我创建的{renderRegisterSign()}({myCreated.length}) +

+ +
+ +
+

+ 我收藏的{renderRegisterSign()}({myCollection.length}) +

+ +
+
+ ); + }; + }, }); diff --git a/src/components/online-video/all/index.scss b/src/components/online-video/all/index.scss index e7fcf1f..65fb4b5 100644 --- a/src/components/online-video/all/index.scss +++ b/src/components/online-video/all/index.scss @@ -1,11 +1,11 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .online-video-all { - .video-all-foot { - @include flexHc(); - padding-top: 4rem; - padding-bottom: 1rem; - - } - -} \ No newline at end of file + .video-all-foot { + @include flexHc(); + padding-top: 4rem; + padding-bottom: 1rem; + } +} diff --git a/src/components/online-video/all/index.tsx b/src/components/online-video/all/index.tsx index d4b5867..283434f 100644 --- a/src/components/online-video/all/index.tsx +++ b/src/components/online-video/all/index.tsx @@ -1,79 +1,94 @@ -import { getAllVideoList, getVideoCategoryList, getVideoTagList } from "@/api/video"; -import { allVideoDatasItem, VideoTagItem } from "@/types/video"; -import { defineComponent, reactive, readonly, ref, watch, } from "vue"; +/** @format */ + +import { getAllVideoList, getVideoCategoryList, getVideoTagList } from '@/api/video'; +import { allVideoDatasItem, VideoTagItem } from '@/types/video'; +import { defineComponent, reactive, readonly, ref, watch } from 'vue'; import VideoList from '@widgets/video-list'; -import "./index.scss"; -import YuanButton from "@/widgets/yuan-button"; -import { messageBus } from "@/utils/event/register"; -import { UNICODE_CHAR } from "@/utils"; -import useUserStore from "@/stores/user"; -import { NEmpty } from "naive-ui"; +import './index.scss'; +import YuanButton from '@/widgets/yuan-button'; +import { messageBus } from '@/utils/event/register'; +import { UNICODE_CHAR } from '@/utils'; +import useUserStore from '@/stores/user'; +import { NEmpty } from 'naive-ui'; -export interface AllVideosInfoType { - list: allVideoDatasItem[]; - hasMore: boolean; - msg: string; +export interface AllVideosInfoType { + list: allVideoDatasItem[]; + hasMore: boolean; + msg: string; } export default defineComponent({ - name: "OnlineVideoAll", - setup(props, context) { - - const allVideosInfo = reactive({ - list: [], - hasMore: true, - msg: '', - }); + name: 'OnlineVideoAll', + setup(props, context) { + const allVideosInfo = reactive({ + list: [], + hasMore: true, + msg: '', + }); + + const userStore = useUserStore(); - const userStore = useUserStore(); + const loadCount = ref(0); - const loadCount = ref(0); - - const updateAllVideoList = () => { - getAllVideoList({ - offset: loadCount.value, - }).then(({code, datas, hasmore, msg}) => { - if(code !== 200) { - messageBus.dispatch('errorMessage', msg); - return; - } - allVideosInfo.list.push(...datas.map(({data}: allVideoDatasItem)=>data)); - allVideosInfo.hasMore = hasmore; - allVideosInfo.msg = msg; - }); - } + const updateAllVideoList = () => { + getAllVideoList({ + offset: loadCount.value, + }).then(({ code, datas, hasmore, msg }) => { + if (code !== 200) { + messageBus.dispatch('errorMessage', msg); + return; + } + allVideosInfo.list.push(...datas.map(({ data }: allVideoDatasItem) => data)); + allVideosInfo.hasMore = hasmore; + allVideosInfo.msg = msg; + }); + }; - watch(() => userStore.isLogin, (isLogin) => { - if(isLogin) { - updateAllVideoList(); - } - }, { - immediate: true, - }); + watch( + () => userStore.isLogin, + (isLogin) => { + if (isLogin) { + updateAllVideoList(); + } + }, + { + immediate: true, + }, + ); - const loadMoreHandler = () => { - if(!allVideosInfo.hasMore) { - return; - } - loadCount.value++; - updateAllVideoList(); - } + const loadMoreHandler = () => { + if (!allVideosInfo.hasMore) { + return; + } + loadCount.value++; + updateAllVideoList(); + }; - return () => { - if(!userStore.isLogin) { - return ( - - ) - } - const { hasMore } = allVideosInfo; - return
-
- -
-
- loadMoreHandler()} disabled={!hasMore}> -
-
; - }; - }, + return () => { + if (!userStore.isLogin) { + return ( + + ); + } + const { hasMore } = allVideosInfo; + return ( +
+
+ +
+
+ loadMoreHandler()} + disabled={!hasMore} + > +
+
+ ); + }; + }, }); diff --git a/src/components/online-video/category/index.scss b/src/components/online-video/category/index.scss index 2c05e90..5d19b72 100644 --- a/src/components/online-video/category/index.scss +++ b/src/components/online-video/category/index.scss @@ -1,7 +1,9 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .online-video-category { - .video-category-layer { - padding: 4rem 0; - } -} \ No newline at end of file + .video-category-layer { + padding: 4rem 0; + } +} diff --git a/src/components/online-video/category/index.tsx b/src/components/online-video/category/index.tsx index 0fca746..a00d2ad 100644 --- a/src/components/online-video/category/index.tsx +++ b/src/components/online-video/category/index.tsx @@ -1,144 +1,148 @@ -import { getVideoCategoryList, getVideos, getVideoTagList } from "@/api/video"; -import { allVideoDatasItem, VideoTagItem } from "@/types/video"; -import { messageBus } from "@/utils/event/register"; -import { NSpace, NxButton } from "naive-ui"; -import { computed, defineComponent, reactive, watch, watchEffect } from "vue"; -import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router"; +/** @format */ + +import { getVideoCategoryList, getVideos, getVideoTagList } from '@/api/video'; +import { allVideoDatasItem, VideoTagItem } from '@/types/video'; +import { messageBus } from '@/utils/event/register'; +import { NSpace, NxButton } from 'naive-ui'; +import { computed, defineComponent, reactive, watch, watchEffect } from 'vue'; +import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'; import VideoList from '@widgets/video-list'; -import "./index.scss"; -import { is } from "@/utils"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import './index.scss'; +import { is } from '@/utils'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export interface CategoryVideosInfoType { - tagList: VideoTagItem[]; - categoryList: VideoTagItem[]; -} + tagList: VideoTagItem[]; + categoryList: VideoTagItem[]; +} /** * 对tagList、categoryList内容排序 - * @param list - * @returns + * @param list + * @returns */ - export const getSortedTagOrCategoryList = (list: VideoTagItem[]) => { - return list.sort((a,b) => a.name.localeCompare(b.name, void 0, { - //地域匹配算法 - localeMatcher: 'lookup', - //排序敏感程度 - sensitivity: 'base', - //是否忽略标点符号 - ignorePunctuation: false, - //是否使用数字排序 - numeric: true, - //大小写排序顺序:小写优先 - caseFirst: 'lower', - })); -} - +export const getSortedTagOrCategoryList = (list: VideoTagItem[]) => { + return list.sort((a, b) => + a.name.localeCompare(b.name, void 0, { + //地域匹配算法 + localeMatcher: 'lookup', + //排序敏感程度 + sensitivity: 'base', + //是否忽略标点符号 + ignorePunctuation: false, + //是否使用数字排序 + numeric: true, + //大小写排序顺序:小写优先 + caseFirst: 'lower', + }), + ); +}; + export default defineComponent({ - name: "OnlineVideoCategory", - setup(props, context) { - const route = useRoute(); - const router = useRouter(); - const activeInfo = reactive({ - index: -1, - videoList: [] as allVideoDatasItem['data'][], - offset: 0, - hasMore: true, - msg: '', - }); + name: 'OnlineVideoCategory', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); + const activeInfo = reactive({ + index: -1, + videoList: [] as allVideoDatasItem['data'][], + offset: 0, + hasMore: true, + msg: '', + }); + + const categoryVideosInfo = reactive({ + tagList: [], + categoryList: [], + }); + + getVideoTagList() + .then(({ data }) => { + categoryVideosInfo.tagList = getSortedTagOrCategoryList(data); + }) + .then(() => { + getVideoList(); + }); + + getVideoCategoryList().then(({ data }) => { + categoryVideosInfo.categoryList = getSortedTagOrCategoryList(data); + }); - const categoryVideosInfo = reactive({ - tagList: [], - categoryList: [], - }); - - getVideoTagList().then(({data}) => { - categoryVideosInfo.tagList = getSortedTagOrCategoryList(data); - }).then(() => { - getVideoList(); - }); - - getVideoCategoryList().then(({data}) => { - categoryVideosInfo.categoryList = getSortedTagOrCategoryList(data); - }); + const getVideoList = async () => { + const [{ id: firstId }] = categoryVideosInfo.tagList; + const { offset = 0, id = firstId } = route.query; + const { code, msg, hasMore, datas } = await getVideos({ + id: Number(id), + offset: Number(offset), + }); + if (code !== 200) { + messageBus.dispatch('errorMessage', msg); + return; + } + activeInfo.videoList = (datas as allVideoDatasItem[]).map(({ data }) => data); + activeInfo.msg = msg; + activeInfo.hasMore = hasMore; + }; - const getVideoList = async () => { - const [{id: firstId}] = categoryVideosInfo.tagList; - const { offset = 0 , id = firstId } = route.query - const { code, msg, hasMore, datas } = await getVideos({ - id: Number(id), - offset: Number(offset), - }); - if(code !== 200) { - messageBus.dispatch('errorMessage', msg); - return; - } - activeInfo.videoList = (datas as allVideoDatasItem[]).map(({data}) => data); - activeInfo.msg = msg; - activeInfo.hasMore = hasMore; - } - - onFilteredBeforeRouteUpdate((to, from) => { - const { offset, id } = to.query; - const { offset:fromOffset, id: fromId } = from.query; - if( id !== fromId || offset !== fromOffset ) { - getVideoList(); - } - }); + onFilteredBeforeRouteUpdate((to, from) => { + const { offset, id } = to.query; + const { offset: fromOffset, id: fromId } = from.query; + if (id !== fromId || offset !== fromOffset) { + getVideoList(); + } + }); - watch<[string, VideoTagItem[]]>(() => [route.query.id as string, categoryVideosInfo.tagList], ([routeId, tagList]) => { - const realRouteId = +routeId; - const targetTagIndex = is.undefined(routeId) ? 0 : tagList.findIndex(({id}) => id === realRouteId); - activeInfo.index = targetTagIndex; - }); + watch<[string, VideoTagItem[]]>( + () => [route.query.id as string, categoryVideosInfo.tagList], + ([routeId, tagList]) => { + const realRouteId = +routeId; + const targetTagIndex = is.undefined(routeId) + ? 0 + : tagList.findIndex(({ id }) => id === realRouteId); + activeInfo.index = targetTagIndex; + }, + ); - const tagButtonClickHandler = (tag: string, id: string | number) => { - router.push({ - ...route, - query: { - ...route.query, - id, - } - }); - } + const tagButtonClickHandler = (tag: string, id: string | number) => { + router.push({ + ...route, + query: { + ...route.query, + id, + }, + }); + }; - const renderVideoTagList = () => { - const activeTagIdxVal = activeInfo.index; - return ( - - { - categoryVideosInfo.tagList.map(({ - id, - name, - }, i) => { - return ( - tagButtonClickHandler(name, id)} - > - { - name - } - - ) - }) - } - - ) - } + const renderVideoTagList = () => { + const activeTagIdxVal = activeInfo.index; + return ( + + {categoryVideosInfo.tagList.map(({ id, name }, i) => { + return ( + tagButtonClickHandler(name, id)} + > + {name} + + ); + })} + + ); + }; - return () => { - return
- { - renderVideoTagList() - } -
- -
-
; - }; - }, + return () => { + return ( +
+ {renderVideoTagList()} +
+ +
+
+ ); + }; + }, }); diff --git a/src/components/online-video/index.scss b/src/components/online-video/index.scss index ec4c682..9414cb1 100644 --- a/src/components/online-video/index.scss +++ b/src/components/online-video/index.scss @@ -1,9 +1,10 @@ -@import "@scss/variable"; -.online-video { - h2 { - font-size: 30px; - font-weight: 600; - color: #333; - } +/** @format */ -} \ No newline at end of file +@import '@scss/variable'; +.online-video { + h2 { + font-size: 30px; + font-weight: 600; + color: #333; + } +} diff --git a/src/components/online-video/index.tsx b/src/components/online-video/index.tsx index 4d207ba..443e874 100644 --- a/src/components/online-video/index.tsx +++ b/src/components/online-video/index.tsx @@ -1,29 +1,28 @@ -import { defineComponent } from "vue"; -import CommonRouterList, { RouteListProp } from "@/widgets/common-router-list"; -import "./index.scss"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; +/** @format */ + +import { defineComponent } from 'vue'; +import CommonRouterList, { RouteListProp } from '@/widgets/common-router-list'; +import './index.scss'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; const videoPageRouteList: RouteListProp = [ - { text: '全部', to: 'all' }, - { text: '分类', to: 'category' }, -] + { text: '全部', to: 'all' }, + { text: '分类', to: 'category' }, +]; export default defineComponent({ - name: "OnlineVideo", - setup(props, context) { - - return () => { - return
-

- 视频 -

-
- -
- { - renderKeepAliveRouterView() - } -
; - }; - }, + name: 'OnlineVideo', + setup(props, context) { + return () => { + return ( +
+

视频

+
+ +
+ {renderKeepAliveRouterView()} +
+ ); + }; + }, }); diff --git a/src/components/personal-recommend/index.scss b/src/components/personal-recommend/index.scss index d92ea55..05f58e0 100644 --- a/src/components/personal-recommend/index.scss +++ b/src/components/personal-recommend/index.scss @@ -1,105 +1,104 @@ +/** @format */ + @use 'sass:math'; @import '@scss/variable'; .personal-recommend { + &-layer { + padding-bottom: 40px; + h5 { + color: #333; + font-weight: 500; + font-size: 24px; + line-height: 3; + } + } - &-layer { - padding-bottom: 40px; - h5 { - color: #333; - font-weight: 500; - font-size: 24px; - line-height: 3; - } - } + .personal-fm { + .fm-box { + position: relative; + padding: math.div(1, 12) * 100%; + border-radius: 8px; - .personal-fm { - .fm-box { - position: relative; - padding: math.div(1 , 12) * 100%; - border-radius: 8px; + .bubble { + position: absolute; + left: 0; + top: 0; + width: 12%; + background-color: rgba(244, 244, 244, 0.6); + clip-path: circle(100% at 0 0); + } - .bubble { - position: absolute; - left: 0; - top: 0; - width: 12%; - background-color: rgba(244,244,244,.6); - clip-path: circle(100% at 0 0); - } + .tip { + position: absolute; + left: 3%; + top: 3%; + color: $colorbase; + font-size: 32px; + font-weight: 600; + z-index: 99; + } - .tip { - position: absolute; - left: 3%; - top: 3%; - color: $colorbase; - font-size: 32px; - font-weight: 600; - z-index: 99; - } + .fm-main { + @include flexVc(); + position: relative; - .fm-main { - @include flexVc(); - position: relative; - - .fm-pic { - border-radius: 8px; - } - - .play-toggle { - @include flexHcVc(); - position: absolute; - left: 0; - top: 0; - width: math.div(1,3) * 100%; - background-color: rgba(40,40,40,.35); - color: $colorbase; - opacity: 0; - transition: opacity .1s; - font-size: 40px; + .fm-pic { + border-radius: 8px; + } - &:hover { - opacity: 1; - } - } + .play-toggle { + @include flexHcVc(); + position: absolute; + left: 0; + top: 0; + width: math.div(1, 3) * 100%; + background-color: rgba(40, 40, 40, 0.35); + color: $colorbase; + opacity: 0; + transition: opacity 0.1s; + font-size: 40px; - .fm-content { - max-width: 100%; - padding-left: 8%; - overflow: hidden; - } + &:hover { + opacity: 1; + } + } - .fm-name { - color: $colorbase; - font-size: 16px; - font-weight: 500; - line-height: 2; - } + .fm-content { + max-width: 100%; + padding-left: 8%; + overflow: hidden; + } - .fm-singers { - color: #ddd; - font-size: 14px; - line-height: 2.2; - } + .fm-name { + color: $colorbase; + font-size: 16px; + font-weight: 500; + line-height: 2; + } - .fm-tools { - @include flexVc(); - color: $colorbase; - - .tool-item { - margin-right: 10px; - font-size: 38px; - cursor: pointer; - transition: color .1s; + .fm-singers { + color: #ddd; + font-size: 14px; + line-height: 2.2; + } - &:hover { - color: var(--theme); - } - } - } - } + .fm-tools { + @include flexVc(); + color: $colorbase; - } - } + .tool-item { + margin-right: 10px; + font-size: 38px; + cursor: pointer; + transition: color 0.1s; -} \ No newline at end of file + &:hover { + color: var(--theme); + } + } + } + } + } + } +} diff --git a/src/components/personal-recommend/index.tsx b/src/components/personal-recommend/index.tsx index f08efa8..cd655cd 100644 --- a/src/components/personal-recommend/index.tsx +++ b/src/components/personal-recommend/index.tsx @@ -1,184 +1,192 @@ -import { playlistRecommend } from "@/api/playlist"; -import useUserStore from "@/stores/user"; +/** @format */ + +import { playlistRecommend } from '@/api/playlist'; +import useUserStore from '@/stores/user'; import { - computed, - defineComponent, - markRaw, - onMounted, - reactive, - readonly, - ref, - watch, - watchEffect, -} from "vue"; -import "./index.scss"; + computed, + defineComponent, + markRaw, + onMounted, + reactive, + readonly, + ref, + watch, + watchEffect, +} from 'vue'; +import './index.scss'; import Songlist from '@widgets/song-list'; -import { getImageMainColorString, is, padPicCrop } from "@/utils"; -import { NGrid, NGridItem } from "naive-ui"; -import { useRoute, useRouter } from "vue-router"; -import { musicRecommend } from "@/api/music"; -import MusicList from "@/widgets/music-list"; -import { getPersonalFm } from "@/api/other"; +import { getImageMainColorString, is, padPicCrop } from '@/utils'; +import { NGrid, NGridItem } from 'naive-ui'; +import { useRoute, useRouter } from 'vue-router'; +import { musicRecommend } from '@/api/music'; +import MusicList from '@/widgets/music-list'; +import { getPersonalFm } from '@/api/other'; import { MusicLoveIcon, MusicSinger } from '@widgets/music-tiny-comp'; -import usePlayerStore, { currentSongRefGlobal, toNext } from "@stores/player"; -import { getModifiedNewestSongInfo } from "@/utils/apiSpecial"; +import usePlayerStore, { currentSongRefGlobal, toNext } from '@stores/player'; +import { getModifiedNewestSongInfo } from '@/utils/apiSpecial'; import { PlayStatusSwitch } from '@widgets/music-tiny-comp'; export const PersonalFm = defineComponent({ - name: 'PersonalFm', - setup(props, { slots, emit }) { - - const userStore = useUserStore(); - const playerStore = usePlayerStore(); - - const getPersonalRecommendFm = async () => { - const { data } = await getPersonalFm(); - const personalFMList = playerStore.personalFM.songList; - data.forEach((songItem: any) => { - if (personalFMList.some((fmItem) => fmItem.id === songItem.id)) { - return; - } - personalFMList.push(getModifiedNewestSongInfo(songItem)); - }); - } - getPersonalRecommendFm(); - - const currentVisibleFM = computed(() => { - const personalFM = playerStore.personalFM; - return personalFM.isFM ? currentSongRefGlobal : personalFM.songList[0]; - }); - - const picMainColor = ref(''); - watchEffect(async () => { - picMainColor.value = await getImageMainColorString(currentVisibleFM.value?.album?.picUrl); - }); - - const toNextHandler = () => { - toNext(); - } - - return () => { - const currentVisibleFMValue = currentVisibleFM.value; - if (is.undefined(currentVisibleFMValue)) { - return; - } - const { detail } = userStore; - const { musicName, singers, album, id } = currentVisibleFMValue; - const picUrl = padPicCrop(album.picUrl, { x: 120, y: 120 }); - - return ( -
- -
哈喽!阿哪哒!今天为你推荐 {detail.profile.nickname} 的音乐电台
- - - -
- - - For -

- You -
-
- -
- -
-
-
- { - musicName - } -
- -
-
- -
-
- -
-
-
-
-
-
-
- -
- - ) - - } - } + name: 'PersonalFm', + setup(props, { slots, emit }) { + const userStore = useUserStore(); + const playerStore = usePlayerStore(); + + const getPersonalRecommendFm = async () => { + const { data } = await getPersonalFm(); + const personalFMList = playerStore.personalFM.songList; + data.forEach((songItem: any) => { + if (personalFMList.some((fmItem) => fmItem.id === songItem.id)) { + return; + } + personalFMList.push(getModifiedNewestSongInfo(songItem)); + }); + }; + getPersonalRecommendFm(); + + const currentVisibleFM = computed(() => { + const personalFM = playerStore.personalFM; + return personalFM.isFM ? currentSongRefGlobal : personalFM.songList[0]; + }); + + const picMainColor = ref(''); + watchEffect(async () => { + picMainColor.value = await getImageMainColorString( + currentVisibleFM.value?.album?.picUrl, + ); + }); + + const toNextHandler = () => { + toNext(); + }; + + return () => { + const currentVisibleFMValue = currentVisibleFM.value; + if (is.undefined(currentVisibleFMValue)) { + return; + } + const { detail } = userStore; + const { musicName, singers, album, id } = currentVisibleFMValue; + const picUrl = padPicCrop(album.picUrl, { x: 120, y: 120 }); + + return ( +
+
+ 哈喽!阿哪哒!今天为你推荐 {detail.profile.nickname} 的音乐电台 +
+ + + +
+ + + For +

+ You +
+
+ +
+ +
+
+
+ {musicName} +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+ ); + }; + }, }); export default defineComponent({ - name: "PersonalRecommend", - setup(props, { slots, emit }) { - - const userStore = useUserStore(); - const router = useRouter(); - - const recommendData = reactive({ - songlist: [] as any[], - music: { - dailySongs: [] as any[], - recommendReasons: [] as string[], - orderSongs: [] as any[], - } - }) - - //获取每日推荐歌单 - const getPersonalRecommendSonglist = async () => { - const { recommend } = await playlistRecommend(); - recommendData.songlist = recommend; - } - - //获取每日推荐音乐 - const getPersonalRecommendMusics = async () => { - const { data: { orderSongs, recommendReasons, dailySongs } } = await musicRecommend(); - recommendData.music = { orderSongs, recommendReasons, dailySongs } - } - - watch(() => userStore.isLogin, (isLogin) => { - if (isLogin) { - getPersonalRecommendSonglist(); - getPersonalRecommendMusics(); - } - }, { - immediate: true - }); - - return () => { - const { - songlist, - music: { - dailySongs - } - } = recommendData; - - return ( -
- - { - !is.emptyArray(dailySongs) && -
-
每日推荐歌曲
- -
- } - { - !is.emptyArray(songlist) && -
-
我的私荐歌单
- -
- } - -
- ); - }; - }, + name: 'PersonalRecommend', + setup(props, { slots, emit }) { + const userStore = useUserStore(); + const router = useRouter(); + + const recommendData = reactive({ + songlist: [] as any[], + music: { + dailySongs: [] as any[], + recommendReasons: [] as string[], + orderSongs: [] as any[], + }, + }); + + //获取每日推荐歌单 + const getPersonalRecommendSonglist = async () => { + const { recommend } = await playlistRecommend(); + recommendData.songlist = recommend; + }; + + //获取每日推荐音乐 + const getPersonalRecommendMusics = async () => { + const { + data: { orderSongs, recommendReasons, dailySongs }, + } = await musicRecommend(); + recommendData.music = { orderSongs, recommendReasons, dailySongs }; + }; + + watch( + () => userStore.isLogin, + (isLogin) => { + if (isLogin) { + getPersonalRecommendSonglist(); + getPersonalRecommendMusics(); + } + }, + { + immediate: true, + }, + ); + + return () => { + const { + songlist, + music: { dailySongs }, + } = recommendData; + + return ( +
+ + {!is.emptyArray(dailySongs) && ( +
+
每日推荐歌曲
+ +
+ )} + {!is.emptyArray(songlist) && ( +
+
我的私荐歌单
+ +
+ )} +
+ ); + }; + }, }); diff --git a/src/components/player-controller/index.scss b/src/components/player-controller/index.scss index 55433ac..d8bb055 100644 --- a/src/components/player-controller/index.scss +++ b/src/components/player-controller/index.scss @@ -1,139 +1,140 @@ -@use "sass:math"; -@import "@scss/variable"; - -.player-controller { - position: relative; - height: $homeBottomControllerHeight; - - &[lyricPageShow="false"] { - color: #666777; - } - - &[lyricPageShow="true"] { - margin: 0 100px; - color: $colorbase; - .playbill-mask { - transform: rotate(180deg); - } - } - - .controller-progressbar { - position: relative; - } - - .controller-main { - @include flexVc(); - height: calc(100% - #{$progressBarWidthOrHeight}); - padding: 8px 20px 16px 45px; - } - - .main-block { - @include flexVc(); - width: math.div(1, 3) * 100%; - height: 100%; - - &.main-center { - justify-content: center; - } - - &.main-right { - justify-content: flex-end; - } - } - - .music-playbill { - position: relative; - height: 100%; - overflow: hidden; - border-radius: 4px; - cursor: pointer; - - &:hover { - .playbill-mask { - opacity: 1; - } - } - - img { - height: 100%; - } - - .playbill-mask { - opacity: 0; - @include flexHcVc(); - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - background-color: rgba(40, 40, 50, 0.3); - backdrop-filter: blur(1px); - font-size: 26px; - color: #edd; - transition: opacity 0.1s; - } - } - - .music-info { - padding-left: 16px; - overflow: hidden; - - .name { - padding-bottom: 10px; - font-size: 16px; - font-weight: 500; - cursor: pointer; - &:hover { - color: var(--theme); - } - } - } - - .controller-play-switch { - @include flexHcVc(); - width: 44px; - height: 44px; - border-radius: 100%; - background-color: var(--theme); - color: #fff; - font-size: 22px; - transition: filter 0.1s; - &:hover { - filter: brightness(115%); - } - } - - .play-queue-icon { - @include flexVc(); - position: relative; - color: currentColor; - cursor: pointer; - transition: color 0.1s; - padding-left: 20px; - - i { - font-size: 34px; - } - - span { - font-size: 14px; - } - - &:hover { - color: var(--theme); - } - } - - .prev-music, - .next-music { - margin: 0 8px; - color: currentColor; - font-size: 34px; - cursor: pointer; - transition: color 0.1s; - &:hover { - color: var(--theme); - } - } - -} \ No newline at end of file +/** @format */ + +@use 'sass:math'; +@import '@scss/variable'; + +.player-controller { + position: relative; + height: $homeBottomControllerHeight; + + &[lyricPageShow='false'] { + color: #666777; + } + + &[lyricPageShow='true'] { + margin: 0 100px; + color: $colorbase; + .playbill-mask { + transform: rotate(180deg); + } + } + + .controller-progressbar { + position: relative; + } + + .controller-main { + @include flexVc(); + height: calc(100% - #{$progressBarWidthOrHeight}); + padding: 8px 20px 16px 45px; + } + + .main-block { + @include flexVc(); + width: math.div(1, 3) * 100%; + height: 100%; + + &.main-center { + justify-content: center; + } + + &.main-right { + justify-content: flex-end; + } + } + + .music-playbill { + position: relative; + height: 100%; + overflow: hidden; + border-radius: 4px; + cursor: pointer; + + &:hover { + .playbill-mask { + opacity: 1; + } + } + + img { + height: 100%; + } + + .playbill-mask { + opacity: 0; + @include flexHcVc(); + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(40, 40, 50, 0.3); + backdrop-filter: blur(1px); + font-size: 26px; + color: #edd; + transition: opacity 0.1s; + } + } + + .music-info { + padding-left: 16px; + overflow: hidden; + + .name { + padding-bottom: 10px; + font-size: 16px; + font-weight: 500; + cursor: pointer; + &:hover { + color: var(--theme); + } + } + } + + .controller-play-switch { + @include flexHcVc(); + width: 44px; + height: 44px; + border-radius: 100%; + background-color: var(--theme); + color: #fff; + font-size: 22px; + transition: filter 0.1s; + &:hover { + filter: brightness(115%); + } + } + + .play-queue-icon { + @include flexVc(); + position: relative; + color: currentColor; + cursor: pointer; + transition: color 0.1s; + padding-left: 20px; + + i { + font-size: 34px; + } + + span { + font-size: 14px; + } + + &:hover { + color: var(--theme); + } + } + + .prev-music, + .next-music { + margin: 0 8px; + color: currentColor; + font-size: 34px; + cursor: pointer; + transition: color 0.1s; + &:hover { + color: var(--theme); + } + } +} diff --git a/src/components/player-controller/index.tsx b/src/components/player-controller/index.tsx index 6bbcd19..8bf418a 100644 --- a/src/components/player-controller/index.tsx +++ b/src/components/player-controller/index.tsx @@ -1,141 +1,159 @@ +/** @format */ import { - PlayStatusSwitch, - Volume, - MusicLoveIcon, - MusicSinger, - PlayOrder, -} from "@/widgets/music-tiny-comp"; -import "./index.scss"; -import { padPicCrop } from "@/utils"; -import { useRoute, useRouter } from "vue-router"; -import usePlayerStore, { currentSongRefGlobal, playerQueueShow , playerQueue, toPrevious , toNext } from "@/stores/player"; -import ProgressBar, { ProgressInfo } from "@/widgets/progress-bar"; -import { renderCurrentPlayTime } from "@/widgets/common-renderer"; -import { computed, defineComponent, PropType, ref } from "vue"; -import { currentTimeRefGlobal, durationRefGlobal, nextSeekTimeRefGlobal, playingRefGlobal } from "@/stores/audio"; -import { onKeyUp } from "@vueuse/core"; + PlayStatusSwitch, + Volume, + MusicLoveIcon, + MusicSinger, + PlayOrder, +} from '@/widgets/music-tiny-comp'; +import './index.scss'; +import { padPicCrop } from '@/utils'; +import { useRoute, useRouter } from 'vue-router'; +import usePlayerStore, { + currentSongRefGlobal, + playerQueueShow, + playerQueue, + toPrevious, + toNext, +} from '@/stores/player'; +import ProgressBar, { ProgressInfo } from '@/widgets/progress-bar'; +import { renderCurrentPlayTime } from '@/widgets/common-renderer'; +import { computed, defineComponent, PropType, ref } from 'vue'; +import { + currentTimeRefGlobal, + durationRefGlobal, + nextSeekTimeRefGlobal, + playingRefGlobal, +} from '@/stores/audio'; +import { onKeyUp } from '@vueuse/core'; export const isCtrlAndArrowRight = ({ ctrlKey, key }: KeyboardEvent) => { - return ctrlKey && key === 'ArrowRight' -} + return ctrlKey && key === 'ArrowRight'; +}; export const isCtrlAndArrowLeft = ({ ctrlKey, key }: KeyboardEvent) => { - return ctrlKey && key === 'ArrowLeft' -} + return ctrlKey && key === 'ArrowLeft'; +}; export default defineComponent({ - name: "PlayerController", - props: { - displayInLyricPage: { - type: Boolean as PropType, - required: false, - default: false, - }, - }, - setup(props, { slots, emit }) { - const router = useRouter(); - const route = useRoute(); - const playerStore = usePlayerStore(); - - /** - * Ctrl+Left切换上一首 - */ - onKeyUp(isCtrlAndArrowLeft, toPrevious); + name: 'PlayerController', + props: { + displayInLyricPage: { + type: Boolean as PropType, + required: false, + default: false, + }, + }, + setup(props, { slots, emit }) { + const router = useRouter(); + const route = useRoute(); + const playerStore = usePlayerStore(); + + /** + * Ctrl+Left切换上一首 + */ + onKeyUp(isCtrlAndArrowLeft, toPrevious); - /** - * Ctrl+Right切换下一首 - */ - onKeyUp(isCtrlAndArrowRight, toNext); + /** + * Ctrl+Right切换下一首 + */ + onKeyUp(isCtrlAndArrowRight, toNext); - const showOrHidePlayerDetailPage = () => { - const { query, path } = route; - router.push({ - path, - query: { - ...query!, - playerStatus: Number(!props.displayInLyricPage), - }, - }); - }; + const showOrHidePlayerDetailPage = () => { + const { query, path } = route; + router.push({ + path, + query: { + ...query!, + playerStatus: Number(!props.displayInLyricPage), + }, + }); + }; - const showPlayerQueueHandler = () => { - playerQueueShow.value = true; - }; + const showPlayerQueueHandler = () => { + playerQueueShow.value = true; + }; - const progressUp = ({ decimal }: ProgressInfo) => { - nextSeekTimeRefGlobal.value = durationRefGlobal.value * decimal; - playingRefGlobal.value = true; - }; + const progressUp = ({ decimal }: ProgressInfo) => { + nextSeekTimeRefGlobal.value = durationRefGlobal.value * decimal; + playingRefGlobal.value = true; + }; - const toCurrentSongPage = () => { - router.push({ - path: `/song/${currentSongRefGlobal.value.id}` - }) - } + const toCurrentSongPage = () => { + router.push({ + path: `/song/${currentSongRefGlobal.value.id}`, + }); + }; - return () => { - const currentTimeValue = currentTimeRefGlobal.value; - const duration= durationRefGlobal.value; - const { id, musicName, singers, album } = currentSongRefGlobal.value; - const queueSongList = playerQueue.value; - return ( -
-
- {}} - onMove={ () => {}} - onChange={() => {}} - onUp={progressUp} - > -
+ return () => { + const currentTimeValue = currentTimeRefGlobal.value; + const duration = durationRefGlobal.value; + const { id, musicName, singers, album } = currentSongRefGlobal.value; + const queueSongList = playerQueue.value; + return ( +
+
+ {}} + onMove={() => {}} + onChange={() => {}} + onUp={progressUp} + > +
-
-
-
- -
- -
-
+
+
+
+ +
+ +
+
-
-
{musicName}
- -
-
+
+
+ {musicName} +
+ +
+
-
- -
- -
-
- -
-
- -
- -
+
+ +
+ +
+
+ +
+
+ +
+ +
-
- { - renderCurrentPlayTime() - } - -
- - {queueSongList.length} -
-
-
-
- ); - }; - }, +
+ {renderCurrentPlayTime()} + +
+ + {queueSongList.length} +
+
+
+
+ ); + }; + }, }); diff --git a/src/components/player-queue/index.scss b/src/components/player-queue/index.scss index 7a8eb0f..77e801c 100644 --- a/src/components/player-queue/index.scss +++ b/src/components/player-queue/index.scss @@ -1,142 +1,140 @@ -@import "@scss/variable"; +/** @format */ +@import '@scss/variable'; .player-queue { - @include flex(); - flex-direction: column; - position: absolute; - left: 100%; - top: 0; - width: 300px; - height: 100%; - z-index: 200; - background-color: #fff; - filter: drop-shadow(-4px 0 10px rgba(107, 94, 83, 0.15)); - transition: transform 0.3s ease-out; - - &[slideShow="true"] { - transform: translateX(-100%); - } - - .queue-header { - padding: 38px 28px 0 26px; - - h3 { - color: #333; - font-size: 22px; - } - - .header-layer { - .total-songs { - color: #888; - font-size: 13px; - } - line-height: 3; - } - } - - .queue-main { - position: relative; - flex: 1; - overflow: hidden; - - .list-body { - position: relative; - height: 100%; - - .song-item { - position: relative; - padding: 16px 28px 16px 26px; - transition: background-color 0.12s; - - &[active="true"], - &:hover { - background-color: rgba(127, 134, 127, 0.1); - .tools { - opacity: 1; - } - } - - h6 { - color: #444; - font-size: 15px; - } - em { - color: #777; - font-size: 13px; - } - .item-layer { - padding-top: 10px; - } - - .tools { - opacity: 0; - @include flexVc(); - position: absolute; - right: 0; - top: 0; - bottom: 0; - padding-right: 20px; - color: #888; - - .tool-item { - cursor: pointer; - margin: 0 6px; - font-size: 24px; - &:hover { - color: var(--theme) - } - } - - } - } - } - - .locate-to-current { - position: absolute; - right: 40px; - bottom: 40px; - padding: 2px; - background-color: $colorbase; - color: #999; - font-size: 22px; - border-radius: 100%; - transition: .28s; - box-shadow: 0 0 28px rgba(120, 140,132, .5); - cursor: pointer; - - &:active { - box-shadow: 0 0 60px $colortaobao; - filter: contrast(200%); - } - } - - } - - .queue-foot { - @include flexHeVc(); - height: 80px; - padding: 0 28px 0 26px; - - em { - color: #666; - transition: color .1s; - cursor: pointer; - &:hover { - color: var(--theme); - } - } - - span { - font-size: 14px; - text-indent: .2em; - vertical-align: middle; - } - - i { - font-size: 30px; - vertical-align: middle; - } - } + @include flex(); + flex-direction: column; + position: absolute; + left: 100%; + top: 0; + width: 300px; + height: 100%; + z-index: 200; + background-color: #fff; + filter: drop-shadow(-4px 0 10px rgba(107, 94, 83, 0.15)); + transition: transform 0.3s ease-out; + &[slideShow='true'] { + transform: translateX(-100%); + } + + .queue-header { + padding: 38px 28px 0 26px; + + h3 { + color: #333; + font-size: 22px; + } + + .header-layer { + .total-songs { + color: #888; + font-size: 13px; + } + line-height: 3; + } + } + + .queue-main { + position: relative; + flex: 1; + overflow: hidden; + + .list-body { + position: relative; + height: 100%; + + .song-item { + position: relative; + padding: 16px 28px 16px 26px; + transition: background-color 0.12s; + + &[active='true'], + &:hover { + background-color: rgba(127, 134, 127, 0.1); + .tools { + opacity: 1; + } + } + + h6 { + color: #444; + font-size: 15px; + } + em { + color: #777; + font-size: 13px; + } + .item-layer { + padding-top: 10px; + } + + .tools { + opacity: 0; + @include flexVc(); + position: absolute; + right: 0; + top: 0; + bottom: 0; + padding-right: 20px; + color: #888; + + .tool-item { + cursor: pointer; + margin: 0 6px; + font-size: 24px; + &:hover { + color: var(--theme); + } + } + } + } + } + + .locate-to-current { + position: absolute; + right: 40px; + bottom: 40px; + padding: 2px; + background-color: $colorbase; + color: #999; + font-size: 22px; + border-radius: 100%; + transition: 0.28s; + box-shadow: 0 0 28px rgba(120, 140, 132, 0.5); + cursor: pointer; + + &:active { + box-shadow: 0 0 60px $colortaobao; + filter: contrast(200%); + } + } + } + + .queue-foot { + @include flexHeVc(); + height: 80px; + padding: 0 28px 0 26px; + + em { + color: #666; + transition: color 0.1s; + cursor: pointer; + &:hover { + color: var(--theme); + } + } + + span { + font-size: 14px; + text-indent: 0.2em; + vertical-align: middle; + } + + i { + font-size: 30px; + vertical-align: middle; + } + } } diff --git a/src/components/player-queue/index.tsx b/src/components/player-queue/index.tsx index 4b42b17..9c26326 100644 --- a/src/components/player-queue/index.tsx +++ b/src/components/player-queue/index.tsx @@ -1,113 +1,122 @@ -import usePlayerStore, { currentSongRefGlobal, playerQueue, playerQueueShow } from "@/stores/player"; -import { onClickOutside } from "@vueuse/core"; -import { defineComponent, ref, watchEffect } from "vue"; -import { useRoute, useRouter } from "vue-router"; -import "./index.scss"; -import { MusicLoveIcon, PlayStatusSwitch, } from "@widgets/music-tiny-comp"; -import { is } from "@/utils"; +/** @format */ + +import usePlayerStore, { + currentSongRefGlobal, + playerQueue, + playerQueueShow, +} from '@/stores/player'; +import { onClickOutside } from '@vueuse/core'; +import { defineComponent, ref, watchEffect } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import './index.scss'; +import { MusicLoveIcon, PlayStatusSwitch } from '@widgets/music-tiny-comp'; +import { is } from '@/utils'; export default defineComponent({ - name: "PlayerQueue", - setup(props, { slots, emit }) { - const router = useRouter(); - const route = useRoute(); - const playerStore = usePlayerStore(); - const playerQueueRef = ref(); - const listBodyElm = ref(); - const listContentUlRef = ref(); + name: 'PlayerQueue', + setup(props, { slots, emit }) { + const router = useRouter(); + const route = useRoute(); + const playerStore = usePlayerStore(); + const playerQueueRef = ref(); + const listBodyElm = ref(); + const listContentUlRef = ref(); - const hidePlayerQueueHandler = () => (playerQueueShow.value = false); - const songItemDblClickHandler = (id: number) => - playerStore.handlePlaySoundNeededData(id); + const hidePlayerQueueHandler = () => (playerQueueShow.value = false); + const songItemDblClickHandler = (id: number) => + playerStore.handlePlaySoundNeededData(id); - onClickOutside(playerQueueRef, () => { - hidePlayerQueueHandler(); - }); + onClickOutside(playerQueueRef, () => { + hidePlayerQueueHandler(); + }); - /** - * 定位到当前歌曲 - * @returns - */ - const locatoToCurrentSong = () => { - const queueSongList = playerQueue.value; - if (is.emptyArray(queueSongList)) { - return; - } - const { id: curSongId } = currentSongRefGlobal.value; - const targetIndex = queueSongList.findIndex(({ id }) => id === curSongId); + /** + * 定位到当前歌曲 + * @returns + */ + const locatoToCurrentSong = () => { + const queueSongList = playerQueue.value; + if (is.emptyArray(queueSongList)) { + return; + } + const { id: curSongId } = currentSongRefGlobal.value; + const targetIndex = queueSongList.findIndex(({ id }) => id === curSongId); - const targetScrollTop = (listContentUlRef.value!.children[targetIndex] as HTMLLIElement).offsetTop; - const { scrollTop, offsetHeight } = listBodyElm.value!; - //如果目标在可视区域内,就return - if ( - targetScrollTop >= scrollTop && - targetScrollTop < scrollTop + offsetHeight - ) { - return; - } - listBodyElm.value!.scrollTop = targetScrollTop; - } + const targetScrollTop = ( + listContentUlRef.value!.children[targetIndex] as HTMLLIElement + ).offsetTop; + const { scrollTop, offsetHeight } = listBodyElm.value!; + //如果目标在可视区域内,就return + if (targetScrollTop >= scrollTop && targetScrollTop < scrollTop + offsetHeight) { + return; + } + listBodyElm.value!.scrollTop = targetScrollTop; + }; - return () => { - const { id: curSongId } =currentSongRefGlobal.value; - const queueSongList = playerQueue.value; + return () => { + const { id: curSongId } = currentSongRefGlobal.value; + const queueSongList = playerQueue.value; - return ( - - ); - }; - }, + return ( + + ); + }; + }, }); diff --git a/src/components/search/album/index.scss b/src/components/search/album/index.scss index e4c6446..ec3e259 100644 --- a/src/components/search/album/index.scss +++ b/src/components/search/album/index.scss @@ -1,6 +1,6 @@ -@import "@scss/variable"; +/** @format */ -.search-album { - +@import '@scss/variable'; -} \ No newline at end of file +.search-album { +} diff --git a/src/components/search/album/index.tsx b/src/components/search/album/index.tsx index c83f360..44c8938 100644 --- a/src/components/search/album/index.tsx +++ b/src/components/search/album/index.tsx @@ -1,39 +1,44 @@ +/** @format */ + import { - defineComponent, - inject, - watch, - reactive, - watchEffect, - onActivated, - WatchStopHandle, -} from "@vue/runtime-core"; -import { SearchCloundData } from "../index"; -import "./index.scss"; -import AlbumList from "@widgets/album-list"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { ref } from "vue"; + defineComponent, + inject, + watch, + reactive, + watchEffect, + onActivated, + WatchStopHandle, +} from '@vue/runtime-core'; +import { SearchCloundData } from '../index'; +import './index.scss'; +import AlbumList from '@widgets/album-list'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { ref } from 'vue'; const defaultSearchAlbumLimit = PAGE_SIZE[COMPONENT_NAME.SEARCH_ALBUM]; - + export default defineComponent({ - name: COMPONENT_NAME.SEARCH_ALBUM, - setup(props, context) { - const searchData = inject("searchCloundData")!; - const total = ref(0); - watchEffect(() => { - total.value = searchData.album.albumCount; - }); - return () => { - const { - album: { albums }, - } = searchData; - return ( - <> -
- -
- - ); - }; - }, + name: COMPONENT_NAME.SEARCH_ALBUM, + setup(props, context) { + const searchData = inject('searchCloundData')!; + const total = ref(0); + watchEffect(() => { + total.value = searchData.album.albumCount; + }); + return () => { + const { + album: { albums }, + } = searchData; + return ( + <> +
+ +
+ + ); + }; + }, }); diff --git a/src/components/search/index.scss b/src/components/search/index.scss index aa77ad2..0bfa275 100644 --- a/src/components/search/index.scss +++ b/src/components/search/index.scss @@ -1,55 +1,57 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .singer-avatar { - border-radius: 100%; + border-radius: 100%; } .album-pic { - border-radius: 0.5rem; + border-radius: 0.5rem; } .search-multimatch { - @include flexHbVc(); - flex-wrap: wrap; - padding: 1rem; - background-color: rgba(213, 223, 217, 0.2); - - .mutimatch-box { - flex: 1; - @include flexVc(); - cursor: pointer; - - &:not(:last-of-type) { - margin-right: 2rem; - } - - img { - width: 100px; - height: 100px; - object-fit: cover; - margin-right: 1rem; - } - - .mutimatch-main { - h5 { - padding-bottom: 1rem; - color: #344; - font-size: 20px; - font-weight: 600; - } - - > div { - @include flexHbVc(); - - em { - margin-right: 1rem; - color: #aaa; - - &.mutimatch-link:hover { - color: var(--theme); - } - } - } - } - } + @include flexHbVc(); + flex-wrap: wrap; + padding: 1rem; + background-color: rgba(213, 223, 217, 0.2); + + .mutimatch-box { + flex: 1; + @include flexVc(); + cursor: pointer; + + &:not(:last-of-type) { + margin-right: 2rem; + } + + img { + width: 100px; + height: 100px; + object-fit: cover; + margin-right: 1rem; + } + + .mutimatch-main { + h5 { + padding-bottom: 1rem; + color: #344; + font-size: 20px; + font-weight: 600; + } + + > div { + @include flexHbVc(); + + em { + margin-right: 1rem; + color: #aaa; + + &.mutimatch-link:hover { + color: var(--theme); + } + } + } + } + } } diff --git a/src/components/search/index.tsx b/src/components/search/index.tsx index ae241b4..923442b 100644 --- a/src/components/search/index.tsx +++ b/src/components/search/index.tsx @@ -1,297 +1,315 @@ +/** @format */ + +import { defineComponent, reactive, watch, provide } from 'vue'; import { - defineComponent, - reactive, - watch, - provide, -} from "vue"; -import { - useRoute, - useRouter, - LocationQuery, - onBeforeRouteLeave, - RouteLocationNormalized, - onBeforeRouteUpdate, -} from "vue-router"; + useRoute, + useRouter, + LocationQuery, + onBeforeRouteLeave, + RouteLocationNormalized, + onBeforeRouteUpdate, +} from 'vue-router'; import { - objToQuery, - deepCopy, - getLocaleCount, - getLocaleDate, - padPicCrop, - is, -} from "@/utils"; -import "./index.scss"; -import { EMPTY_OBJ } from "@/utils"; -import { SearchLyricItem } from "@/types/lyric"; -import { SearchPlaylist } from "@/types/songlist"; -import { SearchSingerItem } from "@/types/singer"; -import { SearchUserProfileItem } from "@/types/user"; -import { searchCloud, searchMulMatch } from "@api/search"; -import CommonRouterList from "@/widgets/common-router-list"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; + objToQuery, + deepCopy, + getLocaleCount, + getLocaleDate, + padPicCrop, + is, +} from '@/utils'; +import './index.scss'; +import { EMPTY_OBJ } from '@/utils'; +import { SearchLyricItem } from '@/types/lyric'; +import { SearchPlaylist } from '@/types/songlist'; +import { SearchSingerItem } from '@/types/singer'; +import { SearchUserProfileItem } from '@/types/user'; +import { searchCloud, searchMulMatch } from '@api/search'; +import CommonRouterList from '@/widgets/common-router-list'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export const baseSearchCate = [ - { text: "歌曲", to: "/search/songs", type: 1, limit: PAGE_SIZE.DEFAULT }, - { text: "视频", to: "/search/video", type: 1014, limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_VIDEO] }, - { text: "专辑", to: "/search/album", type: 10, limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_ALBUM] }, - { text: "歌单", to: "/search/songlist", type: 1000, limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_SONGLIST] }, - { text: "歌词", to: "/search/lyric", type: 1006, limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_LYRIC] }, - { text: "歌手", to: "/search/singer", type: 100, limit: PAGE_SIZE.DEFAULT }, - { text: "电台", to: "/search/radio", type: 1009, limit: PAGE_SIZE.DEFAULT }, - { text: "MV", to: "/search/mv", type: 1004, limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_MV] }, - { text: "用户", to: "/search/user", type: 1002, limit: PAGE_SIZE.DEFAULT }, + { text: '歌曲', to: '/search/songs', type: 1, limit: PAGE_SIZE.DEFAULT }, + { + text: '视频', + to: '/search/video', + type: 1014, + limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_VIDEO], + }, + { + text: '专辑', + to: '/search/album', + type: 10, + limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_ALBUM], + }, + { + text: '歌单', + to: '/search/songlist', + type: 1000, + limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_SONGLIST], + }, + { + text: '歌词', + to: '/search/lyric', + type: 1006, + limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_LYRIC], + }, + { text: '歌手', to: '/search/singer', type: 100, limit: PAGE_SIZE.DEFAULT }, + { text: '电台', to: '/search/radio', type: 1009, limit: PAGE_SIZE.DEFAULT }, + { + text: 'MV', + to: '/search/mv', + type: 1004, + limit: PAGE_SIZE[COMPONENT_NAME.SEARCH_MV], + }, + { text: '用户', to: '/search/user', type: 1002, limit: PAGE_SIZE.DEFAULT }, ]; export enum SearchKeyTypeMap { - music = 1, - video = 1014, - album = 10, - playlist = 1000, - lyric = 1006, - singer = 100, - radio = 1009, - mv = 1004, - user = 1002, + music = 1, + video = 1014, + album = 10, + playlist = 1000, + lyric = 1006, + singer = 100, + radio = 1009, + mv = 1004, + user = 1002, } export interface SearchCloundData extends PlainObject { - music: { - songCount: number; - songs: any[]; - }; - video: { - videoCount: number; - videos: any[]; - }; - album: { - albumCount: number; - albums: any[]; - }; - playlist: { - playlistCount: number; - playlists: SearchPlaylist[]; - }; - lyric: { - songCount: number; - songs: SearchLyricItem[]; - }; - singer: { - artistCount: number; - artists: SearchSingerItem[]; - }; - mv: PlainObject; - user: { - userprofilesCount: number; - userprofiles: SearchUserProfileItem[]; - }; - radio: PlainObject; + music: { + songCount: number; + songs: any[]; + }; + video: { + videoCount: number; + videos: any[]; + }; + album: { + albumCount: number; + albums: any[]; + }; + playlist: { + playlistCount: number; + playlists: SearchPlaylist[]; + }; + lyric: { + songCount: number; + songs: SearchLyricItem[]; + }; + singer: { + artistCount: number; + artists: SearchSingerItem[]; + }; + mv: PlainObject; + user: { + userprofilesCount: number; + userprofiles: SearchUserProfileItem[]; + }; + radio: PlainObject; } export default defineComponent({ - name: "Search", - setup(props, context) { - const route = useRoute(); - const router = useRouter(); + name: 'Search', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); - const searchCate = reactive( - deepCopy(baseSearchCate) - ); + const searchCate = reactive(deepCopy(baseSearchCate)); - const searchMulMatchData = reactive({ - album: [] as any[], - artist: [] as any[], - }); + const searchMulMatchData = reactive({ + album: [] as any[], + artist: [] as any[], + }); - const searchCloundData = reactive({ - music: { - songCount: 0, - songs: [], - }, - video: { - videos: [], - videoCount: 0, - }, - album: { - albumCount: 0, - albums: [], - }, - playlist: { - playlistCount: 0, - playlists: [], - }, - lyric: { - songCount: 0, - songs: [], - }, - singer: { - artistCount: 0, - artists: [], - }, - mv: {}, - user: { - userprofilesCount: 0, - userprofiles: [], - }, - radio: {}, - }); - provide("searchCloundData", searchCloundData); + const searchCloundData = reactive({ + music: { + songCount: 0, + songs: [], + }, + video: { + videos: [], + videoCount: 0, + }, + album: { + albumCount: 0, + albums: [], + }, + playlist: { + playlistCount: 0, + playlists: [], + }, + lyric: { + songCount: 0, + songs: [], + }, + singer: { + artistCount: 0, + artists: [], + }, + mv: {}, + user: { + userprofilesCount: 0, + userprofiles: [], + }, + radio: {}, + }); + provide('searchCloundData', searchCloundData); - const toArtistDetailPage = (id: string | number, path = "") => - router.push({ - path: `/artist/${path}`, - query: { - id, - }, - }); + const toArtistDetailPage = (id: string | number, path = '') => + router.push({ + path: `/artist/${path}`, + query: { + id, + }, + }); - /** - * 获取搜索的头数据(包括歌手、专辑) - * @param keywords - */ - const getSearchMulMatch = async (keywords: string) => { - const { result = {} } = await searchMulMatch({ keywords }); - const { album = [], artist = [] } = result; - searchMulMatchData.album = album; - searchMulMatchData.artist = artist; - }; + /** + * 获取搜索的头数据(包括歌手、专辑) + * @param keywords + */ + const getSearchMulMatch = async (keywords: string) => { + const { result = {} } = await searchMulMatch({ keywords }); + const { album = [], artist = [] } = result; + searchMulMatchData.album = album; + searchMulMatchData.artist = artist; + }; - /** - * 获取搜索的详细数据(包括歌曲、歌词、歌单等各类别) - * @param keywords - * @param type - */ - const getSearchCloundData = async (query: LocationQuery) => { - const { keywords, type, limit, offset } = query as PlainObject; - const typeCat = SearchKeyTypeMap[+type]; - const { result } = await searchCloud({ - type, - keywords, - limit, - offset, - }); - result && (searchCloundData[typeCat] = result); - }; + /** + * 获取搜索的详细数据(包括歌曲、歌词、歌单等各类别) + * @param keywords + * @param type + */ + const getSearchCloundData = async (query: LocationQuery) => { + const { keywords, type, limit, offset } = query as PlainObject; + const typeCat = SearchKeyTypeMap[+type]; + const { result } = await searchCloud({ + type, + keywords, + limit, + offset, + }); + result && (searchCloundData[typeCat] = result); + }; - const routeUpdateHandler = async ( - { query }: RouteLocationNormalized, - { query: oldQuery }: RouteLocationNormalized = EMPTY_OBJ - ) => { - const { keywords: oldKeywords } = oldQuery || EMPTY_OBJ; - const { keywords } = query as PlainObject; - searchCate.forEach((item: typeof baseSearchCate[number], i) => { - const { to: baseTo, type, limit } = baseSearchCate[i]; - const queryStr = objToQuery( - { keywords, type: `${type}`, limit, offset: "0" }, - true - ); - item.to = baseTo + queryStr; - }); - if (oldKeywords !== keywords) { - getSearchMulMatch(keywords); - } - getSearchCloundData(query); - } - routeUpdateHandler(route); + const routeUpdateHandler = async ( + { query }: RouteLocationNormalized, + { query: oldQuery }: RouteLocationNormalized = EMPTY_OBJ, + ) => { + const { keywords: oldKeywords } = oldQuery || EMPTY_OBJ; + const { keywords } = query as PlainObject; + searchCate.forEach((item: typeof baseSearchCate[number], i) => { + const { to: baseTo, type, limit } = baseSearchCate[i]; + const queryStr = objToQuery( + { keywords, type: `${type}`, limit, offset: '0' }, + true, + ); + item.to = baseTo + queryStr; + }); + if (oldKeywords !== keywords) { + getSearchMulMatch(keywords); + } + getSearchCloundData(query); + }; + routeUpdateHandler(route); - onFilteredBeforeRouteUpdate((to, from) => { - routeUpdateHandler(to, from); - }); + onFilteredBeforeRouteUpdate((to, from) => { + routeUpdateHandler(to, from); + }); - const renderArtists = () => { - return searchMulMatchData.artist.map((item, i) => ( -
toArtistDetailPage(item.id)} - > - -
-
- 歌手: - {`${item.name}${item.trans && `(${item.trans})`}`} -
-
- { - ev.stopPropagation(); - toArtistDetailPage(item.id, "allSongs"); - }} - > - 单曲: - {getLocaleCount(item.musicSize)} - - { - ev.stopPropagation(); - toArtistDetailPage(item.id, "mv"); - }} - > - MV: - {getLocaleCount(item.mvSize)} - - - 粉丝: - {getLocaleCount(item.fansSize)} - -
-
-
- )); - }; + const renderArtists = () => { + return searchMulMatchData.artist.map((item, i) => ( +
toArtistDetailPage(item.id)} + > + +
+
+ 歌手: + {`${item.name}${item.trans && `(${item.trans})`}`} +
+
+ { + ev.stopPropagation(); + toArtistDetailPage(item.id, 'allSongs'); + }} + > + 单曲: + {getLocaleCount(item.musicSize)} + + { + ev.stopPropagation(); + toArtistDetailPage(item.id, 'mv'); + }} + > + MV: + {getLocaleCount(item.mvSize)} + + + 粉丝: + {getLocaleCount(item.fansSize)} + +
+
+
+ )); + }; - const renderAlbums = () => { - return searchMulMatchData.album.map((item, i) => ( -
- -
-
- 专辑: - {`${item.name}`} -
-
- - 发布时间: - {getLocaleDate(item.publishTime)} - -
-
-
- )); - }; + const renderAlbums = () => { + return searchMulMatchData.album.map((item, i) => ( +
+ +
+
+ 专辑: + {`${item.name}`} +
+
+ + 发布时间: + {getLocaleDate(item.publishTime)} + +
+
+
+ )); + }; - return () => { - const { artist, album } = searchMulMatchData; - const hasSearchMutiMatch = [artist, album].some((_) => !is.emptyArray(_)); - return ( - - ); - }; - }, + return () => { + const { artist, album } = searchMulMatchData; + const hasSearchMutiMatch = [artist, album].some((_) => !is.emptyArray(_)); + return ( + + ); + }; + }, }); diff --git a/src/components/search/lyric/index.scss b/src/components/search/lyric/index.scss index 3f8fb88..30faed3 100644 --- a/src/components/search/lyric/index.scss +++ b/src/components/search/lyric/index.scss @@ -1,53 +1,55 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .search-lyric { - border-top: 1px solid #eee; - - &-item { - border-bottom: 1px solid #eee; - - .song-info { - @include flexVc(); - padding: 18px 0; - - .song-name { - flex: 2; - border-left: 4px solid var(--theme); - text-indent: 2em; - } - - .song-artist, - .song-album, - .song-duration { - flex: 1; - } - } - - .lyric-wrap { - display: flow-root; - padding: 20px 30px; - - .lyric-container { - float: left; - color: #777; - line-height: 1.6; - } - - .lyric-tool { - float: right; - @include flex(); - padding-top: 6em; - font-size: 14px; - span { - &:hover { - color: var(--theme); - } - padding: 0 20px; - color: #888; - font-weight: 500; - cursor: pointer; - } - } - } - } + border-top: 1px solid #eee; + + &-item { + border-bottom: 1px solid #eee; + + .song-info { + @include flexVc(); + padding: 18px 0; + + .song-name { + flex: 2; + border-left: 4px solid var(--theme); + text-indent: 2em; + } + + .song-artist, + .song-album, + .song-duration { + flex: 1; + } + } + + .lyric-wrap { + display: flow-root; + padding: 20px 30px; + + .lyric-container { + float: left; + color: #777; + line-height: 1.6; + } + + .lyric-tool { + float: right; + @include flex(); + padding-top: 6em; + font-size: 14px; + span { + &:hover { + color: var(--theme); + } + padding: 0 20px; + color: #888; + font-weight: 500; + cursor: pointer; + } + } + } + } } diff --git a/src/components/search/lyric/index.tsx b/src/components/search/lyric/index.tsx index 300dd43..df8baf4 100644 --- a/src/components/search/lyric/index.tsx +++ b/src/components/search/lyric/index.tsx @@ -1,174 +1,167 @@ -import { getLocaleDate, msSecondToTimeStr } from "@/utils"; +/** @format */ + +import { getLocaleDate, msSecondToTimeStr } from '@/utils'; import { - defineComponent, - markRaw, - watch, - onMounted, - WatchStopHandle, - onActivated, - watchEffect, - ref, - inject, - reactive, - computed, - PropType, -} from "vue"; -import { useRoute, useRouter, onBeforeRouteLeave } from "vue-router"; -import { SearchCloundData } from "../index"; -import "./index.scss"; -import { SearchLyricItem } from "@/types/lyric"; -import RoutePagination, { PagiInfo } from "@widgets/route-pagination"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; + defineComponent, + markRaw, + watch, + onMounted, + WatchStopHandle, + onActivated, + watchEffect, + ref, + inject, + reactive, + computed, + PropType, +} from 'vue'; +import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'; +import { SearchCloundData } from '../index'; +import './index.scss'; +import { SearchLyricItem } from '@/types/lyric'; +import RoutePagination, { PagiInfo } from '@widgets/route-pagination'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; -const defaultLimit = PAGE_SIZE[COMPONENT_NAME.SEARCH_LYRIC] +const defaultLimit = PAGE_SIZE[COMPONENT_NAME.SEARCH_LYRIC]; const defaultSearchLyricInfo = { - offset: 0, - limit: defaultLimit, - sizeArr: Array(3) - .fill(0) - .map((v, i) => defaultLimit * (i + 1)), + offset: 0, + limit: defaultLimit, + sizeArr: Array(3) + .fill(0) + .map((v, i) => defaultLimit * (i + 1)), }; //渲染歌词内容 -const renderLyricContent = ( - lyrics: SearchLyricItem["lyrics"], - lineCount?: number -) => { - Number.isFinite(lineCount) && (lyrics = lyrics.slice(0, lineCount)); - return lyrics.map((item) => `
  • ${item}
  • `).join(""); +const renderLyricContent = (lyrics: SearchLyricItem['lyrics'], lineCount?: number) => { + Number.isFinite(lineCount) && (lyrics = lyrics.slice(0, lineCount)); + return lyrics.map((item) => `
  • ${item}
  • `).join(''); }; //歌词元素 export const LyricContentItem = defineComponent({ - name: "LyricContentItem", - props: { - lyricData: { - type: Object as PropType, - requried: true, - }, - }, - //解构props值将会丢失其响应式 - setup({ lyricData }, { slots, emit }) { - if (!lyricData) return; - const { name, alia, ar, lyrics, al, dt } = lyricData; - const router = useRouter(); + name: 'LyricContentItem', + props: { + lyricData: { + type: Object as PropType, + requried: true, + }, + }, + //解构props值将会丢失其响应式 + setup({ lyricData }, { slots, emit }) { + if (!lyricData) return; + const { name, alia, ar, lyrics, al, dt } = lyricData; + const router = useRouter(); - //前往歌手详情界面 - const toArtistDetailPage = (id: number) => - router.push({ - path: "/artist", - query: { id }, - }); + //前往歌手详情界面 + const toArtistDetailPage = (id: number) => + router.push({ + path: '/artist', + query: { id }, + }); - //歌词是否已经展开 - const isSpreaded = ref(false); - //歌词显示的行数 - const briefLineCount = computed(() => (isSpreaded.value ? Infinity : 4)); - //切换歌词展开状态 - const switchSpreadStatus = () => (isSpreaded.value = !isSpreaded.value); - //渲染歌手名字dom - const renderArtists = () => { - const lastIdx = ar.length - 1; - return ar.map(({ name, id }, i) => ( - <> - toArtistDetailPage(id)}>{name} - {lastIdx > i && " / "} - - )); - }; - const artists = ar.map(({ name }) => name).join("/"); - const albumName = `《${al.name}》`; - const duration = msSecondToTimeStr(dt); - return () => ( -
    -
    - - {name} - - - {renderArtists()} - - - {albumName} - - - {duration} - -
    -
    -
      -
      - - {isSpreaded.value ? "收起歌词" : "展开歌词"} - - 复制歌词 -
      -
      -
      - ); - }, + //歌词是否已经展开 + const isSpreaded = ref(false); + //歌词显示的行数 + const briefLineCount = computed(() => (isSpreaded.value ? Infinity : 4)); + //切换歌词展开状态 + const switchSpreadStatus = () => (isSpreaded.value = !isSpreaded.value); + //渲染歌手名字dom + const renderArtists = () => { + const lastIdx = ar.length - 1; + return ar.map(({ name, id }, i) => ( + <> + toArtistDetailPage(id)}>{name} + {lastIdx > i && ' / '} + + )); + }; + const artists = ar.map(({ name }) => name).join('/'); + const albumName = `《${al.name}》`; + const duration = msSecondToTimeStr(dt); + return () => ( +
      +
      + + {name} + + + {renderArtists()} + + + {albumName} + + + {duration} + +
      +
      +
        +
        + + {isSpreaded.value ? '收起歌词' : '展开歌词'} + + 复制歌词 +
        +
        +
        + ); + }, }); export default defineComponent({ - name: COMPONENT_NAME.SEARCH_LYRIC, - setup(props, context) { - const route = useRoute(); - const searchData = inject("searchCloundData")!; - const { - sizeArr, - limit: dftLimit, - offset: dftOffset, - } = defaultSearchLyricInfo; - const lyricPagiInfo = reactive({ - sizeArr, - total: 0, - limit: dftLimit, - offset: dftOffset, - }); + name: COMPONENT_NAME.SEARCH_LYRIC, + setup(props, context) { + const route = useRoute(); + const searchData = inject('searchCloundData')!; + const { sizeArr, limit: dftLimit, offset: dftOffset } = defaultSearchLyricInfo; + const lyricPagiInfo = reactive({ + sizeArr, + total: 0, + limit: dftLimit, + offset: dftOffset, + }); - watchEffect(() => { - lyricPagiInfo.total = searchData.lyric.songCount; - }); + watchEffect(() => { + lyricPagiInfo.total = searchData.lyric.songCount; + }); - let lyricRouteWatcher: WatchStopHandle; - onActivated(() => { - lyricRouteWatcher = watch( - () => route.query, - async (query, oldQuery) => { - const { limit, offset } = query as PlainObject; - lyricPagiInfo.limit = limit; - lyricPagiInfo.offset = offset; - }, - { - immediate: true, - } - ); - }); + let lyricRouteWatcher: WatchStopHandle; + onActivated(() => { + lyricRouteWatcher = watch( + () => route.query, + async (query, oldQuery) => { + const { limit, offset } = query as PlainObject; + lyricPagiInfo.limit = limit; + lyricPagiInfo.offset = offset; + }, + { + immediate: true, + }, + ); + }); - onBeforeRouteLeave(() => { - lyricRouteWatcher(); - }); + onBeforeRouteLeave(() => { + lyricRouteWatcher(); + }); - return () => { - const { - lyric: { songs, songCount }, - } = searchData; - return ( - <> -
        - { - songs?.map((item) => - - ) - } -
        - - - ); - }; - }, + return () => { + const { + lyric: { songs, songCount }, + } = searchData; + return ( + <> +
        + {songs?.map((item) => ( + + ))} +
        + + + ); + }; + }, }); diff --git a/src/components/search/mv/index.scss b/src/components/search/mv/index.scss index e69de29..1a1f0eb 100644 --- a/src/components/search/mv/index.scss +++ b/src/components/search/mv/index.scss @@ -0,0 +1 @@ +/** @format */ diff --git a/src/components/search/mv/index.tsx b/src/components/search/mv/index.tsx index 21bfa1e..a616ebd 100644 --- a/src/components/search/mv/index.tsx +++ b/src/components/search/mv/index.tsx @@ -1,51 +1,57 @@ +/** @format */ + import { - computed, - defineComponent, - markRaw, - onActivated, - onDeactivated, - onMounted, - onUnmounted, - reactive, - ref, - shallowReactive, - toRefs, - watch, - WatchStopHandle, - inject, - watchEffect, -} from "vue"; + computed, + defineComponent, + markRaw, + onActivated, + onDeactivated, + onMounted, + onUnmounted, + reactive, + ref, + shallowReactive, + toRefs, + watch, + WatchStopHandle, + inject, + watchEffect, +} from 'vue'; import { - useRoute, - useRouter, - LocationQuery, - onBeforeRouteUpdate, - onBeforeRouteLeave, -} from "vue-router"; -import "./index.scss"; -import RoutePagination, { PagiInfo } from "@widgets/route-pagination"; -import { SearchCloundData } from "../index"; + useRoute, + useRouter, + LocationQuery, + onBeforeRouteUpdate, + onBeforeRouteLeave, +} from 'vue-router'; +import './index.scss'; +import RoutePagination, { PagiInfo } from '@widgets/route-pagination'; +import { SearchCloundData } from '../index'; import VideoList from '@widgets/video-list'; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import MvList from "@/widgets/mv-list"; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import MvList from '@/widgets/mv-list'; const defaultLimit = PAGE_SIZE[COMPONENT_NAME.SEARCH_MV]; export default defineComponent({ - name: COMPONENT_NAME.SEARCH_MV, - setup(props, context) { - const searchData = inject("searchCloundData")!; - - return () => { - const { - mv: { mvs, mvCount }, - } = searchData; - return ( -
        - -
        + name: COMPONENT_NAME.SEARCH_MV, + setup(props, context) { + const searchData = inject('searchCloundData')!; - ); - }; - }, + return () => { + const { + mv: { mvs, mvCount }, + } = searchData; + return ( +
        + +
        + ); + }; + }, }); diff --git a/src/components/search/radio/index.scss b/src/components/search/radio/index.scss index c32c1d9..55fdbe5 100644 --- a/src/components/search/radio/index.scss +++ b/src/components/search/radio/index.scss @@ -1 +1,3 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; diff --git a/src/components/search/radio/index.tsx b/src/components/search/radio/index.tsx index 35a7a7e..9f905f9 100644 --- a/src/components/search/radio/index.tsx +++ b/src/components/search/radio/index.tsx @@ -1,12 +1,14 @@ -import { defineComponent, markRaw, onMounted, ref } from "vue"; -import { useRoute, useRouter } from "vue-router"; -import "./index.scss"; +/** @format */ + +import { defineComponent, markRaw, onMounted, ref } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import './index.scss'; export default defineComponent({ - name: "SearchRadio", - setup(props, context) { - return () => { - return
        ; - }; - }, + name: 'SearchRadio', + setup(props, context) { + return () => { + return
        ; + }; + }, }); diff --git a/src/components/search/singer/index.scss b/src/components/search/singer/index.scss index b64ab31..9ba27cd 100644 --- a/src/components/search/singer/index.scss +++ b/src/components/search/singer/index.scss @@ -1,34 +1,36 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .search-singer { - @include flex(); - flex-wrap: wrap; + @include flex(); + flex-wrap: wrap; - &-item { - width: 8.2%; + &-item { + width: 8.2%; - img { - width: 100%; - border-radius: 50%; - transition: 0.18s; - cursor: pointer; - &:hover { - transform: scale(1.08); - filter: drop-shadow(0 0 20px rgba(80, 80, 80, 0.2)); - } - } + img { + width: 100%; + border-radius: 50%; + transition: 0.18s; + cursor: pointer; + &:hover { + transform: scale(1.08); + filter: drop-shadow(0 0 20px rgba(80, 80, 80, 0.2)); + } + } - &:not(:nth-of-type(10n)) { - margin-right: 2%; - } + &:not(:nth-of-type(10n)) { + margin-right: 2%; + } - &:nth-of-type(n + 11) { - margin-top: 2%; - } + &:nth-of-type(n + 11) { + margin-top: 2%; + } - .singer-info { - text-align: center; - line-height: 3; - } - } + .singer-info { + text-align: center; + line-height: 3; + } + } } diff --git a/src/components/search/singer/index.tsx b/src/components/search/singer/index.tsx index de911a2..47b691f 100644 --- a/src/components/search/singer/index.tsx +++ b/src/components/search/singer/index.tsx @@ -1,25 +1,27 @@ -import { padPicCrop } from "@/utils"; -import { defineComponent, markRaw, onMounted, ref, inject } from "vue"; -import { useRoute, useRouter } from "vue-router"; -import { SearchCloundData } from "../index"; -import "./index.scss"; +/** @format */ -import ArtistList from "@/widgets/artist-list"; +import { padPicCrop } from '@/utils'; +import { defineComponent, markRaw, onMounted, ref, inject } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { SearchCloundData } from '../index'; +import './index.scss'; + +import ArtistList from '@/widgets/artist-list'; export default defineComponent({ - name: "SearchSinger", - setup(props, context) { - const router = useRouter(); - const searchData = inject("searchCloundData")!; - return () => { - const { - singer: { artists = [], artistCount }, - } = searchData; - return ( -
        - -
        - ); - }; - }, + name: 'SearchSinger', + setup(props, context) { + const router = useRouter(); + const searchData = inject('searchCloundData')!; + return () => { + const { + singer: { artists = [], artistCount }, + } = searchData; + return ( +
        + +
        + ); + }; + }, }); diff --git a/src/components/search/songlist/index.scss b/src/components/search/songlist/index.scss index 5000adc..5a79fef 100644 --- a/src/components/search/songlist/index.scss +++ b/src/components/search/songlist/index.scss @@ -1,56 +1,58 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .search-songlist { - &-item { - @include flexVc(); - padding: 10px; - transition: 0.18s; - - &:hover { - background-color: rgba(170, 160, 180, 0.1); - } - - img { - width: 80px; - height: 80px; - border-radius: 6px; - } - - .songlist-name { - flex: 4; - text-indent: 2em; - } - - .songlist-trackcount { - flex: 1; - color: rgba(80, 80, 80, 0.7); - cursor: text; - } - - .songlist-creator { - flex: 2; - padding-right: 2em; - color: rgba(56, 53, 53, 0.7); - font-size: 18px; - - span { - cursor: pointer; - &:hover { - color: var(--theme); - } - } - } - - .songlist-collection { - flex: 1; - color: rgba(80, 80, 80, 0.7); - cursor: text; - } - - .songlist-listen { - flex: 1; - color: rgba(80, 80, 80, 0.7); - cursor: text; - } - } + &-item { + @include flexVc(); + padding: 10px; + transition: 0.18s; + + &:hover { + background-color: rgba(170, 160, 180, 0.1); + } + + img { + width: 80px; + height: 80px; + border-radius: 6px; + } + + .songlist-name { + flex: 4; + text-indent: 2em; + } + + .songlist-trackcount { + flex: 1; + color: rgba(80, 80, 80, 0.7); + cursor: text; + } + + .songlist-creator { + flex: 2; + padding-right: 2em; + color: rgba(56, 53, 53, 0.7); + font-size: 18px; + + span { + cursor: pointer; + &:hover { + color: var(--theme); + } + } + } + + .songlist-collection { + flex: 1; + color: rgba(80, 80, 80, 0.7); + cursor: text; + } + + .songlist-listen { + flex: 1; + color: rgba(80, 80, 80, 0.7); + cursor: text; + } + } } diff --git a/src/components/search/songlist/index.tsx b/src/components/search/songlist/index.tsx index 3794f0f..cda843d 100644 --- a/src/components/search/songlist/index.tsx +++ b/src/components/search/songlist/index.tsx @@ -1,99 +1,90 @@ -import { getLocaleCount } from "@/utils"; -import { defineComponent, markRaw, onMounted, ref, inject } from "vue"; -import { useRoute, useRouter } from "vue-router"; -import { SearchCloundData } from "../index"; -import RoutePagination, { PagiInfo } from "@widgets/route-pagination"; -import "./index.scss"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; +/** @format */ + +import { getLocaleCount } from '@/utils'; +import { defineComponent, markRaw, onMounted, ref, inject } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { SearchCloundData } from '../index'; +import RoutePagination, { PagiInfo } from '@widgets/route-pagination'; +import './index.scss'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; const defaultLimit = PAGE_SIZE[COMPONENT_NAME.SEARCH_SONGLIST]; export default defineComponent({ - name: COMPONENT_NAME.SEARCH_SONGLIST, - setup(props, context) { - const router = useRouter(); - const route = useRoute(); - const searchData = inject("searchCloundData")!; + name: COMPONENT_NAME.SEARCH_SONGLIST, + setup(props, context) { + const router = useRouter(); + const route = useRoute(); + const searchData = inject('searchCloundData')!; - //前往歌单创建者的详情页 - const toCreatorDetailPage = (userId: number) => - router.push({ - path: "/user", - query: { - id: userId, - }, - }); + //前往歌单创建者的详情页 + const toCreatorDetailPage = (userId: number) => + router.push({ + path: '/user', + query: { + id: userId, + }, + }); - //前往歌单详情页 - const toPlaylistDetailPage = (id: number) => - router.push({ - path: "/songlist/:id", - name: 'songlist', - params: { - id, - }, - }); + //前往歌单详情页 + const toPlaylistDetailPage = (id: number) => + router.push({ + path: '/songlist/:id', + name: 'songlist', + params: { + id, + }, + }); - const renderRoutePagination = (playlistCount: number) => { - const pagiInfo: PagiInfo = { - total: playlistCount, - sizeArr: Array(3) - .fill(0) - .map((v, i) => defaultLimit * ++i), - offset: route.query.offset as string, - limit: defaultLimit, - }; - return ; - }; + const renderRoutePagination = (playlistCount: number) => { + const pagiInfo: PagiInfo = { + total: playlistCount, + sizeArr: Array(3) + .fill(0) + .map((v, i) => defaultLimit * ++i), + offset: route.query.offset as string, + limit: defaultLimit, + }; + return ; + }; - return () => { - const { - playlist: { playlists, playlistCount }, - } = searchData; - return ( -
        - { - playlists.map( - ({ - coverImgUrl, - creator, - trackCount, - id, - name, - playCount, - bookCount, - subscribed, - }) => ( -
        toPlaylistDetailPage(id)} - > - - {`${name}`} - - {`${getLocaleCount(trackCount)}首`} - - - {`by `} - toCreatorDetailPage(creator.userId)} - > - {creator.nickname} - - - - {`收藏:${getLocaleCount(bookCount)}`} - - - {`收听:${getLocaleCount(playCount)}`} - -
        - ) - ) - } - {renderRoutePagination(playlistCount)} -
        - ); - }; - }, + return () => { + const { + playlist: { playlists, playlistCount }, + } = searchData; + return ( +
        + {playlists.map( + ({ + coverImgUrl, + creator, + trackCount, + id, + name, + playCount, + bookCount, + subscribed, + }) => ( +
        toPlaylistDetailPage(id)}> + + {`${name}`} + {`${getLocaleCount(trackCount)}首`} + + {`by `} + toCreatorDetailPage(creator.userId)}> + {creator.nickname} + + + + {`收藏:${getLocaleCount(bookCount)}`} + + {`收听:${getLocaleCount(playCount)}`} +
        + ), + )} + {renderRoutePagination(playlistCount)} +
        + ); + }; + }, }); diff --git a/src/components/search/songs/index.scss b/src/components/search/songs/index.scss index c32c1d9..55fdbe5 100644 --- a/src/components/search/songs/index.scss +++ b/src/components/search/songs/index.scss @@ -1 +1,3 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; diff --git a/src/components/search/songs/index.tsx b/src/components/search/songs/index.tsx index 256af53..62d0116 100644 --- a/src/components/search/songs/index.tsx +++ b/src/components/search/songs/index.tsx @@ -1,49 +1,44 @@ -import { - shallowReactive, - defineComponent, -} from "vue"; -import { - RouteLocationNormalized, - useRoute, - useRouter, -} from "vue-router"; -import { searchCloud } from "@api/search"; -import "./index.scss"; -import MusicList from "@/widgets/music-list"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +/** @format */ + +import { shallowReactive, defineComponent } from 'vue'; +import { RouteLocationNormalized, useRoute, useRouter } from 'vue-router'; +import { searchCloud } from '@api/search'; +import './index.scss'; +import MusicList from '@/widgets/music-list'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export default defineComponent({ - name: "SearchSongs", - setup(props, context) { - const route = useRoute(); + name: 'SearchSongs', + setup(props, context) { + const route = useRoute(); - const songlistData = shallowReactive({ - songCount: 0, - songs: [], - }); + const songlistData = shallowReactive({ + songCount: 0, + songs: [], + }); - const getSearchSongs = async ({ query }: RouteLocationNormalized) => { - const { type, keywords } = query as PlainObject; - const { result } = await searchCloud({ - type, - keywords, - }); - result && - ((songlistData.songCount = result.songCount), - (songlistData.songs = result.songs)); - }; - getSearchSongs(route); + const getSearchSongs = async ({ query }: RouteLocationNormalized) => { + const { type, keywords } = query as PlainObject; + const { result } = await searchCloud({ + type, + keywords, + }); + result && + ((songlistData.songCount = result.songCount), + (songlistData.songs = result.songs)); + }; + getSearchSongs(route); - onFilteredBeforeRouteUpdate((to) => { - getSearchSongs(to); - }); + onFilteredBeforeRouteUpdate((to) => { + getSearchSongs(to); + }); - return () => { - return ( -
        - -
        - ); - }; - }, + return () => { + return ( +
        + +
        + ); + }; + }, }); diff --git a/src/components/search/user/index.scss b/src/components/search/user/index.scss index b646763..4fd33d0 100644 --- a/src/components/search/user/index.scss +++ b/src/components/search/user/index.scss @@ -1,34 +1,36 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .search-user { - &-item { - @include flexVc(); - padding: 12px; - transition: background-color .12s; + &-item { + @include flexVc(); + padding: 12px; + transition: background-color 0.12s; - &:hover { - background-color: rgba(182, 223, 198, 0.1); - } + &:hover { + background-color: rgba(182, 223, 198, 0.1); + } - img { - width: 70px; - height: 70px; - border-radius: 6px; - } + img { + width: 70px; + height: 70px; + border-radius: 6px; + } - .user-name { - flex: 4; - text-indent: 2em; - } + .user-name { + flex: 4; + text-indent: 2em; + } - .user-fans { - flex: 1; - cursor: pointer; - } + .user-fans { + flex: 1; + cursor: pointer; + } - .user-playlist-count { - flex: 1; - cursor: pointer; - } - } + .user-playlist-count { + flex: 1; + cursor: pointer; + } + } } diff --git a/src/components/search/user/index.tsx b/src/components/search/user/index.tsx index d5d938e..ef38aa5 100644 --- a/src/components/search/user/index.tsx +++ b/src/components/search/user/index.tsx @@ -1,90 +1,75 @@ -import { - defineComponent, - markRaw, - inject, - onMounted, - ref, - PropType, -} from "vue"; -import { useRoute, useRouter } from "vue-router"; -import { SearchCloundData } from "../index"; -import "./index.scss"; -import { SearchUserProfileItem } from "@/types/user"; -import { getLocaleCount, padPicCrop } from "@/utils"; +/** @format */ + +import { defineComponent, markRaw, inject, onMounted, ref, PropType } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { SearchCloundData } from '../index'; +import './index.scss'; +import { SearchUserProfileItem } from '@/types/user'; +import { getLocaleCount, padPicCrop } from '@/utils'; export const SearchUserItem = defineComponent({ - name: "SearchUserItem", - props: { - userInfo: { - type: Object as PropType, - required: true, - }, - }, - setup({ userInfo }, context) { - const router = useRouter(); - const route = useRoute(); - const toUserDetailPage = (subPath?: string) => { - router.push({ - path: `/user${subPath ? '/' + subPath : ''}`, - query: { - id: userInfo.userId, - } - }) - } + name: 'SearchUserItem', + props: { + userInfo: { + type: Object as PropType, + required: true, + }, + }, + setup({ userInfo }, context) { + const router = useRouter(); + const route = useRoute(); + const toUserDetailPage = (subPath?: string) => { + router.push({ + path: `/user${subPath ? '/' + subPath : ''}`, + query: { + id: userInfo.userId, + }, + }); + }; - return () => { - const { - nickname, - avatarUrl, - followed, - followeds, - follows, - playlistCount, - mutual, - } = userInfo; - return ( -
        toUserDetailPage()}> - - {nickname} - { - ev.stopPropagation(); - toUserDetailPage('songlist') - }}> - 歌单: - {getLocaleCount(playlistCount)} - - - 粉丝: - {getLocaleCount(followeds)} - -
        - ); - }; - }, + return () => { + const { nickname, avatarUrl, followed, followeds, follows, playlistCount, mutual } = + userInfo; + return ( +
        toUserDetailPage()}> + + {nickname} + { + ev.stopPropagation(); + toUserDetailPage('songlist'); + }} + > + 歌单: + {getLocaleCount(playlistCount)} + + + 粉丝: + {getLocaleCount(followeds)} + +
        + ); + }; + }, }); export default defineComponent({ - name: "SearchUser", - setup(props, context) { - const searchData = inject("searchCloundData") as SearchCloundData; + name: 'SearchUser', + setup(props, context) { + const searchData = inject('searchCloundData') as SearchCloundData; - return () => { - const { - user: { userprofiles, userprofilesCount }, - } = searchData; - return ( -
        - { - userprofiles.map((item) => { - return ; - }) - } -
        - ); - }; - }, + return () => { + const { + user: { userprofiles, userprofilesCount }, + } = searchData; + return ( +
        + {userprofiles.map((item) => { + return ; + })} +
        + ); + }; + }, }); diff --git a/src/components/search/video/index.scss b/src/components/search/video/index.scss index 439371e..fee5591 100644 --- a/src/components/search/video/index.scss +++ b/src/components/search/video/index.scss @@ -1,7 +1,6 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .search-video { - } - - \ No newline at end of file diff --git a/src/components/search/video/index.tsx b/src/components/search/video/index.tsx index 71de4d8..fe9f76f 100644 --- a/src/components/search/video/index.tsx +++ b/src/components/search/video/index.tsx @@ -1,92 +1,90 @@ +/** @format */ + import { - computed, - defineComponent, - markRaw, - onActivated, - onDeactivated, - onMounted, - onUnmounted, - reactive, - ref, - shallowReactive, - toRefs, - watch, - WatchStopHandle, - inject, - watchEffect, -} from "vue"; + computed, + defineComponent, + markRaw, + onActivated, + onDeactivated, + onMounted, + onUnmounted, + reactive, + ref, + shallowReactive, + toRefs, + watch, + WatchStopHandle, + inject, + watchEffect, +} from 'vue'; import { - useRoute, - useRouter, - LocationQuery, - onBeforeRouteUpdate, - onBeforeRouteLeave, - routeLocationKey, - RouteLocationNormalized, -} from "vue-router"; -import { searchCloud } from "@api/search"; -import { getLocaleCount, padPicCrop } from "@utils/index"; -import "./index.scss"; -import RoutePagination, { PagiInfo } from "@widgets/route-pagination"; -import { SearchCloundData } from "../index"; + useRoute, + useRouter, + LocationQuery, + onBeforeRouteUpdate, + onBeforeRouteLeave, + routeLocationKey, + RouteLocationNormalized, +} from 'vue-router'; +import { searchCloud } from '@api/search'; +import { getLocaleCount, padPicCrop } from '@utils/index'; +import './index.scss'; +import RoutePagination, { PagiInfo } from '@widgets/route-pagination'; +import { SearchCloundData } from '../index'; import VideoList from '@widgets/video-list'; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; const defaultLimie = PAGE_SIZE[COMPONENT_NAME.SEARCH_VIDEO]; const defaultSearchVideoInfo = { - offset: 0, - limit: defaultLimie, - sizeArr: Array(3) - .fill(0) - .map((v, i) => defaultLimie * (i + 1)), + offset: 0, + limit: defaultLimie, + sizeArr: Array(3) + .fill(0) + .map((v, i) => defaultLimie * (i + 1)), }; export default defineComponent({ - name: COMPONENT_NAME.SEARCH_VIDEO, - setup(props, context) { - const route = useRoute(); - const { - sizeArr, - limit: dftLimit, - offset: dftOffset, - } = defaultSearchVideoInfo; - const videoPagiConf = reactive({ - sizeArr, - total: 0, - limit: dftLimit, - offset: dftOffset, - }); - const searchData = inject("searchCloundData")!; + name: COMPONENT_NAME.SEARCH_VIDEO, + setup(props, context) { + const route = useRoute(); + const { sizeArr, limit: dftLimit, offset: dftOffset } = defaultSearchVideoInfo; + const videoPagiConf = reactive({ + sizeArr, + total: 0, + limit: dftLimit, + offset: dftOffset, + }); + const searchData = inject('searchCloundData')!; + + watchEffect(() => { + videoPagiConf.total = searchData.video.videoCount; + }); - watchEffect(() => { - videoPagiConf.total = searchData.video.videoCount; - }); - - const routeUpdateHandler = async ({ query }: RouteLocationNormalized) => { - const { limit, offset } = query as PlainObject; - videoPagiConf.limit = limit; - videoPagiConf.offset = offset; - } - routeUpdateHandler(route); + const routeUpdateHandler = async ({ query }: RouteLocationNormalized) => { + const { limit, offset } = query as PlainObject; + videoPagiConf.limit = limit; + videoPagiConf.offset = offset; + }; + routeUpdateHandler(route); - onFilteredBeforeRouteUpdate((to) => { - routeUpdateHandler(to); - }); + onFilteredBeforeRouteUpdate((to) => { + routeUpdateHandler(to); + }); - return () => { - const { - video: { videos, videoCount }, - } = searchData; - return ( - <> -
        - -
        - - - ); - }; - }, + return () => { + const { + video: { videos, videoCount }, + } = searchData; + return ( + <> +
        + +
        + + + ); + }; + }, }); diff --git a/src/components/setting/general/index.scss b/src/components/setting/general/index.scss index f37467a..b781a85 100644 --- a/src/components/setting/general/index.scss +++ b/src/components/setting/general/index.scss @@ -1,9 +1,9 @@ -@import "@scss/variable"; +/** @format */ -.setting-general { - - .theme-picker { - width: 120px; - } +@import '@scss/variable'; -} \ No newline at end of file +.setting-general { + .theme-picker { + width: 120px; + } +} diff --git a/src/components/setting/general/index.tsx b/src/components/setting/general/index.tsx index 736b844..8e74534 100644 --- a/src/components/setting/general/index.tsx +++ b/src/components/setting/general/index.tsx @@ -1,104 +1,109 @@ -import { NColorPicker } from "naive-ui"; +/** @format */ + +import { NColorPicker } from 'naive-ui'; import { - defineComponent, - markRaw, - onMounted, - reactive, - readonly, - ref, - nextTick, - h, - shallowReactive, -} from "vue"; -import "./index.scss"; -import { defaultPlayerPreferences, theme } from "@stores/player"; -import { PreferenceNames } from "@/utils/preference"; -import { deepCopy, NOOP } from "@/utils"; -import { ColorPickerMode } from "naive-ui/lib/color-picker/src/utils"; -import { useEventListener } from "@vueuse/core"; + defineComponent, + markRaw, + onMounted, + reactive, + readonly, + ref, + nextTick, + h, + shallowReactive, +} from 'vue'; +import './index.scss'; +import { defaultPlayerPreferences, theme } from '@stores/player'; +import { PreferenceNames } from '@/utils/preference'; +import { deepCopy, NOOP } from '@/utils'; +import { ColorPickerMode } from 'naive-ui/lib/color-picker/src/utils'; +import { useEventListener } from '@vueuse/core'; const themeConfig = { - value: theme.value, - modes: ['rgb', 'hex', 'hsl', 'hsv'] as ColorPickerMode[], - options: [ - defaultPlayerPreferences[PreferenceNames.theme], - '#18A058', '#2080F0', '#F0A020', 'rgba(208, 48, 80, 1)' - ] -} + value: theme.value, + modes: ['rgb', 'hex', 'hsl', 'hsv'] as ColorPickerMode[], + options: [ + defaultPlayerPreferences[PreferenceNames.theme], + '#18A058', + '#2080F0', + '#F0A020', + 'rgba(208, 48, 80, 1)', + ], +}; const ThemeSetting = defineComponent({ - setup(props, context) { - - const themeConf = shallowReactive(deepCopy(themeConfig)); + setup(props, context) { + const themeConf = shallowReactive(deepCopy(themeConfig)); - const updateTheme = (color: string) => { - themeConf.value = color; - } - let isClickFromColorPickerConfirm = false; - const handleThemePickerConfirm = () => { - theme.value = themeConf.value; - isClickFromColorPickerConfirm = true; - }; - let colorPickClickListenerRemover: CommonFunction = NOOP; + const updateTheme = (color: string) => { + themeConf.value = color; + }; + let isClickFromColorPickerConfirm = false; + const handleThemePickerConfirm = () => { + theme.value = themeConf.value; + isClickFromColorPickerConfirm = true; + }; + let colorPickClickListenerRemover: CommonFunction = NOOP; - const themeColorPickShowToggle = (isShow: boolean) => { - if (!isShow) { - colorPickClickListenerRemover(); - if (isClickFromColorPickerConfirm) { - isClickFromColorPickerConfirm = false; - } else { - themeConf.value = theme.value; - } - } else { - setTimeout(() => { - colorPickClickListenerRemover = useEventListener(document.querySelector('.n-color-picker-panel .n-color-picker-action button'), 'mouseup', handleThemePickerConfirm); - }); - } - } + const themeColorPickShowToggle = (isShow: boolean) => { + if (!isShow) { + colorPickClickListenerRemover(); + if (isClickFromColorPickerConfirm) { + isClickFromColorPickerConfirm = false; + } else { + themeConf.value = theme.value; + } + } else { + setTimeout(() => { + colorPickClickListenerRemover = useEventListener( + document.querySelector('.n-color-picker-panel .n-color-picker-action button'), + 'mouseup', + handleThemePickerConfirm, + ); + }); + } + }; - return () => { - return ( -
        -
        -

        - 外观 -

        -
        -
          -
        • - 主题色 -
          -
          - -
          -
          -
        • -
        -
        - ) - } - } -}) + return () => { + return ( +
        +
        +

        外观

        +
        +
          +
        • + 主题色 +
          +
          + +
          +
          +
        • +
        +
        + ); + }; + }, +}); export default defineComponent({ - name: "Setting", - setup(props, context) { - - return () => { - return ( -
        - -
        - ); - }; - }, + name: 'Setting', + setup(props, context) { + return () => { + return ( +
        + +
        + ); + }; + }, }); diff --git a/src/components/setting/index.scss b/src/components/setting/index.scss index 07c3233..a33858e 100644 --- a/src/components/setting/index.scss +++ b/src/components/setting/index.scss @@ -1,51 +1,49 @@ -@import "@scss/variable"; +/** @format */ -.yplayer-setting-page { - - .setting-header { - - .setting-title { - padding: 1rem; - background-color: $listBgc; - font-weight: 600; - font-size: 30px; - } - } - - $hrBgc: #e9e9e9; - - .setting-block { - width: 60%; - .block-header { - @include flexVc(); - padding-top: 32px; - padding-bottom: 18px; - border-bottom: 1px solid $hrBgc; +@import '@scss/variable'; - .setting-title { - color: $colorSettingTitle; - font-size: 20px; - font-weight: 600; - } - } - - .setting-list { - li { - @include flexVc(); - padding: 2rem 0; - border-bottom: 1px solid $hrBgc; - em.setting-name { - margin-right: 2rem; - color: $colorSettingName; - font-weight: 500; - } - - .setting-content { - flex: 1; - } - } - } - - } - -} \ No newline at end of file +.yplayer-setting-page { + .setting-header { + .setting-title { + padding: 1rem; + background-color: $listBgc; + font-weight: 600; + font-size: 30px; + } + } + + $hrBgc: #e9e9e9; + + .setting-block { + width: 60%; + .block-header { + @include flexVc(); + padding-top: 32px; + padding-bottom: 18px; + border-bottom: 1px solid $hrBgc; + + .setting-title { + color: $colorSettingTitle; + font-size: 20px; + font-weight: 600; + } + } + + .setting-list { + li { + @include flexVc(); + padding: 2rem 0; + border-bottom: 1px solid $hrBgc; + em.setting-name { + margin-right: 2rem; + color: $colorSettingName; + font-weight: 500; + } + + .setting-content { + flex: 1; + } + } + } + } +} diff --git a/src/components/setting/index.tsx b/src/components/setting/index.tsx index 792a568..a107301 100644 --- a/src/components/setting/index.tsx +++ b/src/components/setting/index.tsx @@ -1,41 +1,36 @@ +/** @format */ + import { - defineComponent, - markRaw, - onMounted, - reactive, - readonly, - ref, - shallowReactive, -} from "vue"; -import "./index.scss"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import CommonRouterList from "@/widgets/common-router-list"; -import { deepCopy } from "@/utils"; + defineComponent, + markRaw, + onMounted, + reactive, + readonly, + ref, + shallowReactive, +} from 'vue'; +import './index.scss'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import CommonRouterList from '@/widgets/common-router-list'; +import { deepCopy } from '@/utils'; -export const settingRoutes = [ - { text: "常规设置", to: "/setting/general", }, -]; +export const settingRoutes = [{ text: '常规设置', to: '/setting/general' }]; export default defineComponent({ - name: "Setting", - setup(props, context) { - - const settingRouteData = shallowReactive(deepCopy(settingRoutes)); + name: 'Setting', + setup(props, context) { + const settingRouteData = shallowReactive(deepCopy(settingRoutes)); - return () => { - return ( -
        -
        -

        - 设置 -

        - -
        - { - renderKeepAliveRouterView() - } -
        - ); - }; - }, + return () => { + return ( +
        +
        +

        设置

        + +
        + {renderKeepAliveRouterView()} +
        + ); + }; + }, }); diff --git a/src/components/song-page/comment/index.scss b/src/components/song-page/comment/index.scss index db88a30..4bd9142 100644 --- a/src/components/song-page/comment/index.scss +++ b/src/components/song-page/comment/index.scss @@ -1,105 +1,102 @@ -@import "@scss/variable"; +/** @format */ -.comment-category-layer { +@import '@scss/variable'; - padding-bottom: 30px; - - $commonColor: rgba(0, 0, 0, 0.48); - - .category-headline { - padding-bottom: 12px; - color: $colorSettingName; - font-size: 1.2rem; - font-weight: 500; - } - - .song-comment-item { - @include flex(); - padding: 20px 0; - - .user-icon { - height: 40px; - overflow: hidden; - border-radius: 100%; - margin-right: 20px; - } - - .comment-main { - flex: 1; - - .comment-header { - @include flexHevV(); - height: 40px; - margin-bottom: 20px; - color: $commonColor; - font-size: 12px; - } - - .comment-content { - margin-bottom: 20px; - color: $colorSettingTitle; - font-size: 12px; - line-height: 1.8; - } - - &:not(:last-of-type) { - border-bottom: 1px solid rgba(70, 70, 70, 0.1); - } - - &:hover { - .comment-operation .span-item.report-span { - opacity: 1; - } - } - - .comment-operation { - @include flexVc(); - - .span-item { - margin-right: 20px; - cursor: pointer; - - em { - font-size: 12px; - } - - &.report-span { - opacity: 0; - } - - &.report-span, - &.reply-span { - em { - color: $commonColor; - } - } - - &:hover, - &.liked { - i.iconfont{ - color: var(--theme); - } - em { - color: var(--theme); - } - } - } - } - - .comment-like-span { - .comment-like { - margin-right: 2px; - color: $colorSettingName; - font-size: 22px; - } - em { - color: $colorSettingName; - } - } - - - } - - } - -} \ No newline at end of file +.comment-category-layer { + padding-bottom: 30px; + + $commonColor: rgba(0, 0, 0, 0.48); + + .category-headline { + padding-bottom: 12px; + color: $colorSettingName; + font-size: 1.2rem; + font-weight: 500; + } + + .song-comment-item { + @include flex(); + padding: 20px 0; + + .user-icon { + height: 40px; + overflow: hidden; + border-radius: 100%; + margin-right: 20px; + } + + .comment-main { + flex: 1; + + .comment-header { + @include flexHevV(); + height: 40px; + margin-bottom: 20px; + color: $commonColor; + font-size: 12px; + } + + .comment-content { + margin-bottom: 20px; + color: $colorSettingTitle; + font-size: 12px; + line-height: 1.8; + } + + &:not(:last-of-type) { + border-bottom: 1px solid rgba(70, 70, 70, 0.1); + } + + &:hover { + .comment-operation .span-item.report-span { + opacity: 1; + } + } + + .comment-operation { + @include flexVc(); + + .span-item { + margin-right: 20px; + cursor: pointer; + + em { + font-size: 12px; + } + + &.report-span { + opacity: 0; + } + + &.report-span, + &.reply-span { + em { + color: $commonColor; + } + } + + &:hover, + &.liked { + i.iconfont { + color: var(--theme); + } + em { + color: var(--theme); + } + } + } + } + + .comment-like-span { + .comment-like { + margin-right: 2px; + color: $colorSettingName; + font-size: 22px; + } + em { + color: $colorSettingName; + } + } + } + } +} diff --git a/src/components/song-page/comment/index.tsx b/src/components/song-page/comment/index.tsx index 5a456c0..dda3c68 100644 --- a/src/components/song-page/comment/index.tsx +++ b/src/components/song-page/comment/index.tsx @@ -1,129 +1,143 @@ -import { musicCommentLike } from "@/api/music"; -import { CommentType } from "@/dependency/enum"; -import { SongComment, SongCommentItem } from "@/types/song"; -import { getLocaleDate, padPicCrop, spliceTime2Second, UNICODE_CHAR } from "@/utils"; -import { messageBus } from "@/utils/event/register"; -import { defineComponent, inject, PropType, Ref } from "vue"; -import { useRouter } from "vue-router"; +/** @format */ + +import { musicCommentLike } from '@/api/music'; +import { CommentType } from '@/dependency/enum'; +import { SongComment, SongCommentItem } from '@/types/song'; +import { getLocaleDate, padPicCrop, spliceTime2Second, UNICODE_CHAR } from '@/utils'; +import { messageBus } from '@/utils/event/register'; +import { defineComponent, inject, PropType, Ref } from 'vue'; +import { useRouter } from 'vue-router'; import './index.scss'; const CommentItemComp = defineComponent({ - name: "CommentItemComp", - props: { - commentItem: { - type: Object as PropType, - required: true - }, - sourceId: { - type: [Number, String] as PropType, - required: true, - }, - commentType: { - type: Number as PropType, - required: true - } - }, - setup(props) { - - const commentLikeHandler = async () => { - const { commentItem: { commentId, liked }, sourceId, commentType } = props; - const unOrLike = liked ? 0 : 1; - const isSuccess = await musicCommentLike({ - cid: commentId, - id: sourceId, - type: commentType as CommentType, - t: unOrLike - }); - if(isSuccess) { - props.commentItem.liked = !!unOrLike; - props.commentItem.likedCount += !!unOrLike ? 1 : -1; - messageBus.dispatch('successMessage', `${unOrLike ? '点赞成功!!' : '取消点赞成功~~'}${UNICODE_CHAR.smile}`) - } - } - - const renderCommentLike = () => { - const { liked, likedCount } = props.commentItem; - const likedClass = liked ? 'liked' : ''; - return ( - - - {likedCount} - - ) - } + name: 'CommentItemComp', + props: { + commentItem: { + type: Object as PropType, + required: true, + }, + sourceId: { + type: [Number, String] as PropType, + required: true, + }, + commentType: { + type: Number as PropType, + required: true, + }, + }, + setup(props) { + const commentLikeHandler = async () => { + const { + commentItem: { commentId, liked }, + sourceId, + commentType, + } = props; + const unOrLike = liked ? 0 : 1; + const isSuccess = await musicCommentLike({ + cid: commentId, + id: sourceId, + type: commentType as CommentType, + t: unOrLike, + }); + if (isSuccess) { + props.commentItem.liked = !!unOrLike; + props.commentItem.likedCount += !!unOrLike ? 1 : -1; + messageBus.dispatch( + 'successMessage', + `${unOrLike ? '点赞成功!!' : '取消点赞成功~~'}${UNICODE_CHAR.smile}`, + ); + } + }; + const renderCommentLike = () => { + const { liked, likedCount } = props.commentItem; + const likedClass = liked ? 'liked' : ''; + return ( + + + {likedCount} + + ); + }; - return () => { - const { commentItem: { user, time, content } } = props; - const userImgUrl = padPicCrop(user.avatarUrl, { - x: 44, - y: 44 - }); - return ( -
        -
        - - {userImgUrl} - -
        -
        -
        - {user.nickname} -
        {getLocaleDate(time)}
        -
        -
        - {content} -
        -
        - {renderCommentLike()} - - 回复 - - - 举报 - -
        - -
        -
        - ) - - } - - } -}) + return () => { + const { + commentItem: { user, time, content }, + } = props; + const userImgUrl = padPicCrop(user.avatarUrl, { + x: 44, + y: 44, + }); + return ( +
        +
        + + {userImgUrl} + +
        +
        +
        + {user.nickname} +
        {getLocaleDate(time)}
        +
        +
        {content}
        +
        + {renderCommentLike()} + + 回复 + + + 举报 + +
        +
        +
        + ); + }; + }, +}); export default defineComponent({ - name: "SongComment", - setup() { - - const router = useRouter(); - const commentData = inject>('commentData')!; + name: 'SongComment', + setup() { + const router = useRouter(); + const commentData = inject>('commentData')!; - return () => { - const { hotComments, comments } = commentData.value; - const musicId = router.currentRoute.value.params.id as string; + return () => { + const { hotComments, comments } = commentData.value; + const musicId = router.currentRoute.value.params.id as string; - return ( -
        -
        -

        精彩评论

        - { - hotComments.map((data) => { - return - }) - } -
        -
        -

        全部评论

        - { - comments.map((data) => { - return - }) - } -
        -
        - ) - } - } -}) \ No newline at end of file + return ( +
        +
        +

        精彩评论

        + {hotComments.map((data) => { + return ( + + ); + })} +
        +
        +

        全部评论

        + {comments.map((data) => { + return ( + + ); + })} +
        +
        + ); + }; + }, +}); diff --git a/src/components/song-page/detail/index.scss b/src/components/song-page/detail/index.scss index b4c406b..1af5fbb 100644 --- a/src/components/song-page/detail/index.scss +++ b/src/components/song-page/detail/index.scss @@ -1,20 +1,19 @@ -@import "@scss/variable"; +/** @format */ -.song-comment-container { - - .content-layer { - @include flexVc(); - padding-top: 20px; - color: $colorSettingName; +@import '@scss/variable'; - h6 { - min-width: 5em; - } +.song-comment-container { + .content-layer { + @include flexVc(); + padding-top: 20px; + color: $colorSettingName; - mark { - text-indent: 2rem; - } + h6 { + min-width: 5em; + } - } - -} \ No newline at end of file + mark { + text-indent: 2rem; + } + } +} diff --git a/src/components/song-page/detail/index.tsx b/src/components/song-page/detail/index.tsx index 2fc7d40..f524c19 100644 --- a/src/components/song-page/detail/index.tsx +++ b/src/components/song-page/detail/index.tsx @@ -1,33 +1,32 @@ -import { SongInfo } from "@/types/song"; -import { defineComponent, inject, PropType, Ref } from "vue"; -import { useRouter } from "vue-router"; -import { getSongDetailList } from "../util"; +/** @format */ + +import { SongInfo } from '@/types/song'; +import { defineComponent, inject, PropType, Ref } from 'vue'; +import { useRouter } from 'vue-router'; +import { getSongDetailList } from '../util'; import './index.scss'; export default defineComponent({ - name: "SongDetail", - setup() { - - const router = useRouter(); - const songDetail = inject>('songDetail')!; + name: 'SongDetail', + setup() { + const router = useRouter(); + const songDetail = inject>('songDetail')!; - return () => { - const list = getSongDetailList(songDetail.value); + return () => { + const list = getSongDetailList(songDetail.value); - return ( -
        - { - list.map(({attr, value}) => { - return ( -
        -
        {attr}:
        - {value} -
        - ) - }) - } -
        - ) - } - } -}) \ No newline at end of file + return ( +
        + {list.map(({ attr, value }) => { + return ( +
        +
        {attr}:
        + {value} +
        + ); + })} +
        + ); + }; + }, +}); diff --git a/src/components/song-page/index.scss b/src/components/song-page/index.scss index e141842..22c4525 100644 --- a/src/components/song-page/index.scss +++ b/src/components/song-page/index.scss @@ -1,38 +1,37 @@ -@import "@scss/variable"; +/** @format */ -#song-detail-page { - padding: 0 10px; - - .song-detail-header { - @include flexVc(); - padding: 10px 0 20px 0; - - .song-pic { - width: 120px; - border-radius: 12px; - overflow: hidden; - } - - .song-info { - padding-left: 20px; - - h2 { - padding-bottom: 2rem; - font-size: 2.2rem; - } +@import '@scss/variable'; - > ul { - @include flexVc(); - color: $colorSettingName; - font-size: 14px; - - li { - margin-right: 20px; - } - } - - } - - } - -} \ No newline at end of file +#song-detail-page { + padding: 0 10px; + + .song-detail-header { + @include flexVc(); + padding: 10px 0 20px 0; + + .song-pic { + width: 120px; + border-radius: 12px; + overflow: hidden; + } + + .song-info { + padding-left: 20px; + + h2 { + padding-bottom: 2rem; + font-size: 2.2rem; + } + + > ul { + @include flexVc(); + color: $colorSettingName; + font-size: 14px; + + li { + margin-right: 20px; + } + } + } + } +} diff --git a/src/components/song-page/index.tsx b/src/components/song-page/index.tsx index ec4c18e..b682a92 100644 --- a/src/components/song-page/index.tsx +++ b/src/components/song-page/index.tsx @@ -1,159 +1,157 @@ -import { getMusicComment, getMusicDetail } from "@/api/music"; -import { OriginCoverType, SongFee } from "@/dependency/enum"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; -import { SongComment, SongInfo } from "@/types/song"; -import { objToPathname, padPicCrop } from "@/utils"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import CommonRouterList from "@/widgets/common-router-list"; -import { defineComponent, provide, reactive, ref, shallowReactive } from "vue"; -import { RouteParams, useRouter } from "vue-router"; +/** @format */ + +import { getMusicComment, getMusicDetail } from '@/api/music'; +import { OriginCoverType, SongFee } from '@/dependency/enum'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; +import { SongComment, SongInfo } from '@/types/song'; +import { objToPathname, padPicCrop } from '@/utils'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import CommonRouterList from '@/widgets/common-router-list'; +import { defineComponent, provide, reactive, ref, shallowReactive } from 'vue'; +import { RouteParams, useRouter } from 'vue-router'; import './index.scss'; -import { getSongDetailList } from "./util"; +import { getSongDetailList } from './util'; -import ReplyTextarea from "@widgets/reply-textarea"; +import ReplyTextarea from '@widgets/reply-textarea'; const songPageTemplateRouteList = [ - { - to: '/song/:id/comment', - text: '评论' - }, - { - to: '/song/:id/detail', - text: '详情' - } -] + { + to: '/song/:id/comment', + text: '评论', + }, + { + to: '/song/:id/detail', + text: '详情', + }, +]; export default defineComponent({ - name: "SongPage", - setup() { - - const router = useRouter(); - const songPageRouteList = shallowReactive([]); - - const songCommentDataRef = ref({ - total: 0, - moreHot: false, - more: false, - userId: 0, - isMusician: false, - commentBanner: null, - comments: [], - hotComments: [], - topComments: [] - }); - provide('commentData', songCommentDataRef); - - const songDetailDataRef = ref({ - id: 0, - name: '', - alia: [], - dt: 0, - pop: 0, - ar: [], - al: { - id: 0, - name: '', - picUrl: '' - }, - publishTime: 0, - fee: SongFee.freeOrNoCopyright, - h: Object.create(null), - m: Object.create(null), - l: Object.create(null), - mark: 0, - originCoverType: OriginCoverType.unkown, - privilege: { - id: 0, - chargeInfoList: [] - }, - }); - provide('songDetail', songDetailDataRef); + name: 'SongPage', + setup() { + const router = useRouter(); + const songPageRouteList = shallowReactive([]); - const syncRelativeData = () => { - updatePageRouteList(); - getCommentDate(); - getSongDetail(); - } + const songCommentDataRef = ref({ + total: 0, + moreHot: false, + more: false, + userId: 0, + isMusician: false, + commentBanner: null, + comments: [], + hotComments: [], + topComments: [], + }); + provide('commentData', songCommentDataRef); - const getSongDetail = async () => { - const detailData = await getMusicDetail({ - ids: router.currentRoute.value.params.id - }); - songDetailDataRef.value = { - ...detailData.songs[0], - privilege: detailData.privileges[0], - } - } + const songDetailDataRef = ref({ + id: 0, + name: '', + alia: [], + dt: 0, + pop: 0, + ar: [], + al: { + id: 0, + name: '', + picUrl: '', + }, + publishTime: 0, + fee: SongFee.freeOrNoCopyright, + h: Object.create(null), + m: Object.create(null), + l: Object.create(null), + mark: 0, + originCoverType: OriginCoverType.unkown, + privilege: { + id: 0, + chargeInfoList: [], + }, + }); + provide('songDetail', songDetailDataRef); - const getCommentDate = async () => { - const { params } = router.currentRoute.value; - const data = await getMusicComment({ - id: params.id as string, - }); - songCommentDataRef.value = data; - } + const syncRelativeData = () => { + updatePageRouteList(); + getCommentDate(); + getSongDetail(); + }; - /** - * 更新RouteList路由导航数据 - */ - const updatePageRouteList = () => { - const { params } = router.currentRoute.value; - songPageTemplateRouteList.forEach(({ text, to }, i) => { - songPageRouteList[i] = { - text, - to: to.replace(':id', objToPathname(params, false)), - }; - }); - } + const getSongDetail = async () => { + const detailData = await getMusicDetail({ + ids: router.currentRoute.value.params.id, + }); + songDetailDataRef.value = { + ...detailData.songs[0], + privilege: detailData.privileges[0], + }; + }; - syncRelativeData(); - onFilteredBeforeRouteUpdate((to, from) => { - const { id: toId } = to.params; - const { id: fromId } = from.params; - if (toId != fromId) { - syncRelativeData(); - } - }); + const getCommentDate = async () => { + const { params } = router.currentRoute.value; + const data = await getMusicComment({ + id: params.id as string, + }); + songCommentDataRef.value = data; + }; - return () => { + /** + * 更新RouteList路由导航数据 + */ + const updatePageRouteList = () => { + const { params } = router.currentRoute.value; + songPageTemplateRouteList.forEach(({ text, to }, i) => { + songPageRouteList[i] = { + text, + to: to.replace(':id', objToPathname(params, false)), + }; + }); + }; - const { al: { picUrl }, name } = songDetailDataRef.value; - const songInfoList = getSongDetailList(songDetailDataRef.value).slice(0, 2); + syncRelativeData(); + onFilteredBeforeRouteUpdate((to, from) => { + const { id: toId } = to.params; + const { id: fromId } = from.params; + if (toId != fromId) { + syncRelativeData(); + } + }); - return ( -
        + return () => { + const { + al: { picUrl }, + name, + } = songDetailDataRef.value; + const songInfoList = getSongDetailList(songDetailDataRef.value).slice(0, 2); -
        -
        -
        - -
        -
        -
        -

        {name}

        -
          - { - songInfoList.map(({ attr, value }) => { - return ( -
        • - {attr}: - {value} -
        • - ) - }) - } -
        -
        -
        -
        - -
        - - { - renderKeepAliveRouterView() - } -
        - ) - } - } -}) \ No newline at end of file + return ( +
        +
        +
        +
        + +
        +
        +
        +

        {name}

        +
          + {songInfoList.map(({ attr, value }) => { + return ( +
        • + {attr}: + {value} +
        • + ); + })} +
        +
        +
        +
        + +
        + + {renderKeepAliveRouterView()} +
        + ); + }; + }, +}); diff --git a/src/components/song-page/util.ts b/src/components/song-page/util.ts index ca22eac..4f6aaca 100644 --- a/src/components/song-page/util.ts +++ b/src/components/song-page/util.ts @@ -1,22 +1,40 @@ -import { SongInfo } from "@/types/song"; -import { getLocaleDate, second2TimeStr } from "@/utils"; +/** @format */ -export const getSongDetailList = ({ al: { name: albumName }, ar, dt, publishTime, pop }: SongInfo) => { - return [ - { - attr: '歌者', value: ar.map(({ name, alias }) => `${name}${alias?.length ? '(' + alias.join('、') + ')' : ''}`).join(' / '), - }, - { - attr: '专辑', value: `《 ${albumName} 》`, - }, - { - attr: '时长', value: second2TimeStr(dt / 1000), - }, - { - attr: '发行时间', value: getLocaleDate(publishTime), - }, - { - attr: '热度', value: pop - } - ]; -} \ No newline at end of file +import { SongInfo } from '@/types/song'; +import { getLocaleDate, second2TimeStr } from '@/utils'; + +export const getSongDetailList = ({ + al: { name: albumName }, + ar, + dt, + publishTime, + pop, +}: SongInfo) => { + return [ + { + attr: '歌者', + value: ar + .map( + ({ name, alias }) => + `${name}${alias?.length ? '(' + alias.join('、') + ')' : ''}`, + ) + .join(' / '), + }, + { + attr: '专辑', + value: `《 ${albumName} 》`, + }, + { + attr: '时长', + value: second2TimeStr(dt / 1000), + }, + { + attr: '发行时间', + value: getLocaleDate(publishTime), + }, + { + attr: '热度', + value: pop, + }, + ]; +}; diff --git a/src/components/songlist/comment/index.scss b/src/components/songlist/comment/index.scss index ed09936..e63b877 100644 --- a/src/components/songlist/comment/index.scss +++ b/src/components/songlist/comment/index.scss @@ -1 +1,3 @@ -@import "@/scss/variable"; \ No newline at end of file +/** @format */ + +@import '@/scss/variable'; diff --git a/src/components/songlist/comment/index.tsx b/src/components/songlist/comment/index.tsx index ceca7f7..a30aeac 100644 --- a/src/components/songlist/comment/index.tsx +++ b/src/components/songlist/comment/index.tsx @@ -1,19 +1,21 @@ -import { defineComponent } from "vue"; +/** @format */ + +import { defineComponent } from 'vue'; import { - useRouter, - useRoute, - RouteLocationNormalized, - onBeforeRouteUpdate, -} from "vue-router"; -import "./index.scss"; + useRouter, + useRoute, + RouteLocationNormalized, + onBeforeRouteUpdate, +} from 'vue-router'; +import './index.scss'; export default defineComponent({ - name: "songlistComment", - setup(props, context) { - const route = useRoute(); + name: 'songlistComment', + setup(props, context) { + const route = useRoute(); - return () => { - return
        评论
        ; - }; - }, + return () => { + return
        评论
        ; + }; + }, }); diff --git a/src/components/songlist/index.scss b/src/components/songlist/index.scss index 2f58c25..8eb05ac 100644 --- a/src/components/songlist/index.scss +++ b/src/components/songlist/index.scss @@ -1,113 +1,112 @@ -@import "@/scss/variable"; +/** @format */ -.songlist-page { - - .songlist-relative-playlist { - padding-bottom: 40px; - .relative-title { - padding: 50px 0 20px 0; - color: #555; - font-size: 26px; - font-weight: 600; - } - } - - .songlist-header { - position: relative; - @include flexVc(); - padding-bottom: 2em; - - &-playbill { - flex: 1; - padding-right: 32px; - .songlist-pic { - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 8px; - } - } - - &-body { - flex: 5; - display: flow-root; - } - - .songlist-info { - float: left; - - h5 { - padding-bottom: 1em; - color: #444; - font-size: 26px; - font-weight: 600; - } - - .songlist-creator { - @include flexVc(); - - &-detail { - @include flexVc(); - cursor: pointer; - - em { - color: #566; - font-size: 1.1em; - text-indent: 1em; - } - } - - &-avatar { - position: relative; - - .detail-avatar { - position: absolute; - right: 0; - bottom: 0; - width: 14px; - } - } - - .play-count { - margin-left: 2em; - cursor: default; - } - } - - .desc { - @include flexVc(); - padding: 1em 0; - color: #556; - line-height: 1.6; - - em { - display: inline-block; - max-width: 40em; - } - } - - .relative-data { - line-height: 1.8; - color: #766; - } - } - - .songlist-tools { - float: right; - @include flexVc(); - margin-top: 10px; - - .songlist-btn { - &:not(:last-of-type) { - margin-right: 1em; - } - } - } - } - - .songlist-main { - min-height: 320px; - } +@import '@/scss/variable'; +.songlist-page { + .songlist-relative-playlist { + padding-bottom: 40px; + .relative-title { + padding: 50px 0 20px 0; + color: #555; + font-size: 26px; + font-weight: 600; + } + } + + .songlist-header { + position: relative; + @include flexVc(); + padding-bottom: 2em; + + &-playbill { + flex: 1; + padding-right: 32px; + .songlist-pic { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 8px; + } + } + + &-body { + flex: 5; + display: flow-root; + } + + .songlist-info { + float: left; + + h5 { + padding-bottom: 1em; + color: #444; + font-size: 26px; + font-weight: 600; + } + + .songlist-creator { + @include flexVc(); + + &-detail { + @include flexVc(); + cursor: pointer; + + em { + color: #566; + font-size: 1.1em; + text-indent: 1em; + } + } + + &-avatar { + position: relative; + + .detail-avatar { + position: absolute; + right: 0; + bottom: 0; + width: 14px; + } + } + + .play-count { + margin-left: 2em; + cursor: default; + } + } + + .desc { + @include flexVc(); + padding: 1em 0; + color: #556; + line-height: 1.6; + + em { + display: inline-block; + max-width: 40em; + } + } + + .relative-data { + line-height: 1.8; + color: #766; + } + } + + .songlist-tools { + float: right; + @include flexVc(); + margin-top: 10px; + + .songlist-btn { + &:not(:last-of-type) { + margin-right: 1em; + } + } + } + } + + .songlist-main { + min-height: 320px; + } } - \ No newline at end of file diff --git a/src/components/songlist/index.tsx b/src/components/songlist/index.tsx index ee61af1..5496f7e 100644 --- a/src/components/songlist/index.tsx +++ b/src/components/songlist/index.tsx @@ -1,302 +1,273 @@ +/** @format */ + import { - shallowReactive, - computed, - toRefs, - onActivated, - provide, - reactive, - readonly, - ref, - defineComponent, - h, -} from "vue"; + shallowReactive, + computed, + toRefs, + onActivated, + provide, + reactive, + readonly, + ref, + defineComponent, + h, +} from 'vue'; import { - useRouter, - useRoute, - RouteLocationNormalized, - onBeforeRouteUpdate, - RouterView, -} from "vue-router"; -import { playlistDetail, playlistDetailDynamic, playlistSimilar, relatedPlaylist } from "@api/playlist"; -import { getLocaleDate, objToPathname, objToQuery, padPicCrop, UNICODE_CHAR } from "@utils/index"; -import { getLocaleCount } from "@utils/calc"; -import "./index.scss"; - + useRouter, + useRoute, + RouteLocationNormalized, + onBeforeRouteUpdate, + RouterView, +} from 'vue-router'; +import { + playlistDetail, + playlistDetailDynamic, + playlistSimilar, + relatedPlaylist, +} from '@api/playlist'; import { - NAvatar, - NxButton, - NMenu -} from 'naive-ui'; + getLocaleDate, + objToPathname, + objToQuery, + padPicCrop, + UNICODE_CHAR, +} from '@utils/index'; +import { getLocaleCount } from '@utils/calc'; +import './index.scss'; + +import { NAvatar, NxButton, NMenu } from 'naive-ui'; import Songlist from '@widgets/song-list'; -import CommonRouterList from "@/widgets/common-router-list"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import CommonRouterList from '@/widgets/common-router-list'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; const baseSonglistRoutelists = [ - { text: "歌曲列表", to: "/songlist/:id/music", }, - { text: "评论", to: "/songlist/:id/comments" }, - { text: "订阅者", to: "/songlist/:id/subscribers" }, + { text: '歌曲列表', to: '/songlist/:id/music' }, + { text: '评论', to: '/songlist/:id/comments' }, + { text: '订阅者', to: '/songlist/:id/subscribers' }, ]; const subscribedNumber = 99; export default defineComponent({ - name: "songlist", - setup(props, context) { - const route = useRoute(); - const router = useRouter(); - - const songlistRoutelists = reactive([]); - - const songlistInfo = shallowReactive({ - playlist: { - name: "", - creator: { - avatarDetail: { - identityIconUrl: "", - }, - detailDescription: "", - nickname: "", - avatarUrl: "", - userId: "", - }, - coverImgUrl: "", - description: "", - subscribers: [], - tracks: [], - playCountStr: "", - createTimeStr: "", - subscribedCountStr: "", - updateTimeStr: "", - shareCountStr: "", - }, - dynamicInfo: { - commentCountStr: "", - }, - //推荐歌单 - relativeRecommendSonglist: [], - }); - provide("songlistInfo", songlistInfo); - - const getsonglist = async (id: string) => { - const { playlist = {} } = await playlistDetail({ id, s: subscribedNumber }); - playlist.updateTimeStr = getLocaleDate(playlist.updateTime); - playlist.createTimeStr = getLocaleDate(playlist.createTime); - playlist.shareCountStr = getLocaleCount(playlist.shareCount); - playlist.playCountStr = getLocaleCount(playlist.playCount); - playlist.subscribedCountStr = getLocaleCount(playlist.subscribedCount); - //之所以再获取移除所有歌曲,是因为返回的playlsit.tracks不全 - //@update 2021.09.14 不再再次获取歌曲数据,因为很可能造成缓慢 - // const idsStr = playlist.trackIds.map((_: any) => _.id).join(','); - // if(idsStr) { - // const { songs } = await getMusicDetail({ ids: idsStr }); - // playlist.tracks = songs; - // } - songlistInfo.playlist = playlist; - - }; - - const getsonglistDetail = async (id: string) => { - const { data = {} } = await playlistDetailDynamic({ id }); - const commentCountStr = getLocaleCount(data.commentCount); - songlistInfo.dynamicInfo = { - commentCountStr, - }; - }; - - const getRelativeRecommendSonglist = async (id: string) => { - const { playlists } = await relatedPlaylist({ id }); - songlistInfo.relativeRecommendSonglist = playlists; - } - - const updatePageData = async (params: PlainObject) => { - const { id } = params; - getRelativeRecommendSonglist(id); - await Promise.allSettled([getsonglist(id), getsonglistDetail(id)]); - const tracksLen = songlistInfo.playlist.tracks.length; - const commentCountStr = songlistInfo.dynamicInfo.commentCountStr; - baseSonglistRoutelists.forEach(({ text, to }, i) => { - songlistRoutelists[i] = { - text, - to: to.replace(':id', objToPathname(params, false)), - }; - }); - songlistRoutelists[0].text += tracksLen ? `(${tracksLen})` : ""; - songlistRoutelists[1].text += parseFloat(commentCountStr) - ? `(${commentCountStr})` - : ""; - }; - updatePageData(router.currentRoute.value.params); - - onFilteredBeforeRouteUpdate((to, from) => { - const { id: toId } = to.params; - const { id: fromId } = from.params; - if (toId != fromId) { - updatePageData(to.params); - } - }); - - const toCreatorDetailPage = (id: string) => - router.push({ path: "/user", query: { id } }); - - const renderRelativeRecommendSonglist = () => { - return ( -
        -
        - 相关歌单推荐 {UNICODE_CHAR.smile} -
        - -
        - ) - } - - return () => { - const { playlist } = songlistInfo; - const { - coverImgUrl, - creator: { - userId, - detailDescription, - avatarDetail, - avatarUrl, - nickname, - }, - } = playlist; - - return ( - -
        - -
        - -
        -
        - -
        -
        - -
        - -
        -
        {playlist.name}
        - -
        - -
        toCreatorDetailPage(userId)} - > -
        - - {avatarDetail && ( - - )} -
        - {nickname} -
        - - - 播放量: - {playlist.playCountStr} - -
        - -

        - 描述: - {playlist.description} -

        - -
        - 创建时间: - {playlist.createTimeStr} -
        -
        - 更新时间: - {playlist.updateTimeStr} -
        -
        - -
        -
        - - 分享量: - {playlist.shareCountStr} - -
        -
        - - 订阅量: - {playlist.subscribedCountStr} - -
        -
        - -
        - -
        - -
        - -
        - -
        - { - renderKeepAliveRouterView() - } -
        - - { - renderRelativeRecommendSonglist() - } - -
        - - ); - - }; - - }, - + name: 'songlist', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); + + const songlistRoutelists = reactive([]); + + const songlistInfo = shallowReactive({ + playlist: { + name: '', + creator: { + avatarDetail: { + identityIconUrl: '', + }, + detailDescription: '', + nickname: '', + avatarUrl: '', + userId: '', + }, + coverImgUrl: '', + description: '', + subscribers: [], + tracks: [], + playCountStr: '', + createTimeStr: '', + subscribedCountStr: '', + updateTimeStr: '', + shareCountStr: '', + }, + dynamicInfo: { + commentCountStr: '', + }, + //推荐歌单 + relativeRecommendSonglist: [], + }); + provide('songlistInfo', songlistInfo); + + const getsonglist = async (id: string) => { + const { playlist = {} } = await playlistDetail({ id, s: subscribedNumber }); + playlist.updateTimeStr = getLocaleDate(playlist.updateTime); + playlist.createTimeStr = getLocaleDate(playlist.createTime); + playlist.shareCountStr = getLocaleCount(playlist.shareCount); + playlist.playCountStr = getLocaleCount(playlist.playCount); + playlist.subscribedCountStr = getLocaleCount(playlist.subscribedCount); + //之所以再获取移除所有歌曲,是因为返回的playlsit.tracks不全 + //@update 2021.09.14 不再再次获取歌曲数据,因为很可能造成缓慢 + // const idsStr = playlist.trackIds.map((_: any) => _.id).join(','); + // if(idsStr) { + // const { songs } = await getMusicDetail({ ids: idsStr }); + // playlist.tracks = songs; + // } + songlistInfo.playlist = playlist; + }; + + const getsonglistDetail = async (id: string) => { + const { data = {} } = await playlistDetailDynamic({ id }); + const commentCountStr = getLocaleCount(data.commentCount); + songlistInfo.dynamicInfo = { + commentCountStr, + }; + }; + + const getRelativeRecommendSonglist = async (id: string) => { + const { playlists } = await relatedPlaylist({ id }); + songlistInfo.relativeRecommendSonglist = playlists; + }; + + const updatePageData = async (params: PlainObject) => { + const { id } = params; + getRelativeRecommendSonglist(id); + await Promise.allSettled([getsonglist(id), getsonglistDetail(id)]); + const tracksLen = songlistInfo.playlist.tracks.length; + const commentCountStr = songlistInfo.dynamicInfo.commentCountStr; + baseSonglistRoutelists.forEach(({ text, to }, i) => { + songlistRoutelists[i] = { + text, + to: to.replace(':id', objToPathname(params, false)), + }; + }); + songlistRoutelists[0].text += tracksLen ? `(${tracksLen})` : ''; + songlistRoutelists[1].text += parseFloat(commentCountStr) + ? `(${commentCountStr})` + : ''; + }; + updatePageData(router.currentRoute.value.params); + + onFilteredBeforeRouteUpdate((to, from) => { + const { id: toId } = to.params; + const { id: fromId } = from.params; + if (toId != fromId) { + updatePageData(to.params); + } + }); + + const toCreatorDetailPage = (id: string) => + router.push({ path: '/user', query: { id } }); + + const renderRelativeRecommendSonglist = () => { + return ( +
        +
        相关歌单推荐 {UNICODE_CHAR.smile}
        + +
        + ); + }; + + return () => { + const { playlist } = songlistInfo; + const { + coverImgUrl, + creator: { userId, detailDescription, avatarDetail, avatarUrl, nickname }, + } = playlist; + + return ( +
        +
        +
        +
        + +
        +
        + +
        +
        +
        {playlist.name}
        + +
        +
        toCreatorDetailPage(userId)} + > +
        + + {avatarDetail && ( + + )} +
        + {nickname} +
        + + + 播放量: + {playlist.playCountStr} + +
        + +

        + 描述: + {playlist.description} +

        + +
        + 创建时间: + {playlist.createTimeStr} +
        +
        + 更新时间: + {playlist.updateTimeStr} +
        +
        + +
        +
        + + 分享量: + {playlist.shareCountStr} + +
        +
        + + 订阅量: + {playlist.subscribedCountStr} + +
        +
        +
        +
        + +
        + +
        + +
        {renderKeepAliveRouterView()}
        + + {renderRelativeRecommendSonglist()} +
        + ); + }; + }, }); diff --git a/src/components/songlist/music/index.scss b/src/components/songlist/music/index.scss index ed09936..e63b877 100644 --- a/src/components/songlist/music/index.scss +++ b/src/components/songlist/music/index.scss @@ -1 +1,3 @@ -@import "@/scss/variable"; \ No newline at end of file +/** @format */ + +@import '@/scss/variable'; diff --git a/src/components/songlist/music/index.tsx b/src/components/songlist/music/index.tsx index 197e9cc..cf1658d 100644 --- a/src/components/songlist/music/index.tsx +++ b/src/components/songlist/music/index.tsx @@ -1,16 +1,16 @@ -import { defineComponent, inject, onActivated, toRefs } from "vue"; -import "./index.scss"; -import MusicList from "@/widgets/music-list"; +/** @format */ + +import { defineComponent, inject, onActivated, toRefs } from 'vue'; +import './index.scss'; +import MusicList from '@/widgets/music-list'; export default defineComponent({ - name: "songlistComment", - setup(props, context) { - const songlistInfo = inject("songlistInfo") as any; - return () => { - const { playlist, showIndex } = songlistInfo; - return ( - - ); - }; - }, + name: 'songlistComment', + setup(props, context) { + const songlistInfo = inject('songlistInfo') as any; + return () => { + const { playlist, showIndex } = songlistInfo; + return ; + }; + }, }); diff --git a/src/components/songlist/subscriber/index.scss b/src/components/songlist/subscriber/index.scss index ed09936..e63b877 100644 --- a/src/components/songlist/subscriber/index.scss +++ b/src/components/songlist/subscriber/index.scss @@ -1 +1,3 @@ -@import "@/scss/variable"; \ No newline at end of file +/** @format */ + +@import '@/scss/variable'; diff --git a/src/components/songlist/subscriber/index.tsx b/src/components/songlist/subscriber/index.tsx index 968e5db..f4d6653 100644 --- a/src/components/songlist/subscriber/index.tsx +++ b/src/components/songlist/subscriber/index.tsx @@ -1,83 +1,101 @@ +/** @format */ + import { - toRefs, - inject, - shallowReactive, - watch, - getCurrentInstance, - ref, - defineComponent, - onActivated, -} from "vue"; -import { - useRouter, - useRoute, - onBeforeRouteLeave, - onBeforeRouteUpdate, -} from "vue-router"; -import SubscriberListComp from "@widgets/subscriber-list"; -import RoutePagination, { PagiInfo } from "@widgets/route-pagination"; -import { PlaylistSubscribe } from "@api/playlist"; -import { Subscriber, SubscriberList } from "@/types/songlist"; -import { is, override } from "@/utils"; -import "./index.scss"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; -import { NEmpty } from "naive-ui"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; + toRefs, + inject, + shallowReactive, + watch, + getCurrentInstance, + ref, + defineComponent, + onActivated, +} from 'vue'; +import { useRouter, useRoute, onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'; +import SubscriberListComp from '@widgets/subscriber-list'; +import RoutePagination, { PagiInfo } from '@widgets/route-pagination'; +import { PlaylistSubscribe } from '@api/playlist'; +import { Subscriber, SubscriberList } from '@/types/songlist'; +import { is, override } from '@/utils'; +import './index.scss'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; +import { NEmpty } from 'naive-ui'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; -const defaultLimit = PAGE_SIZE[COMPONENT_NAME.SONGLIST_SUBSCRIBER] +const defaultLimit = PAGE_SIZE[COMPONENT_NAME.SONGLIST_SUBSCRIBER]; export default defineComponent({ - name: COMPONENT_NAME.SONGLIST_SUBSCRIBER, - setup(props, context) { - const route = useRoute(); - const router = useRouter(); - const vm = getCurrentInstance()!; - const subscribers = shallowReactive([]); + name: COMPONENT_NAME.SONGLIST_SUBSCRIBER, + setup(props, context) { + const route = useRoute(); + const router = useRouter(); + const vm = getCurrentInstance()!; + const subscribers = shallowReactive([]); + + const isShowPagi = ref(); + const subscriberPagiInfo = shallowReactive({ + total: 0, + hasMore: true, + }); + const getPlaylistSubscribers = async () => { + const { + params: { id }, + query: { limit = defaultLimit, offset = 0 }, + } = router.currentRoute.value; + const { + more, + total, + subscribers: subs, + reason, + } = await PlaylistSubscribe({ + id: String(id), + limit: Number(limit), + offset: Number(offset), + }); + subscribers.length = 0; + subscribers.push(...subs); + subscriberPagiInfo.total = total; + subscriberPagiInfo.hasMore = more; + isShowPagi.value = reason !== 'needLogin'; + }; + onActivated(() => { + getPlaylistSubscribers(); + }); - const isShowPagi = ref(); - const subscriberPagiInfo = shallowReactive({ - total: 0, - hasMore: true - }) - const getPlaylistSubscribers = async () => { - const { params: { id }, query: { limit = defaultLimit, offset = 0 } } = router.currentRoute.value; - const { more, total, subscribers: subs, reason } = await PlaylistSubscribe({ - id: String(id), - limit: Number(limit), - offset: Number(offset), - }); - subscribers.length = 0; - subscribers.push(...subs); - subscriberPagiInfo.total = total; - subscriberPagiInfo.hasMore = more; - isShowPagi.value = reason !== "needLogin"; - }; - onActivated(() => { - getPlaylistSubscribers(); - }); + onFilteredBeforeRouteUpdate((to, from) => { + const { + params: { id }, + query: { limit, offset }, + } = to; + const { + params: { id: oldId }, + query: { limit: oldLimit, offset: oldOffset }, + } = from; + if (id !== oldId || limit !== oldLimit || offset !== oldOffset) { + getPlaylistSubscribers(); + } + }); - onFilteredBeforeRouteUpdate((to, from) => { - const { params: { id }, query: { limit, offset } } = to; - const { params: { id:oldId }, query: { limit:oldLimit , offset:oldOffset } } = from; - if(id !== oldId || limit !== oldLimit || offset !== oldOffset) { - getPlaylistSubscribers(); - } - }); - - const renderNotLogin = () => { - if (is.emptyArray(subscribers) && isShowPagi.value === false) { - return - } - }; + const renderNotLogin = () => { + if (is.emptyArray(subscribers) && isShowPagi.value === false) { + return ( + + ); + } + }; - return () => { - const { hasMore, total } = subscriberPagiInfo; - return ( -
        - {renderNotLogin()} - -
        - ); - }; - }, + return () => { + const { hasMore, total } = subscriberPagiInfo; + return ( +
        + {renderNotLogin()} + +
        + ); + }; + }, }); diff --git a/src/components/user/collection/index.scss b/src/components/user/collection/index.scss index f7234ab..e63b877 100644 --- a/src/components/user/collection/index.scss +++ b/src/components/user/collection/index.scss @@ -1,2 +1,3 @@ -@import "@/scss/variable"; - \ No newline at end of file +/** @format */ + +@import '@/scss/variable'; diff --git a/src/components/user/collection/index.tsx b/src/components/user/collection/index.tsx index da36184..57d423f 100644 --- a/src/components/user/collection/index.tsx +++ b/src/components/user/collection/index.tsx @@ -1,40 +1,47 @@ +/** @format */ + import { - toRefs, - watch, - ref, - reactive, - shallowReactive, - shallowRef, - Ref, - computed, - defineComponent, - inject, -} from "vue"; -import { useRouter, useRoute, onBeforeRouteLeave } from "vue-router"; -import Songlist from "@widgets/song-list"; -import "./index.scss"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; + toRefs, + watch, + ref, + reactive, + shallowReactive, + shallowRef, + Ref, + computed, + defineComponent, + inject, +} from 'vue'; +import { useRouter, useRoute, onBeforeRouteLeave } from 'vue-router'; +import Songlist from '@widgets/song-list'; +import './index.scss'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; const defaultLimit = PAGE_SIZE[COMPONENT_NAME.USER_COLLECTION]; export default defineComponent({ - name: COMPONENT_NAME.USER_COLLECTION, - setup(props, context) { - const songlists = inject("songlists") as any; - return () => { - const { collection: { data, hasMore } } = songlists; - return ( -
        - -
        -
        - 收藏的 - {data.length} -
        - -
        -
        - ) - }; - }, + name: COMPONENT_NAME.USER_COLLECTION, + setup(props, context) { + const songlists = inject('songlists') as any; + return () => { + const { + collection: { data, hasMore }, + } = songlists; + return ( +
        +
        +
        + 收藏的 + {data.length} +
        + +
        +
        + ); + }; + }, }); diff --git a/src/components/user/config.ts b/src/components/user/config.ts index 8fe772d..7ee84c1 100644 --- a/src/components/user/config.ts +++ b/src/components/user/config.ts @@ -1,12 +1,14 @@ +/** @format */ + export const playRecordTimeRange = [ - { type: 1, text: "最近一周" }, - { type: 0, text: "所有时间" }, + { type: 1, text: '最近一周' }, + { type: 0, text: '所有时间' }, ]; export const defaultPlayRecordType = playRecordTimeRange[0].type; export const baseUserMenuRouteLists = [ - { to: "/user/playRecord", text: "听歌排行" }, - { to: "/user/collection", text: "收藏" }, - { to: "/user/songlist", text: "歌单" }, + { to: '/user/playRecord', text: '听歌排行' }, + { to: '/user/collection', text: '收藏' }, + { to: '/user/songlist', text: '歌单' }, ]; diff --git a/src/components/user/index.scss b/src/components/user/index.scss index 1ad9db0..483531a 100644 --- a/src/components/user/index.scss +++ b/src/components/user/index.scss @@ -1,49 +1,49 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .user-header { - @include flexVc(); - padding-bottom: 2em; - max-height: 300px; - .user-avatar { - width: 180px; - margin-right: 1em; - object-fit: cover; - border-radius: 100%; - } - - .user-name { - padding-bottom: 1em; - font-size: 24px; - } - - .user-btns { - padding: 1em 0; - } - - .userinfo-layer { - @include flex(); - padding-bottom: 1em; - - > em { - width: 5rem; - color: #455; - } - - > span { - flex: 1; - line-height: 1.4; - color: #666; - } - } + @include flexVc(); + padding-bottom: 2em; + max-height: 300px; + .user-avatar { + width: 180px; + margin-right: 1em; + object-fit: cover; + border-radius: 100%; + } + + .user-name { + padding-bottom: 1em; + font-size: 24px; + } + + .user-btns { + padding: 1em 0; + } + + .userinfo-layer { + @include flex(); + padding-bottom: 1em; + + > em { + width: 5rem; + color: #455; + } + + > span { + flex: 1; + line-height: 1.4; + color: #666; + } + } } - - -.user-routes { - .songlist-layer { - .songlist-title { - font-size: 18px; - font-weight: 500; - } - } -} \ No newline at end of file +.user-routes { + .songlist-layer { + .songlist-title { + font-size: 18px; + font-weight: 500; + } + } +} diff --git a/src/components/user/index.tsx b/src/components/user/index.tsx index 27d94bf..53461c8 100644 --- a/src/components/user/index.tsx +++ b/src/components/user/index.tsx @@ -1,205 +1,204 @@ +/** @format */ + +import { + toRefs, + watch, + ref, + reactive, + shallowReactive, + computed, + provide, + onActivated, + WatchStopHandle, + defineComponent, +} from 'vue'; import { - toRefs, - watch, - ref, - reactive, - shallowReactive, - computed, - provide, - onActivated, - WatchStopHandle, - defineComponent, -} from "vue"; + useRouter, + useRoute, + onBeforeRouteLeave, + RouterView, + routeLocationKey, + RouteLocationNormalized, +} from 'vue-router'; +import { userDetail, userSubcount, userRecord, userPlaylist } from '@api/user'; import { - useRouter, - useRoute, - onBeforeRouteLeave, - RouterView, - routeLocationKey, - RouteLocationNormalized, -} from "vue-router"; -import { userDetail, userSubcount, userRecord, userPlaylist } from "@api/user"; -import { EMPTY_ARR, EMPTY_OBJ, getLocaleDate, NOOP, objToQuery, padPicCrop } from "@utils/index"; -import { PlayRecord } from "@/types/song"; -import { UserDetail } from "@/types/user"; -import "./index.scss"; + EMPTY_ARR, + EMPTY_OBJ, + getLocaleDate, + NOOP, + objToQuery, + padPicCrop, +} from '@utils/index'; +import { PlayRecord } from '@/types/song'; +import { UserDetail } from '@/types/user'; +import './index.scss'; -import { baseUserMenuRouteLists, defaultPlayRecordType } from "./config"; -import { PlaylistCommon } from "@/types/songlist"; -import { NxButton, NSpace } from "naive-ui"; -import CommonRouterList from "@/widgets/common-router-list"; -import { renderKeepAliveRouterView } from "@/widgets/common-renderer"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { baseUserMenuRouteLists, defaultPlayRecordType } from './config'; +import { PlaylistCommon } from '@/types/songlist'; +import { NxButton, NSpace } from 'naive-ui'; +import CommonRouterList from '@/widgets/common-router-list'; +import { renderKeepAliveRouterView } from '@/widgets/common-renderer'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export default defineComponent({ - name: "UserDetail", - setup(props, context) { - const route = useRoute(); + name: 'UserDetail', + setup(props, context) { + const route = useRoute(); + + //用户基础信息 + const userInfo = ref({ + adValid: false, + bindings: [], + createTime: 0, + createDays: 0, + level: 0, + listenSongs: 0, + peopleCanSeeMyPlayRecord: false, + profile: { + avatarUrl: '', + nickname: '', + }, + }); + provide('userInfo', userInfo); + const playRecordData = shallowReactive([]); + provide('playRecordData', playRecordData); - //用户基础信息 - const userInfo = ref({ - adValid: false, - bindings: [], - createTime: 0, - createDays: 0, - level: 0, - listenSongs: 0, - peopleCanSeeMyPlayRecord: false, - profile: { - avatarUrl: "", - nickname: "", - }, - }); - provide("userInfo", userInfo); - const playRecordData = shallowReactive([]); - provide("playRecordData", playRecordData); + //路由菜单列表 + const userInfoRouteLists = computed(() => { + const query = objToQuery({ id: route.query.id as string }, true); + return baseUserMenuRouteLists.reduce((total, { to, text }, i) => { + total[i] = { + to: to + query, + text, + }; + return total; + }, [] as typeof baseUserMenuRouteLists); + }); - //路由菜单列表 - const userInfoRouteLists = computed(() => { - const query = objToQuery({ id: route.query.id as string }, true); - return baseUserMenuRouteLists.reduce((total, { to, text }, i) => { - total[i] = { - to: to + query, - text, - }; - return total; - }, [] as typeof baseUserMenuRouteLists); - }); + //歌单(包括收藏和创建) + const songlists = shallowReactive({ + created: { + data: [] as PlaylistCommon[], + hasMore: true, + }, + collection: { + data: [] as PlaylistCommon[], + hasMore: true, + }, + }); - //歌单(包括收藏和创建) - const songlists = shallowReactive({ - created: { - data: [] as PlaylistCommon[], - hasMore: true - }, - collection: { - data: [] as PlaylistCommon[], - hasMore: true - }, - }); + provide('songlists', songlists); + const getUserPlaylist = async (query: any) => { + const { id: uid, limit, offset } = query; + const { playlist = EMPTY_ARR, more } = await userPlaylist({ uid, limit, offset }); + const collection: PlaylistCommon[] = []; + const created: PlaylistCommon[] = []; + playlist.forEach((list: PlaylistCommon) => { + const { + creator: { userId }, + } = list; + (userId == uid ? created : collection).push(list); + }); + songlists.created = { + data: created, + hasMore: more, + }; + songlists.collection = { + data: collection, + hasMore: more, + }; + }; - provide("songlists", songlists); - const getUserPlaylist = async (query: any) => { - const { id: uid, limit, offset } = query; - const { playlist = EMPTY_ARR, more } = await userPlaylist({ uid, limit, offset }); - const collection: PlaylistCommon[] = []; - const created: PlaylistCommon[] = []; - playlist.forEach((list: PlaylistCommon) => { - const { - creator: { userId }, - } = list; - (userId == uid ? created : collection).push(list); - }); - songlists.created = { - data: created, - hasMore: more - }; - songlists.collection = { - data: collection, - hasMore: more - }; - }; + //获取用户最近播放记录 + const getUserRecord = async (uid: string | number, type: 0 | 1) => { + const { weekData, allData } = await userRecord({ + uid, + type, + }); + const weekOrAllData = weekData || allData || []; + playRecordData.length = 0; + playRecordData.push(...weekOrAllData); + }; - //获取用户最近播放记录 - const getUserRecord = async (uid: string | number, type: 0 | 1) => { - const { weekData, allData } = await userRecord({ - uid, - type, - }); - const weekOrAllData = weekData || allData || []; - playRecordData.length = 0; - playRecordData.push(...weekOrAllData); - }; + //获取用户详细信息 + const getUserDetail = async (uid: string | number) => { + const data = await userDetail(uid); + data && (userInfo.value = data); + }; - //获取用户详细信息 - const getUserDetail = async (uid: string | number) => { - const data = await userDetail(uid); - data && (userInfo.value = data); - }; + const routeUpdateHandler = async ( + { query }: RouteLocationNormalized, + { query: oldQuery = EMPTY_OBJ }: RouteLocationNormalized = EMPTY_OBJ, + ) => { + const { id, type, limit, offset } = query as PlainObject; + const { id: oldId, type: oldType, limit: oldLimit, offset: oldOffset } = oldQuery; + if (id !== oldId || limit !== oldLimit || offset !== oldOffset) { + getUserPlaylist({ id, limit, offset }); + } + if (id !== oldId) { + await getUserDetail(id); + } + if (id !== oldId || type !== oldType) { + //如果用户设置了播放记录是可见的 + userInfo.value.peopleCanSeeMyPlayRecord && getUserRecord(id, type); + } + }; + routeUpdateHandler(route); - const routeUpdateHandler = async ( - { query }: RouteLocationNormalized, - { query: oldQuery = EMPTY_OBJ }: RouteLocationNormalized = EMPTY_OBJ, - ) => { - const { id, type, limit, offset } = query as PlainObject; - const { - id: oldId, - type: oldType, - limit: oldLimit, - offset: oldOffset, - } = oldQuery; - if (id !== oldId || limit !== oldLimit || offset !== oldOffset) { - getUserPlaylist({ id, limit, offset }); - } - if (id !== oldId) { - await getUserDetail(id); - } - if (id !== oldId || type !== oldType) { - //如果用户设置了播放记录是可见的 - userInfo.value.peopleCanSeeMyPlayRecord && getUserRecord(id, type); - } - } - routeUpdateHandler(route); - - onFilteredBeforeRouteUpdate((to, from) => { - routeUpdateHandler(to, from); - }); + onFilteredBeforeRouteUpdate((to, from) => { + routeUpdateHandler(to, from); + }); - return () => { - return ( -
        -
        - -
        -

        {userInfo.value.profile.nickname}

        -
        - - - 动态: - {userInfo.value.profile.eventCount} - - - 关注: - {userInfo.value.profile.follows} - - - 粉丝: - {userInfo.value.profile.followeds} - - -
        -

        - 个性签名: - {userInfo.value.profile.signature || "暂无"} -

        -

        - 生日: - - {getLocaleDate(Number(userInfo.value.profile.birthday)) || - "暂无"} - -

        -
        -
        + return () => { + return ( +
        +
        + +
        +

        {userInfo.value.profile.nickname}

        +
        + + + 动态: + {userInfo.value.profile.eventCount} + + + 关注: + {userInfo.value.profile.follows} + + + 粉丝: + {userInfo.value.profile.followeds} + + +
        +

        + 个性签名: + {userInfo.value.profile.signature || '暂无'} +

        +

        + 生日: + + {getLocaleDate(Number(userInfo.value.profile.birthday)) || '暂无'} + +

        +
        +
        -
        - -
        - { - renderKeepAliveRouterView() - } -
        -
        -
        - ); - }; - }, +
        + +
        {renderKeepAliveRouterView()}
        +
        +
        + ); + }; + }, }); diff --git a/src/components/user/play-record/index.scss b/src/components/user/play-record/index.scss index 33f9591..bbd975b 100644 --- a/src/components/user/play-record/index.scss +++ b/src/components/user/play-record/index.scss @@ -1,34 +1,36 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .play-record-header { - @include flexHbVc(); - padding-bottom: 20px; + @include flexHbVc(); + padding-bottom: 20px; } .record-desc { - color: #666; - font-size: 16px; + color: #666; + font-size: 16px; - span { - font-weight: 600; - } + span { + font-weight: 600; + } } .record-icon-info { - @include flexInline(); - width: 20px; - height: 20px; - margin-right: .3rem; - background: url("@assets/img/chat-warning.svg") center / cover no-repeat; - vertical-align: middle; + @include flexInline(); + width: 20px; + height: 20px; + margin-right: 0.3rem; + background: url('@assets/img/chat-warning.svg') center / cover no-repeat; + vertical-align: middle; } .canNotSee { - padding-top: 2em; - color: #666; - span { - margin-right: 1rem; - color: rgb(247, 171, 101); - font-weight: 600; - } -} \ No newline at end of file + padding-top: 2em; + color: #666; + span { + margin-right: 1rem; + color: rgb(247, 171, 101); + font-weight: 600; + } +} diff --git a/src/components/user/play-record/index.tsx b/src/components/user/play-record/index.tsx index e6a37d8..659146d 100644 --- a/src/components/user/play-record/index.tsx +++ b/src/components/user/play-record/index.tsx @@ -1,100 +1,90 @@ +/** @format */ + +import { toRefs, watch, ref, Ref, computed, inject, defineComponent } from 'vue'; import { - toRefs, - watch, - ref, - Ref, - computed, - inject, - defineComponent, -} from "vue"; -import { useRouter, useRoute, onBeforeRouteLeave, RouteLocationRaw, RouteLocationNormalized } from "vue-router"; -import MusicList from "@/widgets/music-list"; + useRouter, + useRoute, + onBeforeRouteLeave, + RouteLocationRaw, + RouteLocationNormalized, +} from 'vue-router'; +import MusicList from '@/widgets/music-list'; -import { PlayRecord } from "@/types/song"; -import { defaultPlayRecordType, playRecordTimeRange } from "../config"; -import "./index.scss"; -import { NRadio, NRadioButton, NRadioGroup, NSpace, NIcon, } from "naive-ui"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { PlayRecord } from '@/types/song'; +import { defaultPlayRecordType, playRecordTimeRange } from '../config'; +import './index.scss'; +import { NRadio, NRadioButton, NRadioGroup, NSpace, NIcon } from 'naive-ui'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export default defineComponent({ - name: "PlayRecord", - setup(props, context) { - const route = useRoute(); - const router = useRouter(); + name: 'PlayRecord', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); - const playlist = inject("playRecordData")!; - const userInfo = inject>("userInfo")!; + const playlist = inject('playRecordData')!; + const userInfo = inject>('userInfo')!; - const musiclist = computed(() => playlist.map(({ song }) => song)); + const musiclist = computed(() => playlist.map(({ song }) => song)); - const curPlayRecordRange = ref(1); + const curPlayRecordRange = ref(1); - const playRecordRangeChange = (typeVal: number | string) => { - router.push({ - path: route.path, - query: { ...route.query, type: typeVal }, - }); - }; + const playRecordRangeChange = (typeVal: number | string) => { + router.push({ + path: route.path, + query: { ...route.query, type: typeVal }, + }); + }; - const routeUpdateHandler = async ({query: { type }} : RouteLocationNormalized) => { - const curType = Number(type); - const isExsit = playRecordTimeRange.some( - ({ type }) => curType === type - ); - curPlayRecordRange.value = isExsit ? curType : defaultPlayRecordType; - } - routeUpdateHandler(route); + const routeUpdateHandler = async ({ query: { type } }: RouteLocationNormalized) => { + const curType = Number(type); + const isExsit = playRecordTimeRange.some(({ type }) => curType === type); + curPlayRecordRange.value = isExsit ? curType : defaultPlayRecordType; + }; + routeUpdateHandler(route); - onFilteredBeforeRouteUpdate((to) => { - routeUpdateHandler(to); - }); + onFilteredBeforeRouteUpdate((to) => { + routeUpdateHandler(to); + }); - return () => { - const { listenSongs, peopleCanSeeMyPlayRecord, profile } = userInfo.value; - return ( -
        -
        -
        - - 最近累计听歌 - {listenSongs}首 -
        - { - peopleCanSeeMyPlayRecord && ( -
        - - - { - playRecordTimeRange.map((item, i) => ( - - {item.text} - - )) - } - - -
        - ) - } -
        + return () => { + const { listenSongs, peopleCanSeeMyPlayRecord, profile } = userInfo.value; + return ( +
        +
        +
        + + 最近累计听歌 + {listenSongs}首 +
        + {peopleCanSeeMyPlayRecord && ( +
        + + + {playRecordTimeRange.map((item, i) => ( + + {item.text} + + ))} + + +
        + )} +
        - { - peopleCanSeeMyPlayRecord - ? ( - - ) - : ( -

        - {profile?.nickname} - 没有开放播放记录查看权限喔~~ -

        - ) - } -
        - ); - }; - }, + {peopleCanSeeMyPlayRecord ? ( + + ) : ( +

        + {profile?.nickname} + 没有开放播放记录查看权限喔~~ +

        + )} +
        + ); + }; + }, }); diff --git a/src/components/user/songlist/index.scss b/src/components/user/songlist/index.scss index f4ab24f..e63b877 100644 --- a/src/components/user/songlist/index.scss +++ b/src/components/user/songlist/index.scss @@ -1,2 +1,3 @@ -@import "@/scss/variable"; +/** @format */ +@import '@/scss/variable'; diff --git a/src/components/user/songlist/index.tsx b/src/components/user/songlist/index.tsx index 5ac1c0b..645264c 100644 --- a/src/components/user/songlist/index.tsx +++ b/src/components/user/songlist/index.tsx @@ -1,41 +1,42 @@ -import { - inject, - computed, - Ref, - defineComponent, - provide, -} from "vue"; -import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router"; -import Songlist from "@widgets/song-list"; -import "./index.scss"; -import { COMPONENT_NAME, PAGE_SIZE } from "@/utils/preference"; +/** @format */ + +import { inject, computed, Ref, defineComponent, provide } from 'vue'; +import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'; +import Songlist from '@widgets/song-list'; +import './index.scss'; +import { COMPONENT_NAME, PAGE_SIZE } from '@/utils/preference'; const defaultLimit = PAGE_SIZE[COMPONENT_NAME.USER_SONGLIST]; export default defineComponent({ - name: COMPONENT_NAME.USER_SONGLIST, - components: { - Songlist, - }, - setup(props, context) { - const router = useRouter(); - const id = Number(router.currentRoute.value.query.id); - const songlists = inject("songlists") as any; - - return () => { - const { created: { data, hasMore } } = songlists; - return ( -
        -
        -
        - 创建的 - {data.length} -
        - -
        + name: COMPONENT_NAME.USER_SONGLIST, + components: { + Songlist, + }, + setup(props, context) { + const router = useRouter(); + const id = Number(router.currentRoute.value.query.id); + const songlists = inject('songlists') as any; -
        - ); - }; - }, + return () => { + const { + created: { data, hasMore }, + } = songlists; + return ( +
        +
        +
        + 创建的 + {data.length} +
        + +
        +
        + ); + }; + }, }); diff --git a/src/components/video/index.scss b/src/components/video/index.scss index 351f9e5..4675fe8 100644 --- a/src/components/video/index.scss +++ b/src/components/video/index.scss @@ -1,84 +1,83 @@ -@import "@/scss/variable"; -.video-page { - @include flex(); - .video-content { - .video-container{ - width: 800px; - height: 450px; - video { - width: 100%; - height: 100%; - object-fit: contain; - border-radius: 2rem; - } - } - - .video-operator { - padding-top: 2rem; - .video-author { - @include flexHbVc(); - padding-bottom: 2rem; - .author-avatar { - width: 50px; - height: 50px; - position: relative; - background-size: cover; - background-position: center center; - background-repeat: no-repeat; - border-radius: 100%; - cursor: pointer; - &:hover { - color: var(--theme); - } - em { - position: absolute; - left: calc(100% + 1rem); - top: 50%; - white-space: nowrap; - transform: translateY(-50%); - } - } - } - .video-title { - padding-bottom: 1rem; - color: #646464; - font-size: 22px; - font-weight: 500; - } - .video-info { - padding-bottom: 1rem; - color: #848484; - } - .video-tags { - padding-bottom: 1rem; - } - .video-buttons { - padding-top: 1rem; - i { - font-size: 1.4rem; - } - em { - text-indent: .5rem; - } - } - } +/** @format */ - .video-recommend { - > h4 { - font-size: 20px; - font-weight: 500; - line-height: 3 ; - } - margin-top: 2rem; - } +@import '@/scss/variable'; +.video-page { + @include flex(); + .video-content { + .video-container { + width: 800px; + height: 450px; + video { + width: 100%; + height: 100%; + object-fit: contain; + border-radius: 2rem; + } + } - } + .video-operator { + padding-top: 2rem; + .video-author { + @include flexHbVc(); + padding-bottom: 2rem; + .author-avatar { + width: 50px; + height: 50px; + position: relative; + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + border-radius: 100%; + cursor: pointer; + &:hover { + color: var(--theme); + } + em { + position: absolute; + left: calc(100% + 1rem); + top: 50%; + white-space: nowrap; + transform: translateY(-50%); + } + } + } + .video-title { + padding-bottom: 1rem; + color: #646464; + font-size: 22px; + font-weight: 500; + } + .video-info { + padding-bottom: 1rem; + color: #848484; + } + .video-tags { + padding-bottom: 1rem; + } + .video-buttons { + padding-top: 1rem; + i { + font-size: 1.4rem; + } + em { + text-indent: 0.5rem; + } + } + } - .video-comment { - flex: 1; - background-color: turquoise; - height: 100%; - } - + .video-recommend { + > h4 { + font-size: 20px; + font-weight: 500; + line-height: 3; + } + margin-top: 2rem; + } + } -} \ No newline at end of file + .video-comment { + flex: 1; + background-color: turquoise; + height: 100%; + } +} diff --git a/src/components/video/index.tsx b/src/components/video/index.tsx index b2cd195..8aac084 100644 --- a/src/components/video/index.tsx +++ b/src/components/video/index.tsx @@ -1,282 +1,321 @@ -import { computed, defineComponent, nextTick, reactive, ref, watch, watchEffect } from "vue"; -import { onBeforeRouteLeave, useRoute, useRouter } from "vue-router"; -import "./index.scss"; +/** @format */ -import { - getRelativeVideos, - getVideoDetail, - getVideoRelativeInfo, - getVideoPlaybackSource, -} from "@api/video"; -import { extend, getLocaleCount, getLocaleDate, is, padPicCrop } from "@/utils"; -import { VideoDetailInfoItem, VideoPlaybackSourceItem } from "@/types/video"; -import usePlayerStore from "@/stores/player"; -import VideoList from "@/widgets/video-list"; -import FollowButton, { FollowType } from "@/widgets/follow-button"; -import { NSpace, NxButton } from "naive-ui"; -import YuanButton from "@/widgets/yuan-button"; -import { praiseResource } from "@/api/other"; -import { unOrFocusUser, userVideoCollect } from "@/api/user"; -import { messageBus } from "@/utils/event/register"; -import { render } from "naive-ui/lib/_utils"; +import { + computed, + defineComponent, + nextTick, + reactive, + ref, + watch, + watchEffect, +} from 'vue'; +import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'; +import './index.scss'; + +import { + getRelativeVideos, + getVideoDetail, + getVideoRelativeInfo, + getVideoPlaybackSource, +} from '@api/video'; +import { extend, getLocaleCount, getLocaleDate, is, padPicCrop } from '@/utils'; +import { VideoDetailInfoItem, VideoPlaybackSourceItem } from '@/types/video'; +import usePlayerStore from '@/stores/player'; +import VideoList from '@/widgets/video-list'; +import FollowButton, { FollowType } from '@/widgets/follow-button'; +import { NSpace, NxButton } from 'naive-ui'; +import YuanButton from '@/widgets/yuan-button'; +import { praiseResource } from '@/api/other'; +import { unOrFocusUser, userVideoCollect } from '@/api/user'; +import { messageBus } from '@/utils/event/register'; +import { render } from 'naive-ui/lib/_utils'; export default defineComponent({ - name: "Video", - setup(props, context) { - const route = useRoute(); - const router = useRouter(); - const playerStore = usePlayerStore(); - const videoRef = ref(); - const hasVideoSource = ref(false); - const videoData = reactive({ - relativeInfo: { - commentCount: 0, - liked: false, - likedCount: 0, - shareCount: 0, - }, - detail: { - videoGroup: [] as VideoDetailInfoItem['videoGroup'], - coverUrl: '', - creator: { - avatarUrl: '', - nickname: '', - }, - subscribeCount: 0, - description: '', - vid: '', - } as VideoDetailInfoItem, - isCollected: false, - }); + name: 'Video', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); + const playerStore = usePlayerStore(); + const videoRef = ref(); + const hasVideoSource = ref(false); + const videoData = reactive({ + relativeInfo: { + commentCount: 0, + liked: false, + likedCount: 0, + shareCount: 0, + }, + detail: { + videoGroup: [] as VideoDetailInfoItem['videoGroup'], + coverUrl: '', + creator: { + avatarUrl: '', + nickname: '', + }, + subscribeCount: 0, + description: '', + vid: '', + } as VideoDetailInfoItem, + isCollected: false, + }); + + const videoUrlInfo = ref([ + { + id: '', + payInfo: null, + needPay: false, + r: 0, + size: 0, + url: '', + validityTime: 0, + }, + ]); + + const recommendVideos = ref([]); + + const vidWatcher = watch( + () => route.query.vid as string, + (vid) => { + getRelativeVideos({ + id: vid, + }).then(({ data }) => { + recommendVideos.value = data; + }); + getVideoDetail({ + id: vid, + }).then(({ data, code }) => { + const isSuccess = code === 200; + hasVideoSource.value = isSuccess; + if (!isSuccess) { + messageBus.dispatch('errorMessage', '无法获取视频信息'); + return; + } + videoData.detail = data; + }); + getVideoRelativeInfo({ + vid, + }).then((detailInfoFromServer) => { + videoData.relativeInfo = detailInfoFromServer; + }); + getVideoPlaybackSource({ + id: vid, + }).then(({ urls }) => { + if (is.emptyArray(urls)) { + return; + } + videoUrlInfo.value = urls; + }); + }, + { + immediate: true, + }, + ); - const videoUrlInfo = ref([{ - id: '', - payInfo: null, - needPay: false, - r: 0, - size: 0, - url: '', - validityTime: 0 - }]); + watchEffect(() => { + videoData.isCollected = playerStore.isVideoBeLiked( + videoData.detail.vid, + videoData.detail.description, + ); + }); - const recommendVideos = ref([]); - - const vidWatcher = watch(() => route.query.vid as string, (vid) => { - getRelativeVideos({ - id: vid, - }).then(({data}) => { - recommendVideos.value = data; - }); - getVideoDetail({ - id: vid, - }).then(({data, code}) => { - const isSuccess = code === 200; - hasVideoSource.value = isSuccess; - if(!isSuccess) { - messageBus.dispatch('errorMessage', '无法获取视频信息'); - return; - } - videoData.detail = data; - }); - getVideoRelativeInfo({ - vid, - }).then((detailInfoFromServer) => { - videoData.relativeInfo = detailInfoFromServer; - }); - getVideoPlaybackSource({ - id: vid, - }).then(({urls}) => { - if(is.emptyArray(urls)) { - return; - } - videoUrlInfo.value = urls; - }); - }, { - immediate: true, - }); + onBeforeRouteLeave(() => { + vidWatcher(); + }); - watchEffect(() => { - videoData.isCollected = playerStore.isVideoBeLiked(videoData.detail.vid, videoData.detail.description); - }); - - onBeforeRouteLeave(() => { - vidWatcher(); - }); - - watch(() => playerStore.video.isPlay, async (isPlay) => { - await nextTick(); - const videoRefValuePaused = videoRef.value!.paused; - if(isPlay === videoRefValuePaused) { - videoRef.value![ videoRefValuePaused ? 'play' : 'pause'](); - } - }); + watch( + () => playerStore.video.isPlay, + async (isPlay) => { + await nextTick(); + const videoRefValuePaused = videoRef.value!.paused; + if (isPlay === videoRefValuePaused) { + videoRef.value![videoRefValuePaused ? 'play' : 'pause'](); + } + }, + ); - const onVideoPlayHandler = () => { - playerStore.setVideoIsPlay(true); - } - const onVideoPauseHandler = () => { - playerStore.setVideoIsPlay(false); - } + const onVideoPlayHandler = () => { + playerStore.setVideoIsPlay(true); + }; + const onVideoPauseHandler = () => { + playerStore.setVideoIsPlay(false); + }; - const toVideoTagListPage = (id: string | number) => { - router.push({ - path: '/onlinevideo/category', - query: { - id - } - }); - } + const toVideoTagListPage = (id: string | number) => { + router.push({ + path: '/onlinevideo/category', + query: { + id, + }, + }); + }; - /** - * 收藏视频 - * @param value - */ - const collectVideoHandler = async (value: boolean) => { - const { code , message } = await userVideoCollect({ - id: String(route.query.vid), - sure: value, - }); - const isSuccess = code === 200; - let messageTopic = 'warnMessage'; - if(isSuccess) { - videoData.isCollected = value; - videoData.detail.subscribeCount = videoData.detail.subscribeCount + (value ? 1 : -1); - messageTopic = 'successMessage'; - } - messageBus.dispatch(messageTopic, message); - } + /** + * 收藏视频 + * @param value + */ + const collectVideoHandler = async (value: boolean) => { + const { code, message } = await userVideoCollect({ + id: String(route.query.vid), + sure: value, + }); + const isSuccess = code === 200; + let messageTopic = 'warnMessage'; + if (isSuccess) { + videoData.isCollected = value; + videoData.detail.subscribeCount = + videoData.detail.subscribeCount + (value ? 1 : -1); + messageTopic = 'successMessage'; + } + messageBus.dispatch(messageTopic, message); + }; - /** - * 点赞视频 - * @param value - */ - const praiseVideoHandler = async (value: boolean) => { - const { code } = await praiseResource({ - id: String(route.query.vid), - sure: value, - type: 5, - }); - const isSuccess = code === 200; - let messageTopic = 'warnMessage'; - let msg = '取消点赞成功~~'; - if(isSuccess) { - videoData.relativeInfo.liked = value; - videoData.relativeInfo.likedCount = videoData.relativeInfo.likedCount + (value ? 1 : -1); - messageTopic = 'successMessage'; - value && (msg = '点赞成功~~'); - } - messageBus.dispatch( - messageTopic, - msg - ); - } + /** + * 点赞视频 + * @param value + */ + const praiseVideoHandler = async (value: boolean) => { + const { code } = await praiseResource({ + id: String(route.query.vid), + sure: value, + type: 5, + }); + const isSuccess = code === 200; + let messageTopic = 'warnMessage'; + let msg = '取消点赞成功~~'; + if (isSuccess) { + videoData.relativeInfo.liked = value; + videoData.relativeInfo.likedCount = + videoData.relativeInfo.likedCount + (value ? 1 : -1); + messageTopic = 'successMessage'; + value && (msg = '点赞成功~~'); + } + messageBus.dispatch(messageTopic, msg); + }; - /** - * 关注用户状态改变 - * @param isFollow - */ - const followedChangeHandler = async (isFollow: boolean) => { - videoData.detail.creator.followed = isFollow; - } + /** + * 关注用户状态改变 + * @param isFollow + */ + const followedChangeHandler = async (isFollow: boolean) => { + videoData.detail.creator.followed = isFollow; + }; - const renderVideoOperator = () => { - const { detail, isCollected, relativeInfo: { liked, likedCount, shareCount } } = videoData; - const { title, publishTime, videoGroup, subscribeCount, creator: { avatarUrl, userId, nickname, followed } } = detail; - const videoAuthorAvatarUrl = padPicCrop(avatarUrl, {x: 80, y:80}); - const videoAvatarStyle = `background-image:url(${videoAuthorAvatarUrl})`; - const publishTimeStr = getLocaleDate(publishTime); - const subscribeCountStr = getLocaleCount(subscribeCount); - const likedCountStr = getLocaleCount(likedCount); - const shareCountStr = getLocaleCount(shareCount); - return ( -
        -
        - - {nickname} - - -
        -

        - {title} -

        -

        - - 发布: - - { publishTimeStr } - - -

        -
        - - { - videoGroup.map(({id,name}) => { - return toVideoTagListPage(id)}>{name} - }) - } - -
        -
        - - collectVideoHandler(value)}> - - 收藏{subscribeCountStr} - - praiseVideoHandler(value)}> - - 赞{likedCountStr} - - - - 分享{shareCountStr} - - - - 下载 - - -
        -
        - ) - } + const renderVideoOperator = () => { + const { + detail, + isCollected, + relativeInfo: { liked, likedCount, shareCount }, + } = videoData; + const { + title, + publishTime, + videoGroup, + subscribeCount, + creator: { avatarUrl, userId, nickname, followed }, + } = detail; + const videoAuthorAvatarUrl = padPicCrop(avatarUrl, { x: 80, y: 80 }); + const videoAvatarStyle = `background-image:url(${videoAuthorAvatarUrl})`; + const publishTimeStr = getLocaleDate(publishTime); + const subscribeCountStr = getLocaleCount(subscribeCount); + const likedCountStr = getLocaleCount(likedCount); + const shareCountStr = getLocaleCount(shareCount); + return ( +
        +
        + + {nickname} + + +
        +

        + {title} +

        +

        + + 发布: + {publishTimeStr} + +

        +
        + + {videoGroup.map(({ id, name }) => { + return ( + toVideoTagListPage(id)}> + {name} + + ); + })} + +
        +
        + + collectVideoHandler(value)} + > + + 收藏{subscribeCountStr} + + praiseVideoHandler(value)} + > + + 赞{likedCountStr} + + + + 分享{shareCountStr} + + + + 下载 + + +
        +
        + ); + }; - return () => { - const [{url}] = videoUrlInfo.value; - const { detail } = videoData; - const hasVideoSourceValue = hasVideoSource.value; + return () => { + const [{ url }] = videoUrlInfo.value; + const { detail } = videoData; + const hasVideoSourceValue = hasVideoSource.value; - return
        -
        -
        - -
        + return ( +
        +
        +
        + +
        - { - hasVideoSourceValue && renderVideoOperator() - } - -
        -

        相关推荐

        - -
        -
        -
        + {hasVideoSourceValue && renderVideoOperator()} -
        -
        - }; - }, +
        +

        相关推荐

        + +
        +
        +
        +
        + ); + }; + }, }); diff --git a/src/database/audio.ts b/src/database/audio.ts index 693da49..e8d150a 100644 --- a/src/database/audio.ts +++ b/src/database/audio.ts @@ -1 +1,3 @@ -export {} \ No newline at end of file +/** @format */ + +export {}; diff --git a/src/database/database.d.ts b/src/database/database.d.ts index 8a2017e..c8d52f5 100644 --- a/src/database/database.d.ts +++ b/src/database/database.d.ts @@ -1,3 +1,7 @@ -import Dexie, { IndexableType } from "dexie"; +/** @format */ -export declare interface DbTable extends Dexie.Table, PlainObject { } \ No newline at end of file +import Dexie, { IndexableType } from 'dexie'; + +export declare interface DbTable + extends Dexie.Table, + PlainObject {} diff --git a/src/database/index.ts b/src/database/index.ts index 5223472..bd7ed91 100644 --- a/src/database/index.ts +++ b/src/database/index.ts @@ -1,2 +1,4 @@ +/** @format */ + export * from './player'; -export * from './audio'; \ No newline at end of file +export * from './audio'; diff --git a/src/database/player.ts b/src/database/player.ts index a045602..cff6cb5 100644 --- a/src/database/player.ts +++ b/src/database/player.ts @@ -1,32 +1,30 @@ -import Dexie from "dexie"; +/** @format */ + +import Dexie from 'dexie'; import { DbTable } from './database'; import { CurrentSongInfo } from '@utils/apiSpecial'; -import { EMPTY_ARR, EMPTY_OBJ, is, override } from "@/utils"; -import { toRaw } from "vue"; +import { EMPTY_ARR, EMPTY_OBJ, is, override } from '@/utils'; +import { toRaw } from 'vue'; export enum PlayerDBTables { - playerQueue = 'playerQueue', + playerQueue = 'playerQueue', } -const songInfos = "&id,albumName,duration,localedDuration,localedMark,mark,musicName,publishTime,starred,name,*alias,*singers,*artists,album.name"; +const songInfos = + '&id,albumName,duration,localedDuration,localedMark,mark,musicName,publishTime,starred,name,*alias,*singers,*artists,album.name'; class PlayerDatabase extends Dexie { - - public playQueue!: DbTable; - - constructor() { - super('PlayerDatabase'); - this.version(1).stores({ - playQueue: `${songInfos}`, - }) - this.playQueue.mapToClass(PlayerQueueMaster); - } - + public playQueue!: DbTable; + + constructor() { + super('PlayerDatabase'); + this.version(1).stores({ + playQueue: `${songInfos}`, + }); + this.playQueue.mapToClass(PlayerQueueMaster); + } } -export class PlayerQueueMaster { - - -} +export class PlayerQueueMaster {} export const playerDB = new PlayerDatabase(); playerDB.open(); @@ -35,32 +33,29 @@ export async function getOrPutPlayQueue(): Promise; export async function getOrPutPlayQueue(song: CurrentSongInfo): Promise; export async function getOrPutPlayQueue(song: CurrentSongInfo[]): Promise; export async function getOrPutPlayQueue(song?: CurrentSongInfo | CurrentSongInfo[]) { - let method = ''; - if (is.undefined(song)) { - method = 'toArray'; - } - else if (is.array(song)) { - method = 'bulkPut'; - } - else { - method = 'put'; - } - return await playerDB.playQueue[method](song && toRaw(song)); + let method = ''; + if (is.undefined(song)) { + method = 'toArray'; + } else if (is.array(song)) { + method = 'bulkPut'; + } else { + method = 'put'; + } + return await playerDB.playQueue[method](song && toRaw(song)); } export function getOrPutCurrentSong(): CurrentSongInfo | null; export function getOrPutCurrentSong(song: CurrentSongInfo): void; export function getOrPutCurrentSong(song?: CurrentSongInfo) { - if (song) { - return localStorage.setItem('currentSong', JSON.stringify(toRaw(song))); - } else { - return JSON.parse(localStorage.getItem('currentSong')!); - } - // const uniqueCurrentSong = (await playerDB.currentSong.toArray())[0]; - // return is.undefined(song) - // ? uniqueCurrentSong - // : uniqueCurrentSong - // ? await playerDB.currentSong.toCollection().modify(toRaw(song)) - // : await playerDB.currentSong.put(toRaw(song)); + if (song) { + return localStorage.setItem('currentSong', JSON.stringify(toRaw(song))); + } else { + return JSON.parse(localStorage.getItem('currentSong')!); + } + // const uniqueCurrentSong = (await playerDB.currentSong.toArray())[0]; + // return is.undefined(song) + // ? uniqueCurrentSong + // : uniqueCurrentSong + // ? await playerDB.currentSong.toCollection().modify(toRaw(song)) + // : await playerDB.currentSong.put(toRaw(song)); } - diff --git a/src/dependency/enum.ts b/src/dependency/enum.ts index 0edfe7d..6523e33 100644 --- a/src/dependency/enum.ts +++ b/src/dependency/enum.ts @@ -1,18 +1,20 @@ +/** @format */ + export enum CommentType { - // 歌曲 - music, - // mv - mv, - // 歌单 - songlist, - // 专辑 - album, - // 电台 - radio, - // 视频 - video, - // 动态 - dynamic, + // 歌曲 + music, + // mv + mv, + // 歌单 + songlist, + // 专辑 + album, + // 电台 + radio, + // 视频 + video, + // 动态 + dynamic, } /** @@ -24,14 +26,14 @@ export enum CommentType { * * @export * @enum {number} - * + * * fee 为 1 或 8 的歌曲均可单独购买 2 元单曲 */ export enum SongFee { - freeOrNoCopyright = 0, - vip = 1, - buyCD = 4, - weakness = 8 + freeOrNoCopyright = 0, + vip = 1, + buyCD = 4, + weakness = 8, } /** @@ -41,10 +43,10 @@ export enum SongFee { * @enum {number} */ export enum OriginCoverType { - // 未知 - unkown, - // 原曲 - origin, - // 翻唱 - cover, -} \ No newline at end of file + // 未知 + unkown, + // 原曲 + origin, + // 翻唱 + cover, +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index f1defe9..3ff50c1 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,3 +1,5 @@ -export * from "./useBetterFullscreen"; -export * from "./useTimeSlice"; -export * from "./useHowler"; +/** @format */ + +export * from './useBetterFullscreen'; +export * from './useTimeSlice'; +export * from './useHowler'; diff --git a/src/hooks/onBeforeInstallPrompt.ts b/src/hooks/onBeforeInstallPrompt.ts index 383482a..f7a3397 100644 --- a/src/hooks/onBeforeInstallPrompt.ts +++ b/src/hooks/onBeforeInstallPrompt.ts @@ -1,25 +1,26 @@ /** * 添加APP到主屏幕之前 + * + * @format */ -export default function onBeforeInstallPrompt(cb: ( - confirm: CommonFunction -) => void) { - navigator?.serviceWorker?.ready.then((res) => { - window.addEventListener('beforeinstallprompt', (ev) => { - // 防止 Chrome 67 及更早版本自动显示安装提示 - ev.preventDefault(); - const addToHomeScreen = () => { - ev.prompt(); - // 等待用户反馈 - ev.userChoice.then((choiceResult) => { - if (choiceResult.outcome === 'accepted') { - console.log('User accepted the A2HS prompt'); - } else { - console.log('User dismissed the A2HS prompt'); - } - }); - } - cb(addToHomeScreen); - }); - }) -} \ No newline at end of file + +export default function onBeforeInstallPrompt(cb: (confirm: CommonFunction) => void) { + navigator?.serviceWorker?.ready.then((res) => { + window.addEventListener('beforeinstallprompt', (ev) => { + // 防止 Chrome 67 及更早版本自动显示安装提示 + ev.preventDefault(); + const addToHomeScreen = () => { + ev.prompt(); + // 等待用户反馈 + ev.userChoice.then((choiceResult) => { + if (choiceResult.outcome === 'accepted') { + console.log('User accepted the A2HS prompt'); + } else { + console.log('User dismissed the A2HS prompt'); + } + }); + }; + cb(addToHomeScreen); + }); + }); +} diff --git a/src/hooks/onRouteHook.ts b/src/hooks/onRouteHook.ts index 3aa525c..87a6f57 100644 --- a/src/hooks/onRouteHook.ts +++ b/src/hooks/onRouteHook.ts @@ -1,53 +1,66 @@ -import { is, NOOP } from "@/utils"; -import { NavigationGuard, onBeforeRouteLeave, onBeforeRouteUpdate, RouteLocationNormalized } from "vue-router"; +/** @format */ + +import { is, NOOP } from '@/utils'; +import { + NavigationGuard, + onBeforeRouteLeave, + onBeforeRouteUpdate, + RouteLocationNormalized, +} from 'vue-router'; export enum RouteHookNames { - beforeLeave = 'beforeLeave', - beforeUpdate = 'beforeUpdate', + beforeLeave = 'beforeLeave', + beforeUpdate = 'beforeUpdate', } const ROUTE_HOOK_MAP = new Map void>(); ROUTE_HOOK_MAP.set(RouteHookNames.beforeLeave, onBeforeRouteLeave); ROUTE_HOOK_MAP.set(RouteHookNames.beforeUpdate, onBeforeRouteUpdate); -export type OnRouteHookGuard = (to: RouteLocationNormalized, from: RouteLocationNormalized) => void; -export type OnRouteHookFilter = (to: RouteLocationNormalized, from: RouteLocationNormalized) => boolean; +export type OnRouteHookGuard = ( + to: RouteLocationNormalized, + from: RouteLocationNormalized, +) => void; +export type OnRouteHookFilter = ( + to: RouteLocationNormalized, + from: RouteLocationNormalized, +) => boolean; /** * 带过滤验证的路由Composition钩子 - * @param hookName - * @param guard - * @param filter - * @returns + * @param hookName + * @param guard + * @param filter + * @returns */ export const onRouteHook = ( - hookName: RouteHookNames, - guard: OnRouteHookGuard, - filter: OnRouteHookFilter = NOOP, + hookName: RouteHookNames, + guard: OnRouteHookGuard, + filter: OnRouteHookFilter = NOOP, ) => { - if (!RouteHookNames[hookName]) { - if (import.meta.env.DEV) { - console.error(`The 'hookName' parameter of 'onRouteHook' function is invalid!`); - } - return; - } - const hookFn = ROUTE_HOOK_MAP.get(RouteHookNames[hookName])!; - hookFn((to, from, next) => { - filter(to, from) && guard(to, from); - next(); - }); -} + if (!RouteHookNames[hookName]) { + if (import.meta.env.DEV) { + console.error(`The 'hookName' parameter of 'onRouteHook' function is invalid!`); + } + return; + } + const hookFn = ROUTE_HOOK_MAP.get(RouteHookNames[hookName])!; + hookFn((to, from, next) => { + filter(to, from) && guard(to, from); + next(); + }); +}; -export const onFilteredBeforeRouteUpdate: (guard: OnRouteHookGuard) => void = (updateGuard) => { - onRouteHook( - RouteHookNames.beforeUpdate, - updateGuard, - ((to, from) => { - const newToQuery = { ...to.query }; - const newFromQuery = { ...from.query }; - Reflect.deleteProperty(newToQuery, 'playerStatus'); - Reflect.deleteProperty(newFromQuery, 'playerStatus'); - return JSON.stringify(newToQuery) !== JSON.stringify(newFromQuery) || to.path !== from.path; - }) - ) -} \ No newline at end of file +export const onFilteredBeforeRouteUpdate: (guard: OnRouteHookGuard) => void = ( + updateGuard, +) => { + onRouteHook(RouteHookNames.beforeUpdate, updateGuard, (to, from) => { + const newToQuery = { ...to.query }; + const newFromQuery = { ...from.query }; + Reflect.deleteProperty(newToQuery, 'playerStatus'); + Reflect.deleteProperty(newFromQuery, 'playerStatus'); + return ( + JSON.stringify(newToQuery) !== JSON.stringify(newFromQuery) || to.path !== from.path + ); + }); +}; diff --git a/src/hooks/useBetterFullscreen.ts b/src/hooks/useBetterFullscreen.ts index d46c456..b851820 100644 --- a/src/hooks/useBetterFullscreen.ts +++ b/src/hooks/useBetterFullscreen.ts @@ -1,12 +1,14 @@ +/** @format */ + import { - onKeyDown, - onKeyUp, - useFullscreen, - useMagicKeys, - MaybeElementRef, -} from "@vueuse/core"; -import { onMounted, ref, watchEffect, watch } from "@vue/runtime-core"; -import { EMPTY_OBJ } from "@/utils"; + onKeyDown, + onKeyUp, + useFullscreen, + useMagicKeys, + MaybeElementRef, +} from '@vueuse/core'; +import { onMounted, ref, watchEffect, watch } from '@vue/runtime-core'; +import { EMPTY_OBJ } from '@/utils'; /** * 使用全屏方法(基于vueuse的useFullscreen,额外做了F11优化) @@ -15,46 +17,45 @@ import { EMPTY_OBJ } from "@/utils"; * @returns */ export function useBetterFullscreen( - target?: MaybeElementRef, - options?: { - F11Interval?: number; //F11节流间隔 - } + target?: MaybeElementRef, + options?: { + F11Interval?: number; //F11节流间隔 + }, ) { - const { F11Interval = 500 } = options || EMPTY_OBJ; - //默认监听document - const { isSupported, enter, exit, toggle, isFullscreen } = - useFullscreen(target); - const F11 = "F11"; + const { F11Interval = 500 } = options || EMPTY_OBJ; + //默认监听document + const { isSupported, enter, exit, toggle, isFullscreen } = useFullscreen(target); + const F11 = 'F11'; - //清除F11默认进入全屏行为,因为js无法监听其变化 - onKeyDown(F11, (ev: KeyboardEvent) => { - ev.preventDefault(); - }); + //清除F11默认进入全屏行为,因为js无法监听其变化 + onKeyDown(F11, (ev: KeyboardEvent) => { + ev.preventDefault(); + }); - let prev = 0; - //按F11时,切换全屏 - onKeyUp( - F11, - (ev: KeyboardEvent) => { - const now = performance.now(); - //之所以这里设置F11触发间隔,是为了解决连续快速按F11出现的无法执行进入全屏方法的报错信息 - if (now - prev >= F11Interval) { - toggle(); - prev = now; - } - }, - { - //设置F11的keyup事件触发对象为html(默认为window),以解决如下报错: - // Failed to execute 'requestFullScreen' on 'Element': API can only be initiated by a user gesture. - target: document.documentElement, - } - ); + let prev = 0; + //按F11时,切换全屏 + onKeyUp( + F11, + (ev: KeyboardEvent) => { + const now = performance.now(); + //之所以这里设置F11触发间隔,是为了解决连续快速按F11出现的无法执行进入全屏方法的报错信息 + if (now - prev >= F11Interval) { + toggle(); + prev = now; + } + }, + { + //设置F11的keyup事件触发对象为html(默认为window),以解决如下报错: + // Failed to execute 'requestFullScreen' on 'Element': API can only be initiated by a user gesture. + target: document.documentElement, + }, + ); - return { - isSupported, - toggle, - isFullscreen, - enter, - exit, - }; + return { + isSupported, + toggle, + isFullscreen, + enter, + exit, + }; } diff --git a/src/hooks/useChildren.ts b/src/hooks/useChildren.ts index dd43aa6..792fc83 100644 --- a/src/hooks/useChildren.ts +++ b/src/hooks/useChildren.ts @@ -1,99 +1,104 @@ +/** @format */ + import { - VNode, - isVNode, - provide, - reactive, - InjectionKey, - getCurrentInstance, - VNodeNormalizedChildren, - ComponentPublicInstance, - ComponentInternalInstance, + VNode, + isVNode, + provide, + reactive, + InjectionKey, + getCurrentInstance, + VNodeNormalizedChildren, + ComponentPublicInstance, + ComponentInternalInstance, } from 'vue'; export function flattenVNodes(children: VNodeNormalizedChildren) { - const result: VNode[] = []; + const result: VNode[] = []; - const traverse = (children: VNodeNormalizedChildren) => { - if (Array.isArray(children)) { - children.forEach((child) => { - if (isVNode(child)) { - result.push(child); + const traverse = (children: VNodeNormalizedChildren) => { + if (Array.isArray(children)) { + children.forEach((child) => { + if (isVNode(child)) { + result.push(child); - if (child.component?.subTree) { - traverse(child.component.subTree.children); - } + if (child.component?.subTree) { + traverse(child.component.subTree.children); + } - if (child.children) { - traverse(child.children); - } - } - }); - } - }; + if (child.children) { + traverse(child.children); + } + } + }); + } + }; - traverse(children); + traverse(children); - return result; + return result; } // sort children instances by vnodes order export function sortChildren( - parent: ComponentInternalInstance, - publicChildren: ComponentPublicInstance[], - internalChildren: ComponentInternalInstance[] + parent: ComponentInternalInstance, + publicChildren: ComponentPublicInstance[], + internalChildren: ComponentInternalInstance[], ) { - const vnodes = flattenVNodes(parent.subTree.children); + const vnodes = flattenVNodes(parent.subTree.children); - internalChildren.sort( - (a, b) => vnodes.indexOf(a.vnode) - vnodes.indexOf(b.vnode) - ); + internalChildren.sort((a, b) => vnodes.indexOf(a.vnode) - vnodes.indexOf(b.vnode)); - const orderedPublicChildren = internalChildren.map((item) => item.proxy!); + const orderedPublicChildren = internalChildren.map((item) => item.proxy!); - publicChildren.sort((a, b) => { - const indexA = orderedPublicChildren.indexOf(a); - const indexB = orderedPublicChildren.indexOf(b); - return indexA - indexB; - }); + publicChildren.sort((a, b) => { + const indexA = orderedPublicChildren.indexOf(a); + const indexB = orderedPublicChildren.indexOf(b); + return indexA - indexB; + }); } - export const useChildren = < - Child extends ComponentPublicInstance = ComponentPublicInstance, any>, - ProvideValue = never ->(key: InjectionKey) => { - - const publicChildren: Child[] = reactive([]); - const internalChildren: ComponentInternalInstance[] = reactive([]); - const parent = getCurrentInstance()!; - - const linkChildren = (value?: ProvideValue) => { - const link = (child: ComponentInternalInstance) => { - if (child.proxy) { - internalChildren.push(child); - publicChildren.push(child.proxy as Child); - sortChildren(parent, publicChildren, internalChildren); - } - } - const unlink = (child: ComponentInternalInstance) => { - const index = internalChildren.indexOf(child); - publicChildren.splice(index, 1); - internalChildren.splice(index, 1); - } - provide(key, Object.assign( - { - link, - unlink, - children: publicChildren, - internalChildren - }, - value - )) - } - - return { - children: publicChildren, - linkChildren, - } - -} \ No newline at end of file + Child extends ComponentPublicInstance = ComponentPublicInstance< + Record, + any + >, + ProvideValue = never, +>( + key: InjectionKey, +) => { + const publicChildren: Child[] = reactive([]); + const internalChildren: ComponentInternalInstance[] = reactive([]); + const parent = getCurrentInstance()!; + + const linkChildren = (value?: ProvideValue) => { + const link = (child: ComponentInternalInstance) => { + if (child.proxy) { + internalChildren.push(child); + publicChildren.push(child.proxy as Child); + sortChildren(parent, publicChildren, internalChildren); + } + }; + const unlink = (child: ComponentInternalInstance) => { + const index = internalChildren.indexOf(child); + publicChildren.splice(index, 1); + internalChildren.splice(index, 1); + }; + provide( + key, + Object.assign( + { + link, + unlink, + children: publicChildren, + internalChildren, + }, + value, + ), + ); + }; + + return { + children: publicChildren, + linkChildren, + }; +}; diff --git a/src/hooks/useHowler.ts b/src/hooks/useHowler.ts index baa37de..285a2ac 100644 --- a/src/hooks/useHowler.ts +++ b/src/hooks/useHowler.ts @@ -1,13 +1,13 @@ -import { - customRef, -} from "vue"; +/** @format */ + +import { customRef } from 'vue'; //类型声明需安装: npm i @types/howler -import { Howl, Howler, HowlErrorCallback, HowlOptions } from "howler"; -import { MaybeRef } from "@vueuse/core"; -import { is } from "@/utils"; +import { Howl, Howler, HowlErrorCallback, HowlOptions } from 'howler'; +import { MaybeRef } from '@vueuse/core'; +import { is } from '@/utils'; -export type HowlerStateType = "loaded" | "unloaded" | "loading"; +export type HowlerStateType = 'loaded' | 'unloaded' | 'loading'; //自动尝试在移动端和浏览器桌面端播放音频 Howler.autoUnlock = false; @@ -20,166 +20,165 @@ Howler.autoSuspend = true; * @returns */ -export const defaultHowlOptions: HowlOptions = { - autoplay: false, - html5: true, - format: ["mp3", "mpeg", "flac", "wav", "webm", "m4a", "ogg", "opus", "aac"], - xhr: { - withCredentials: true, - }, +export const defaultHowlOptions: Partial = { + autoplay: false, + html5: true, + format: ['mp3', 'mpeg', 'flac', 'wav', 'webm', 'm4a', 'ogg', 'opus', 'aac'], + xhr: { + withCredentials: true, + }, }; - const useHowler = (() => { - let howl: Howl; - const eventFuncQueue: CommonFunction[] = []; - const eventFuncOnceQueue: CommonFunction[] = []; - return (baseOptions: HowlOptions = defaultHowlOptions) => { - const playSound = ( - options: HowlOptions = defaultHowlOptions - ) => { - //先销毁前面的实例 - Howler.unload(); - const realHowlOptions = { - ...baseOptions, - ...options, - } - howl = new Howl(realHowlOptions); - if (!is.emptyArray(eventFuncOnceQueue)) { - eventFuncOnceQueue.forEach(func => func()); - eventFuncOnceQueue.length = 0; - } - eventFuncQueue.forEach((func) => func()); - - //解决某些浏览器自动播放失效问题 - if (realHowlOptions.autoplay) { - howl.on("playerror", () => { - howl.once("unlock", () => howl.play()); - }); - } - }; - - const muteRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.volume() === 0; - }, - set(value) { - howl?.mute(value); - trigger(); - }, - })); - - const loopRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.loop(); - }, - set(value) { - howl?.loop(value); - trigger(); - }, - })); - - const currentTimeRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.seek() as number; - }, - set(value) { - const fn = () => howl.seek(value); - !howl ? eventFuncOnceQueue.push(fn) : fn(); - trigger(); - }, - })); - - const volumeRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.volume(); - }, - set(value) { - if (value < 0) value = 0; - else if (value > 1) value = 1; - howl?.volume(value); - trigger(); - }, - })); - - const rateRef = customRef((track, trigger) => ({ - get() { - track(); - return howl.rate(); - }, - set(value) { - if (value < 0.5) value = 0.5; - else if (value > 4) value = 4; - howl?.rate(value); - trigger(); - }, - })); - - const durationRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.duration(); - }, - set(value) { - // trigger(); - }, - })); - - const stateRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.state(); - }, - set(value) { - // trigger(); - }, - })); - - const playingRef = customRef((track, trigger) => ({ - get() { - track(); - return howl?.playing(); - }, - set(value) { - howl?.[value ? 'play' : 'pause'](); - trigger(); - }, - })); - - return { - volume: volumeRef, - rate: rateRef, - currentTime: currentTimeRef, - loop: loopRef, - mute: muteRef, - duration: durationRef, - playing: playingRef, - state: stateRef, - - playSound, - load: () => howl?.load(), - unload: () => howl?.unload(), - on: (...args: FuncParamsType) => { - eventFuncQueue.push(() => { - howl.on(...args); - }); - }, - once: (...args: FuncParamsType) => { - eventFuncQueue.push(() => { - howl.once(...args); - }); - }, - off: (...args: FuncParamsType) => howl?.off(...args), - fade: (...args: FuncParamsType) => howl?.fade(...args), - stop: (...args: FuncParamsType) => howl?.stop(...args), - play: (...args: FuncParamsType) => howl?.play(...args), - pause: (...args: FuncParamsType) => howl?.pause(...args), - }; - }; + let howl: Howl; + const eventFuncQueue: CommonFunction[] = []; + const eventFuncOnceQueue: CommonFunction[] = []; + return (baseOptions: HowlOptions = defaultHowlOptions as Required) => { + const playSound = ( + options: HowlOptions = defaultHowlOptions as Required, + ) => { + //先销毁前面的实例 + Howler.unload(); + const realHowlOptions = { + ...baseOptions, + ...options, + }; + howl = new Howl(realHowlOptions); + if (!is.emptyArray(eventFuncOnceQueue)) { + eventFuncOnceQueue.forEach((func) => func()); + eventFuncOnceQueue.length = 0; + } + eventFuncQueue.forEach((func) => func()); + + //解决某些浏览器自动播放失效问题 + if (realHowlOptions.autoplay) { + howl.on('playerror', () => { + howl.once('unlock', () => howl.play()); + }); + } + }; + + const muteRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.volume() === 0; + }, + set(value) { + howl?.mute(value); + trigger(); + }, + })); + + const loopRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.loop(); + }, + set(value) { + howl?.loop(value); + trigger(); + }, + })); + + const currentTimeRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.seek() as number; + }, + set(value) { + const fn = () => howl.seek(value); + !howl ? eventFuncOnceQueue.push(fn) : fn(); + trigger(); + }, + })); + + const volumeRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.volume(); + }, + set(value) { + if (value < 0) value = 0; + else if (value > 1) value = 1; + howl?.volume(value); + trigger(); + }, + })); + + const rateRef = customRef((track, trigger) => ({ + get() { + track(); + return howl.rate(); + }, + set(value) { + if (value < 0.5) value = 0.5; + else if (value > 4) value = 4; + howl?.rate(value); + trigger(); + }, + })); + + const durationRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.duration(); + }, + set(value) { + // trigger(); + }, + })); + + const stateRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.state(); + }, + set(value) { + // trigger(); + }, + })); + + const playingRef = customRef((track, trigger) => ({ + get() { + track(); + return howl?.playing(); + }, + set(value) { + howl?.[value ? 'play' : 'pause'](); + trigger(); + }, + })); + + return { + volume: volumeRef, + rate: rateRef, + currentTime: currentTimeRef, + loop: loopRef, + mute: muteRef, + duration: durationRef, + playing: playingRef, + state: stateRef, + + playSound, + load: () => howl?.load(), + unload: () => howl?.unload(), + on: (...args: FuncParamsType) => { + eventFuncQueue.push(() => { + howl.on(...args); + }); + }, + once: (...args: FuncParamsType) => { + eventFuncQueue.push(() => { + howl.once(...args); + }); + }, + off: (...args: FuncParamsType) => howl?.off(...args), + fade: (...args: FuncParamsType) => howl?.fade(...args), + stop: (...args: FuncParamsType) => howl?.stop(...args), + play: (...args: FuncParamsType) => howl?.play(...args), + pause: (...args: FuncParamsType) => howl?.pause(...args), + }; + }; })(); export default useHowler; diff --git a/src/hooks/useParent.ts b/src/hooks/useParent.ts index ee28ea9..f96d1bc 100644 --- a/src/hooks/useParent.ts +++ b/src/hooks/useParent.ts @@ -1,33 +1,40 @@ -import { ComponentInternalInstance, ComponentPublicInstance, computed, getCurrentInstance, inject, InjectionKey, onUnmounted, ref } from "vue"; - - +/** @format */ + +import { + ComponentInternalInstance, + ComponentPublicInstance, + computed, + getCurrentInstance, + inject, + InjectionKey, + onUnmounted, + ref, +} from 'vue'; export type ParentProvide = T & { - link: (child: ComponentInternalInstance) => void; - unlink: (child: ComponentInternalInstance) => void; - children: ComponentPublicInstance[]; - internalChildren: ComponentInternalInstance[]; -} - -export const useParent = (key: InjectionKey> ) => { - - const parent = inject(key, null); - if(parent) { - const instance = getCurrentInstance()!; - const { link , unlink , internalChildren } = parent; - link(instance); - onUnmounted(() => unlink(instance)); - const index = computed(() => internalChildren.indexOf(instance)); - - return { - parent, - index - } - } - - return { - parent: null, - index: ref(-1), - } - -} + link: (child: ComponentInternalInstance) => void; + unlink: (child: ComponentInternalInstance) => void; + children: ComponentPublicInstance[]; + internalChildren: ComponentInternalInstance[]; +}; + +export const useParent = (key: InjectionKey>) => { + const parent = inject(key, null); + if (parent) { + const instance = getCurrentInstance()!; + const { link, unlink, internalChildren } = parent; + link(instance); + onUnmounted(() => unlink(instance)); + const index = computed(() => internalChildren.indexOf(instance)); + + return { + parent, + index, + }; + } + + return { + parent: null, + index: ref(-1), + }; +}; diff --git a/src/hooks/useTimeSlice.ts b/src/hooks/useTimeSlice.ts index f7c7fe0..4e6e46e 100644 --- a/src/hooks/useTimeSlice.ts +++ b/src/hooks/useTimeSlice.ts @@ -1,12 +1,14 @@ +/** @format */ + import { - onKeyDown, - onKeyUp, - useFullscreen, - useMagicKeys, - MaybeElementRef, -} from "@vueuse/core"; -import { onMounted, ref, watchEffect, watch } from "@vue/runtime-core"; -import { EMPTY_OBJ } from "@/utils"; + onKeyDown, + onKeyUp, + useFullscreen, + useMagicKeys, + MaybeElementRef, +} from '@vueuse/core'; +import { onMounted, ref, watchEffect, watch } from '@vue/runtime-core'; +import { EMPTY_OBJ } from '@/utils'; /** * 时间切片(分块渲染) @@ -14,23 +16,23 @@ import { EMPTY_OBJ } from "@/utils"; * @returns (p: number) => void */ export function useTimeSlice(upperLimit = 10) { - const showPriority = ref(0); - const runShowPriority = () => { - const step = () => { - requestAnimationFrame(() => { - showPriority.value++; - if (showPriority.value < upperLimit) { - step(); - } - }); - }; - step(); - }; - onMounted(() => { - runShowPriority(); - }); + const showPriority = ref(0); + const runShowPriority = () => { + const step = () => { + requestAnimationFrame(() => { + showPriority.value++; + if (showPriority.value < upperLimit) { + step(); + } + }); + }; + step(); + }; + onMounted(() => { + runShowPriority(); + }); - return (priority: number) => { - showPriority.value >= priority; - }; + return (priority: number) => { + showPriority.value >= priority; + }; } diff --git a/src/main.ts b/src/main.ts index a008d59..93eb543 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,48 +1,44 @@ -import { createApp } from "vue"; +/** @format */ + +import { createApp } from 'vue'; import { createPinia } from 'pinia'; -import routerObj from "@router/index"; -import YuanPlayer from "./App"; +import routerObj from '@router/index'; +import YuanPlayer from './App'; import '@stores/initStore'; -import "@scss/main.scss"; -import "swiper/css"; -import "swiper/css/effect-coverflow"; +import '@scss/main.scss'; +import 'swiper/css'; +import 'swiper/css/effect-coverflow'; import SwiperCore, { - EffectCoverflow, - Pagination, - Navigation, - Autoplay, - Lazy, - Mousewheel, -} from "swiper"; -import { registerSW } from 'virtual:pwa-register' -import "@/webComponents/index"; + EffectCoverflow, + Pagination, + Navigation, + Autoplay, + Lazy, + Mousewheel, +} from 'swiper'; +import { registerSW } from 'virtual:pwa-register'; +import '@/webComponents/index'; //自动更新间隔:3小时 const intervalMS = 3 * 60 * 60 * 1000; const updateSW = registerSW({ - onRegistered(r) { - r && setInterval(() => { - r.update() - }, intervalMS) - }, - onNeedRefresh() { }, - onOfflineReady() { }, - onRegisterError(error) { }, -}) + onRegistered(r) { + r && + setInterval(() => { + r.update(); + }, intervalMS); + }, + onNeedRefresh() {}, + onOfflineReady() {}, + onRegisterError(error) {}, +}); -SwiperCore.use([ - Pagination, - EffectCoverflow, - Navigation, - Autoplay, - Lazy, - Mousewheel, -]); +SwiperCore.use([Pagination, EffectCoverflow, Navigation, Autoplay, Lazy, Mousewheel]); //创建app根实例 -const vueApp = createApp(YuanPlayer); +const vueApp = createApp(YuanPlayer); vueApp.use(createPinia()); vueApp.use(routerObj); //挂在实例 -vueApp.mount("#Yuan-Player"); +vueApp.mount('#Yuan-Player'); diff --git a/src/request/index.ts b/src/request/index.ts index 7d06c8d..7cca79b 100644 --- a/src/request/index.ts +++ b/src/request/index.ts @@ -1,32 +1,37 @@ -import ryoko, { abortPendingRequest, InterceptorCtor, RyokoResponseType } from "ryoko"; +/** @format */ + +import ryoko, { abortPendingRequest, InterceptorCtor, RyokoResponseType } from 'ryoko'; import type { RyokoClass } from 'ryoko'; -import { messageBus } from "@/utils/event/register"; -import { loginCookie } from "@/utils/auth"; +import { messageBus } from '@/utils/event/register'; +import { loginCookie } from '@/utils/auth'; export function createRequestInstance(responseType: RyokoResponseType = 'json') { - const ins = ryoko.create({ - mode: 'cors', - prefixUrl: import.meta.env.VITE_APP_PREFIX_URL as string, - timeout: 15000, - cache: "force-cache", - responseType, - // 允许跨域访问自动携带cookie - credentials: 'include', - // params: import.meta.env.DEV ? {} : { realIP: '116.25.146.177' }, - onDefer(deferMsg) { }, - verifyStatus: (status) => status >= 200 && status < 500, - }); - ins.interceptors.request.use((config) => { - // const MUSIC_C_COOKIE = loginCookie.value; - // if (MUSIC_C_COOKIE) { - // Reflect.set(config.params as PlainObject, 'cookie', MUSIC_C_COOKIE) - // } - return config; - }); - ins.interceptors.response.use((res) => res.data, (err: any) => { - console.info(err.message, '请求出错啦~~') - }); - return ins; + const ins = ryoko.create({ + mode: 'cors', + prefixUrl: import.meta.env.VITE_APP_PREFIX_URL as string, + timeout: 15000, + cache: 'force-cache', + responseType, + // 允许跨域访问自动携带cookie + credentials: 'include', + // params: import.meta.env.DEV ? {} : { realIP: '116.25.146.177' }, + onDefer(deferMsg) {}, + verifyStatus: (status) => status >= 200 && status < 500, + }); + ins.interceptors.request.use((config) => { + // const MUSIC_C_COOKIE = loginCookie.value; + // if (MUSIC_C_COOKIE) { + // Reflect.set(config.params as PlainObject, 'cookie', MUSIC_C_COOKIE) + // } + return config; + }); + ins.interceptors.response.use( + (res) => res.data, + (err: any) => { + console.info(err.message, '请求出错啦~~'); + }, + ); + return ins; } export const anfrage = createRequestInstance(); @@ -37,28 +42,30 @@ useLoadingMixin(anfrageWithLoading.interceptors); let requestCount = 0; let isLoadingFailure = false; function useLoadingMixin(interceptors: RyokoClass['interceptors']) { - interceptors.request.use(config => { - if (requestCount === 0) { - messageBus.dispatch('startLoading'); - } - requestCount++; - return config; - }); + interceptors.request.use((config) => { + if (requestCount === 0) { + messageBus.dispatch('startLoading'); + } + requestCount++; + return config; + }); - const handleResLoadingClose = () => { - if (--requestCount === 0) { - messageBus.dispatch(isLoadingFailure ? 'errorLoading' : 'finishLoading'); - isLoadingFailure = false; - } - } + const handleResLoadingClose = () => { + if (--requestCount === 0) { + messageBus.dispatch(isLoadingFailure ? 'errorLoading' : 'finishLoading'); + isLoadingFailure = false; + } + }; - interceptors.response.use(resData => { - handleResLoadingClose(); - return resData; - }, err => { - isLoadingFailure = true; - handleResLoadingClose(); - throw err; - }) + interceptors.response.use( + (resData) => { + handleResLoadingClose(); + return resData; + }, + (err) => { + isLoadingFailure = true; + handleResLoadingClose(); + throw err; + }, + ); } - diff --git a/src/router/index.ts b/src/router/index.ts index 63850b5..9232f42 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,48 +1,50 @@ -import { nextTick } from "vue"; +/** @format */ + +import { nextTick } from 'vue'; import { - createRouter, - createWebHistory, - createWebHashHistory, - RouteRecord, - RouterScrollBehavior, - RouteLocationNormalized, -} from "vue-router"; + createRouter, + createWebHistory, + createWebHashHistory, + RouteRecord, + RouterScrollBehavior, + RouteLocationNormalized, +} from 'vue-router'; import routes from './routes'; const routerObj = createRouter({ - // scrollBehavior(to, from, savedPosition) { - // if (savedPosition == null) { - // savedPosition = { left: 0, top: 0 } - // } - // const realPosition: ReturnType = { - // ...savedPosition, - // el: document.querySelector('.yplayer-home-main') as Element, - // behavior: 'smooth', - // } - // return realPosition; - // }, - // history: createWebHashHistory(), - history: createWebHashHistory(), - linkActiveClass: "active-link", - linkExactActiveClass: "exact-active-link", - routes, + // scrollBehavior(to, from, savedPosition) { + // if (savedPosition == null) { + // savedPosition = { left: 0, top: 0 } + // } + // const realPosition: ReturnType = { + // ...savedPosition, + // el: document.querySelector('.yplayer-home-main') as Element, + // behavior: 'smooth', + // } + // return realPosition; + // }, + // history: createWebHashHistory(), + history: createWebHashHistory(), + linkActiveClass: 'active-link', + linkExactActiveClass: 'exact-active-link', + routes, }); const scrollToTopQueries = ['limit', 'offset']; function handlerScrollToTop(to: RouteLocationNormalized, from: RouteLocationNormalized) { - const { query: toQuery } = to; - const { query: fromQuery } = from; - if (scrollToTopQueries.some(q => toQuery[q] !== fromQuery[q])) { - requestAnimationFrame(() => { - const homeMainElm = document.querySelector(".player-container"); - homeMainElm && (homeMainElm.scrollTop = 0); - }); - } + const { query: toQuery } = to; + const { query: fromQuery } = from; + if (scrollToTopQueries.some((q) => toQuery[q] !== fromQuery[q])) { + requestAnimationFrame(() => { + const homeMainElm = document.querySelector('.player-container'); + homeMainElm && (homeMainElm.scrollTop = 0); + }); + } } routerObj.beforeEach((to, from, next) => { - handlerScrollToTop(to, from); - next(); + handlerScrollToTop(to, from); + next(); }); export default routerObj; diff --git a/src/router/routes.ts b/src/router/routes.ts index fd83397..cd5f9e5 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -1,14 +1,15 @@ +/** @format */ -const Home = () => import("@views/home"); +const Home = () => import('@views/home'); -const MusicHall = () => import("@components/music-hall"); -const MusicHallFeatured = () => import("@components/music-hall/featured"); -const MusicHallTop = () => import("@components/music-hall/top"); -const MusicHallNewest = () => import("@components/music-hall/newestmusic"); -const MusicHallSonglist = () => import("@components/music-hall/songlist"); -const MusicHallArtist = () => import("@components/music-hall/artist"); -const MusicHallNewdisc = () => import("@components/music-hall/newestdisc"); -const MusicRadio = () => import("@components/music-radio"); +const MusicHall = () => import('@components/music-hall'); +const MusicHallFeatured = () => import('@components/music-hall/featured'); +const MusicHallTop = () => import('@components/music-hall/top'); +const MusicHallNewest = () => import('@components/music-hall/newestmusic'); +const MusicHallSonglist = () => import('@components/music-hall/songlist'); +const MusicHallArtist = () => import('@components/music-hall/artist'); +const MusicHallNewdisc = () => import('@components/music-hall/newestdisc'); +const MusicRadio = () => import('@components/music-radio'); const OnlineVideo = () => import('@components/online-video'); @@ -58,275 +59,273 @@ const PersonalRecommend = () => import('@components/personal-recommend'); const MyPage = () => import('@components/my-page'); -const SongPage = () => import("@components/song-page"); -const SongComment = () => import("@components/song-page/comment"); -const SongDetail = () => import("@components/song-page/detail"); +const SongPage = () => import('@components/song-page'); +const SongComment = () => import('@components/song-page/comment'); +const SongDetail = () => import('@components/song-page/detail'); -import { RouteLocation, RouteRecordRaw } from "vue-router"; +import { RouteLocation, RouteRecordRaw } from 'vue-router'; const routes: RouteRecordRaw[] = [ - - { - path: "/", - redirect: "/musichall", - component: Home, - props: {}, - beforeEnter() { }, - meta: {}, - children: [ - { - path: "musichall", - redirect: "/musichall/featrued", - component: MusicHall, - children: [ - { - path: "featrued", - component: MusicHallFeatured, - }, - { - path: "top", - component: MusicHallTop, - }, - { - path: "newestmusic", - component: MusicHallNewest, - }, - { - path: "newestdisc", - component: MusicHallNewdisc, - }, - { - path: "songlist", - component: MusicHallSonglist, - }, - { - path: "artist", - component: MusicHallArtist, - }, - ], - }, - - { - path: "musicradio", - component: MusicRadio, - }, - - { - path: "onlinevideo", - redirect: "/onlinevideo/all", - component: OnlineVideo, - children: [ - { - path: 'all', - component: VideoAll, - }, - { - path: 'category', - component: VideoCategory, - }, - ] - }, - - { - path: "setting", - redirect: '/setting/general', - component: Setting, - children: [ - { - path: 'general', - component: SettingGeneral, - } - ] - }, - - { - path: 'personalRecommend', - component: PersonalRecommend, - }, - - { - path: "artist", - redirect: "/artist/featured", - component: Artist, - children: [ - { - path: "featured", - component: ArtistFeatured, - }, - - { - path: "allSongs", - component: ArtistAllSongs, - }, - - { - path: "album", - component: ArtistAlbum, - }, - - { - path: "mv", - component: ArtistMv, - }, - - { - path: "similarSinger", - component: ArtistSimiSinger, - }, - - { - path: "description", - component: ArtistDesc, - }, - ], - }, - - { - path: "songlist/:id", - name: 'songlist', - redirect: (to: RouteLocation) => { - return { - path: 'songlist/:id/music', - name: 'songlistMusic', - params: { - id: to.params.id - } - } - }, - component: Songlist, - children: [ - { - path: "music", - name: 'songlistMusic', - component: SonglistMusic, - }, - - { - path: "comments", - name: 'songlistComments', - component: SonglistComment, - }, - - { - path: "subscribers", - name: 'songlistSubscribers', - component: SonglistSubscriber, - }, - ], - }, - - { - path: "user", - redirect: "/user/playRecord", - component: User, - children: [ - { - path: "playRecord", - component: UserPlayRecord, - }, - { - path: "collection", - component: UserCollection, - }, - { - path: "songlist", - component: UserSonglist, - }, - ], - }, - - { - path: "search", - redirect: "/search/songs", - component: Search, - children: [ - { - path: "songs", - component: SearchSongs, - }, - { - path: "album", - component: SearchAlbum, - }, - { - path: "lyric", - component: SearchLyric, - }, - { - path: "radio", - component: SearchRadio, - }, - { - path: "video", - component: SearchVideo, - }, - { - path: "mv", - component: SearchMv, - }, - { - path: "user", - component: SearchUser, - }, - { - path: "songlist", - component: SearchSonglist, - }, - { - path: "singer", - component: SearchSinger, - }, - ], - }, - - { - path: 'mv', - component: Mv - }, - - { - path: 'video', - component: Video - }, - - { - path: 'album', - component: Album - }, - - { - path: "/myPage", - component: MyPage, - }, - - { - path: "song/:id", - component: SongPage, - redirect: (to: RouteLocation) => { - return { - path: 'song/:id/comment', - name: 'SongComment', - params: { - id: to.params.id - } - } - }, - children: [ - { - path: "comment", - name: 'SongComment', - component: SongComment, - }, - { - path: "detail", - name: 'SongDetail', - component: SongDetail, - } - ] - } - - ], - } + { + path: '/', + redirect: '/musichall', + component: Home, + props: {}, + beforeEnter() {}, + meta: {}, + children: [ + { + path: 'musichall', + redirect: '/musichall/featrued', + component: MusicHall, + children: [ + { + path: 'featrued', + component: MusicHallFeatured, + }, + { + path: 'top', + component: MusicHallTop, + }, + { + path: 'newestmusic', + component: MusicHallNewest, + }, + { + path: 'newestdisc', + component: MusicHallNewdisc, + }, + { + path: 'songlist', + component: MusicHallSonglist, + }, + { + path: 'artist', + component: MusicHallArtist, + }, + ], + }, + + { + path: 'musicradio', + component: MusicRadio, + }, + + { + path: 'onlinevideo', + redirect: '/onlinevideo/all', + component: OnlineVideo, + children: [ + { + path: 'all', + component: VideoAll, + }, + { + path: 'category', + component: VideoCategory, + }, + ], + }, + + { + path: 'setting', + redirect: '/setting/general', + component: Setting, + children: [ + { + path: 'general', + component: SettingGeneral, + }, + ], + }, + + { + path: 'personalRecommend', + component: PersonalRecommend, + }, + + { + path: 'artist', + redirect: '/artist/featured', + component: Artist, + children: [ + { + path: 'featured', + component: ArtistFeatured, + }, + + { + path: 'allSongs', + component: ArtistAllSongs, + }, + + { + path: 'album', + component: ArtistAlbum, + }, + + { + path: 'mv', + component: ArtistMv, + }, + + { + path: 'similarSinger', + component: ArtistSimiSinger, + }, + + { + path: 'description', + component: ArtistDesc, + }, + ], + }, + + { + path: 'songlist/:id', + name: 'songlist', + redirect: (to: RouteLocation) => { + return { + path: 'songlist/:id/music', + name: 'songlistMusic', + params: { + id: to.params.id, + }, + }; + }, + component: Songlist, + children: [ + { + path: 'music', + name: 'songlistMusic', + component: SonglistMusic, + }, + + { + path: 'comments', + name: 'songlistComments', + component: SonglistComment, + }, + + { + path: 'subscribers', + name: 'songlistSubscribers', + component: SonglistSubscriber, + }, + ], + }, + + { + path: 'user', + redirect: '/user/playRecord', + component: User, + children: [ + { + path: 'playRecord', + component: UserPlayRecord, + }, + { + path: 'collection', + component: UserCollection, + }, + { + path: 'songlist', + component: UserSonglist, + }, + ], + }, + + { + path: 'search', + redirect: '/search/songs', + component: Search, + children: [ + { + path: 'songs', + component: SearchSongs, + }, + { + path: 'album', + component: SearchAlbum, + }, + { + path: 'lyric', + component: SearchLyric, + }, + { + path: 'radio', + component: SearchRadio, + }, + { + path: 'video', + component: SearchVideo, + }, + { + path: 'mv', + component: SearchMv, + }, + { + path: 'user', + component: SearchUser, + }, + { + path: 'songlist', + component: SearchSonglist, + }, + { + path: 'singer', + component: SearchSinger, + }, + ], + }, + + { + path: 'mv', + component: Mv, + }, + + { + path: 'video', + component: Video, + }, + + { + path: 'album', + component: Album, + }, + + { + path: '/myPage', + component: MyPage, + }, + + { + path: 'song/:id', + component: SongPage, + redirect: (to: RouteLocation) => { + return { + path: 'song/:id/comment', + name: 'SongComment', + params: { + id: to.params.id, + }, + }; + }, + children: [ + { + path: 'comment', + name: 'SongComment', + component: SongComment, + }, + { + path: 'detail', + name: 'SongDetail', + component: SongDetail, + }, + ], + }, + ], + }, ]; export default routes; diff --git a/src/scss/common.scss b/src/scss/common.scss index 87ff431..bbe6e42 100644 --- a/src/scss/common.scss +++ b/src/scss/common.scss @@ -1,8 +1,10 @@ -@use "sass:math"; -@import "variable"; +/** @format */ + +@use 'sass:math'; +@import 'variable'; * { - box-sizing: border-box; + box-sizing: border-box; } body, @@ -15,185 +17,184 @@ h5, h6, ul, ol { - margin: 0; - padding: 0; + margin: 0; + padding: 0; } img { - @include noSelect; - object-fit: contain; + @include noSelect; + object-fit: contain; } ul, ol { - list-style-type: none; + list-style-type: none; } br { - font-size: 0; + font-size: 0; } h3, h4, h5, h6 { - font-weight: normal; + font-weight: normal; } button, textarea, input { - appearance: none; - border: none; - outline: none; + appearance: none; + border: none; + outline: none; } a { - text-decoration: none; - color: currentColor; + text-decoration: none; + color: currentColor; } em, i { - font-style: normal; + font-style: normal; } -mark { - background-color: inherit; - color: currentColor; +mark { + background-color: inherit; + color: currentColor; } :focus-visible { - outline: none; + outline: none; } -[contenteditable="true"] { - caret-color: var(--theme); +[contenteditable='true'] { + caret-color: var(--theme); } -[scrollbar="auto"] { - overflow-y: auto; +[scrollbar='auto'] { + overflow-y: auto; } -[scrollbar="overlay"] { - overflow-y: auto; - overflow-y: overlay; +[scrollbar='overlay'] { + overflow-y: auto; + overflow-y: overlay; } [scrollbar] { - scrollbar-width: auto; - scrollbar-color: $colorscrollbar transparent; - scroll-behavior: smooth; - -ms-scroll-snap-type: y mandatory; - -webkit-scroll-snap-type: y mandatory; - scroll-snap-type: y mandatory; - scroll-snap-align: end; - - &::-webkit-scrollbar { - width: $scrollbarWidth; - height: $scrollbarWidth; - } - - &::-webkit-scrollbar-thumb { - background-color: $colorscrollbar; - border-radius: math.div($scrollbarWidth, 4); - } - &::-webkit-scrollbar-track { - background-color: $colornoop; - } + scrollbar-width: auto; + scrollbar-color: $colorscrollbar transparent; + scroll-behavior: smooth; + -ms-scroll-snap-type: y mandatory; + -webkit-scroll-snap-type: y mandatory; + scroll-snap-type: y mandatory; + scroll-snap-align: end; + + &::-webkit-scrollbar { + width: $scrollbarWidth; + height: $scrollbarWidth; + } + + &::-webkit-scrollbar-thumb { + background-color: $colorscrollbar; + border-radius: math.div($scrollbarWidth, 4); + } + &::-webkit-scrollbar-track { + background-color: $colornoop; + } } // 当hover时,才显示scrollbar的颜色 [scrollbarWhenHover] { - &::-webkit-scrollbar-thumb { - background-color: $colornoop; - } - &:hover { - &::-webkit-scrollbar-thumb { - background-color: $colorscrollbar; - } - } + &::-webkit-scrollbar-thumb { + background-color: $colornoop; + } + &:hover { + &::-webkit-scrollbar-thumb { + background-color: $colorscrollbar; + } + } } -.skeleton { - .skeleton-item { - animation: 2s SkeletonAnimation infinite cubic-bezier(0.36, 0, 0.64, 1) - } +.skeleton { + .skeleton-item { + animation: 2s SkeletonAnimation infinite cubic-bezier(0.36, 0, 0.64, 1); + } } @keyframes SkeletonAnimation { - 0% { - background: #dedede; - } - 45% { - background: #efefef; - } - 75% { - background: #dedede; - - } - 100% { - background: #efefef; - } + 0% { + background: #dedede; + } + 45% { + background: #efefef; + } + 75% { + background: #dedede; + } + 100% { + background: #efefef; + } } [singallinedot] { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -[visibility="true"] { - visibility: visible; +[visibility='true'] { + visibility: visible; } -[visibility="false"] { - visibility: hidden; +[visibility='false'] { + visibility: hidden; } -[iconactive="true"] { - color: var(--theme); +[iconactive='true'] { + color: var(--theme); } @supports (aspect-ratio: 1) { - @for $ratio from 1 through 90 { - $realRatio: math.div($ratio, 10); + @for $ratio from 1 through 90 { + $realRatio: math.div($ratio, 10); - [aspectratio="#{$realRatio}"] { - position: relative; - aspect-ratio: $realRatio; - > [aspectratio-full], - > img { - display: block; - height: 100%; - } - } - } + [aspectratio='#{$realRatio}'] { + position: relative; + aspect-ratio: $realRatio; + > [aspectratio-full], + > img { + display: block; + height: 100%; + } + } + } } @supports not (aspect-ratio: 1) { - @for $ratio from 1 through 90 { - $realRatio: math.div($ratio, 10); - $paddingBottomPercent: math.div(10, $ratio) * 100%; - - [aspectratio="#{$realRatio}"] { - position: relative; - padding-bottom: $paddingBottomPercent; - > [aspectratio-full], - > img { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - } - } - } -} - + @for $ratio from 1 through 90 { + $realRatio: math.div($ratio, 10); + $paddingBottomPercent: math.div(10, $ratio) * 100%; + + [aspectratio='#{$realRatio}'] { + position: relative; + padding-bottom: $paddingBottomPercent; + > [aspectratio-full], + > img { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + } + } + } +} + [sticky-list] { - position: sticky; - left: 0; - top: 0; - z-index: 97; - background-color: $baseBackgroud; -} \ No newline at end of file + position: sticky; + left: 0; + top: 0; + z-index: 97; + background-color: $baseBackgroud; +} diff --git a/src/scss/main.scss b/src/scss/main.scss index ca1b3a0..28dc412 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -1,4 +1,5 @@ -@import "variable.scss"; -@import "/src/assets/icons/iconfont.css"; -@import "common.scss"; - \ No newline at end of file +/** @format */ + +@import 'variable.scss'; +@import '/src/assets/icons/iconfont.css'; +@import 'common.scss'; diff --git a/src/scss/variable.scss b/src/scss/variable.scss index 4784a85..aa422e0 100644 --- a/src/scss/variable.scss +++ b/src/scss/variable.scss @@ -1,12 +1,14 @@ -@use "sass:math"; +/** @format */ + +@use 'sass:math'; $scrollbarWidth: 8px; $playbillWidth: 360px; $colorscrollbar: rgba(0, 0, 0, 0.18); -$colorSettingTitle: rgba(40, 40, 40, .8); -$colorSettingName: rgba(70, 70, 70, .8); +$colorSettingTitle: rgba(40, 40, 40, 0.8); +$colorSettingName: rgba(70, 70, 70, 0.8); $listBgc: rgb(240, 240, 240); -$baseBackgroud:rgb(246, 246, 246); +$baseBackgroud: rgb(246, 246, 246); $replyTextareaBgc: rgb(239, 239, 239); $colornoop: transparent; $colorbase: rgb(254, 254, 254); @@ -14,110 +16,109 @@ $colortaobao: #f40; $oneLyricItemHeight: calc(100% / 11); $lyricContainerMaxheight: 440px; -$lyricItemheihgt: math.div($lyricContainerMaxheight , 11); +$lyricItemheihgt: math.div($lyricContainerMaxheight, 11); $homeTopHeaderHeight: 80px; $homeBottomControllerHeight: 90px; $progressBarWidthOrHeight: 14px; /* 控制不同类型的图标实时显示 */ -$iconTypes: order, singalLoop, random, loop, play, pause, volume, muted, full, - cancelFull; +$iconTypes: order, singalLoop, random, loop, play, pause, volume, muted, full, cancelFull; @mixin noSelect { - user-select: none; - -webkit-user-drag: none; + user-select: none; + -webkit-user-drag: none; } @mixin flexInline { - display: inline-flex; + display: inline-flex; } @mixin flex { - display: flex; + display: flex; } @mixin flexV { - @include flex(); - flex-direction: column; + @include flex(); + flex-direction: column; } @mixin flexVc { - @include flex(); - align-items: center; + @include flex(); + align-items: center; } @mixin flexVe { - @include flex(); - align-items: flex-end; + @include flex(); + align-items: flex-end; } @mixin flexHb { - @include flex(); - justify-content: space-between; + @include flex(); + justify-content: space-between; } @mixin flexHa { - @include flex(); - justify-content: space-around; + @include flex(); + justify-content: space-around; } @mixin flexHev { - @include flex(); - justify-content: space-evenly; + @include flex(); + justify-content: space-evenly; } @mixin flexHe { - @include flex(); - justify-content: flex-end; + @include flex(); + justify-content: flex-end; } @mixin flexHbC { - @include flexV(); - @include flexHb(); + @include flexV(); + @include flexHb(); } @mixin flexHc { - @include flex(); - justify-content: center; + @include flex(); + justify-content: center; } @mixin flexHbVc { - @include flexVc; - @include flexHb; + @include flexVc; + @include flexHb; } @mixin flexHbVe { - @include flexVe; - @include flexHb; + @include flexVe; + @include flexHb; } @mixin flexHeVc { - @include flexVc; - @include flexHe; + @include flexVc; + @include flexHe; } @mixin flexHcVc { - @include flexHc(); - @include flexVc(); + @include flexHc(); + @include flexVc(); } @mixin flexHcVe { - @include flexHc(); - @include flexVe(); + @include flexHc(); + @include flexVe(); } @mixin flexHevV { - @include flexHev(); - @include flexV(); + @include flexHev(); + @include flexV(); } @mixin flexVHe { - @include flexHe(); - @include flexV(); + @include flexHe(); + @include flexV(); } @mixin flexHevVc { - @include flexHev(); - @include flexVc(); + @include flexHev(); + @include flexVc(); } diff --git a/src/shim.d.ts b/src/shim.d.ts index 68e9341..bce7324 100644 --- a/src/shim.d.ts +++ b/src/shim.d.ts @@ -1,45 +1,47 @@ +/** @format */ + import { - ComponentCustomProperties, - ComponentCustomOptions, - Prop, - ComponentPublicInstance, -} from "vue"; + ComponentCustomProperties, + ComponentCustomOptions, + Prop, + ComponentPublicInstance, +} from 'vue'; // import { Store } from "vuex"; /* eslint-disable */ -declare module "*.vue" { - import type { DefineComponent } from "vue"; - const component: DefineComponent<{}, {}, any>; - export default component; +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + const component: DefineComponent<{}, {}, any>; + export default component; } //由于vuex 4删除了其在vue组件中this.$store等的全局类型,因此需要手动增添类型声明 -declare module "@vue/runtime-core" { - interface ComponentCustomProperties { - // $store: Store; - $props: Prop; - $refs: any; - [key: string]: any; //以保证在computed等方法中使用this.xxx(data属性)不会类型报错 - } +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + // $store: Store; + $props: Prop; + $refs: any; + [key: string]: any; //以保证在computed等方法中使用this.xxx(data属性)不会类型报错 + } - //让getCurrentInstance中支持ctx上下文属性 - interface ComponentInternalInstance { - ctx: ComponentPublicInstance; - } + //让getCurrentInstance中支持ctx上下文属性 + interface ComponentInternalInstance { + ctx: ComponentPublicInstance; + } } /** * 在tsx下给Element添加非标准HTMLAttributes不报错 */ -declare module "@vue/runtime-dom" { - interface HTMLAttributes { - [attr: string]: any; - singallinedot?: any; - scrollbar?: any; - aspectratio?: string | number; - 'aspectratio-full'?: boolean; - loading?: string; - } +declare module '@vue/runtime-dom' { + interface HTMLAttributes { + [attr: string]: any; + singallinedot?: any; + scrollbar?: any; + aspectratio?: string | number; + 'aspectratio-full'?: boolean; + loading?: string; + } } -declare type NaiveFormValidateError = Array> | undefined; \ No newline at end of file +declare type NaiveFormValidateError = Array> | undefined; diff --git a/src/stores/audio.ts b/src/stores/audio.ts index 7c37e5b..1741404 100644 --- a/src/stores/audio.ts +++ b/src/stores/audio.ts @@ -1,245 +1,261 @@ -import { PreferenceNames } from "@/utils/preference"; -import { customRef, ref, watchEffect } from "vue"; -import useHowler from "@/hooks/useHowler"; -import { UNICODE_CHAR, is, extend, isURL } from "@utils/index"; -import { messageBus } from "@utils/event/register"; -import EventDispatcher from "@/utils/event/event"; -import usePlayerStore from "./player"; -import { toNext } from "."; +/** @format */ + +import { PreferenceNames } from '@/utils/preference'; +import { customRef, ref, watchEffect } from 'vue'; +import useHowler from '@/hooks/useHowler'; +import { UNICODE_CHAR, is, extend, isURL } from '@utils/index'; +import { messageBus } from '@utils/event/register'; +import EventDispatcher from '@/utils/event/event'; +import usePlayerStore from './player'; +import { toNext } from '.'; export const { - state: stateHowlerRef, - playing: playingHowlerRef, - volume: volumeHowlerRef, - rate: rateHowlerRef, - mute: muteHowlerRef, - loop: loopHowlerRef, - currentTime: currentTimeHowlerRef, - duration: durationHowlerRef, - playSound, - load, - play, - stop, - on, - off, - once, + state: stateHowlerRef, + playing: playingHowlerRef, + volume: volumeHowlerRef, + rate: rateHowlerRef, + mute: muteHowlerRef, + loop: loopHowlerRef, + currentTime: currentTimeHowlerRef, + duration: durationHowlerRef, + playSound, + load, + play, + stop, + on, + off, + once, } = useHowler(); export const defaultAudioPreferences = { - [PreferenceNames.playing]: false, - [PreferenceNames.currentTime]: 0, - [PreferenceNames.rate]: 1, - [PreferenceNames.volume]: 0.5, - [PreferenceNames.mute]: false, - //播放顺序,options:random、singleLoop - [PreferenceNames.order]: "order", -} + [PreferenceNames.playing]: false, + [PreferenceNames.currentTime]: 0, + [PreferenceNames.rate]: 1, + [PreferenceNames.volume]: 0.5, + [PreferenceNames.mute]: false, + //播放顺序,options:random、singleLoop + [PreferenceNames.order]: 'order', +}; export const playingRefGlobal = (() => { - let playing = localStorage.getItem(PreferenceNames.playing) === 'true' || defaultAudioPreferences[PreferenceNames.playing]; - return customRef((track, trigger) => { - return { - get() { - track(); - return playing; - }, - set(value) { - if (!is.boolean(value)) { - console.error(`The 'playing' must be a boolean!`); - return; - } - if (value === true) { - usePlayerStore().setVideoIsPlay(false); - } - playingHowlerRef.value = playing = value; - localStorage.setItem(PreferenceNames.playing, String(playing)); - trigger(); - } - } - }) + let playing = + localStorage.getItem(PreferenceNames.playing) === 'true' || + defaultAudioPreferences[PreferenceNames.playing]; + return customRef((track, trigger) => { + return { + get() { + track(); + return playing; + }, + set(value) { + if (!is.boolean(value)) { + console.error(`The 'playing' must be a boolean!`); + return; + } + if (value === true) { + usePlayerStore().setVideoIsPlay(false); + } + playingHowlerRef.value = playing = value; + localStorage.setItem(PreferenceNames.playing, String(playing)); + trigger(); + }, + }; + }); })(); export const currentTimeRefGlobal = (() => { - let currentTime = Number(localStorage.getItem(PreferenceNames.currentTime)) || defaultAudioPreferences[PreferenceNames.currentTime]; - return customRef((track, trigger) => { - return { - get() { - track(); - return currentTime; - }, - set(value) { - if (!is.number(value)) { - console.error(`The 'currentTime' must be a number!`); - return; - } - currentTime = value; - localStorage.setItem(PreferenceNames.currentTime, String(currentTime)); - trigger(); - } - } - }) + let currentTime = + Number(localStorage.getItem(PreferenceNames.currentTime)) || + defaultAudioPreferences[PreferenceNames.currentTime]; + return customRef((track, trigger) => { + return { + get() { + track(); + return currentTime; + }, + set(value) { + if (!is.number(value)) { + console.error(`The 'currentTime' must be a number!`); + return; + } + currentTime = value; + localStorage.setItem(PreferenceNames.currentTime, String(currentTime)); + trigger(); + }, + }; + }); })(); export const rateRefGlobal = (() => { - let rate = Number(localStorage.getItem(PreferenceNames.rate)) || defaultAudioPreferences[PreferenceNames.rate]; - return customRef((track, trigger) => { - return { - get() { - track(); - return rate; - }, - set(value) { - if (!is.number(value)) { - console.error(`The 'rate' must be a number!`); - return; - } - rateHowlerRef.value = rate = value; - localStorage.setItem(PreferenceNames.rate, String(rate)); - trigger(); - } - } - }) + let rate = + Number(localStorage.getItem(PreferenceNames.rate)) || + defaultAudioPreferences[PreferenceNames.rate]; + return customRef((track, trigger) => { + return { + get() { + track(); + return rate; + }, + set(value) { + if (!is.number(value)) { + console.error(`The 'rate' must be a number!`); + return; + } + rateHowlerRef.value = rate = value; + localStorage.setItem(PreferenceNames.rate, String(rate)); + trigger(); + }, + }; + }); })(); export const volumeRefGlobal = (() => { - let volume = Number(localStorage.getItem(PreferenceNames.volume)) || defaultAudioPreferences[PreferenceNames.volume]; - return customRef((track, trigger) => { - return { - get() { - track(); - return volume; - }, - set(value) { - if (!is.number(value)) { - console.error(`The 'volume' must be a number!`); - return; - } - volumeHowlerRef.value = volume = value; - muteHowlerRef.value = value === 0; - localStorage.setItem(PreferenceNames.volume, String(volume)); - trigger(); - } - } - }) + let volume = + Number(localStorage.getItem(PreferenceNames.volume)) || + defaultAudioPreferences[PreferenceNames.volume]; + return customRef((track, trigger) => { + return { + get() { + track(); + return volume; + }, + set(value) { + if (!is.number(value)) { + console.error(`The 'volume' must be a number!`); + return; + } + volumeHowlerRef.value = volume = value; + muteHowlerRef.value = value === 0; + localStorage.setItem(PreferenceNames.volume, String(volume)); + trigger(); + }, + }; + }); })(); export type Order = 'order' | 'singleLoop' | 'random'; const orderOptions: Order[] = ['order', 'singleLoop', 'random']; export const orderRefGlobal = (() => { - let order = (localStorage.getItem(PreferenceNames.order) || defaultAudioPreferences[PreferenceNames.order]) as Order; - return customRef((track, trigger) => { - return { - get() { - track(); - return order; - }, - set(value) { - if (!orderOptions.includes(value)) { - console.error(`The 'order' must be one of the three options:${orderOptions.join('、')}`); - return; - } - order = value; - loopHowlerRef.value = AudioMaster.isSingleLoopOrder; - localStorage.setItem(PreferenceNames.order, order); - trigger(); - } - } - }) + let order = (localStorage.getItem(PreferenceNames.order) || + defaultAudioPreferences[PreferenceNames.order]) as Order; + return customRef((track, trigger) => { + return { + get() { + track(); + return order; + }, + set(value) { + if (!orderOptions.includes(value)) { + console.error( + `The 'order' must be one of the three options:${orderOptions.join('、')}`, + ); + return; + } + order = value; + loopHowlerRef.value = AudioMaster.isSingleLoopOrder; + localStorage.setItem(PreferenceNames.order, order); + trigger(); + }, + }; + }); })(); export const muteRefGlobal = (() => { - let mute = localStorage.getItem(PreferenceNames.mute) === 'true' || defaultAudioPreferences[PreferenceNames.mute]; - return customRef((track, trigger) => { - return { - get() { - track(); - return mute; - }, - set(value) { - if (!is.boolean(value)) { - console.error(`The 'mute' must be a boolean!`); - return; - } - muteHowlerRef.value = mute = value; - localStorage.setItem(PreferenceNames.mute, String(mute)); - trigger(); - } - } - }) + let mute = + localStorage.getItem(PreferenceNames.mute) === 'true' || + defaultAudioPreferences[PreferenceNames.mute]; + return customRef((track, trigger) => { + return { + get() { + track(); + return mute; + }, + set(value) { + if (!is.boolean(value)) { + console.error(`The 'mute' must be a boolean!`); + return; + } + muteHowlerRef.value = mute = value; + localStorage.setItem(PreferenceNames.mute, String(mute)); + trigger(); + }, + }; + }); })(); export const srcOrIdRefGlobal = (() => { - let srcOrId: string | number = ''; - return customRef((track, trigger) => { - return { - get() { - track(); - return srcOrId; - }, - set(value) { - srcOrId = value; - //清除播放失败后要播放下一首的的定时器 - AudioMaster.clearPlayToNextTimeoutWhenError(); - //同时清除播放失败消息提示 - messageBus.dispatch('destroyAllMessage'); - playSound({ - src: AudioMaster.getSoundUrl(srcOrId), - autoplay: playingRefGlobal.value, - rate: rateRefGlobal.value, - loop: AudioMaster.isSingleLoopOrder, - volume: volumeHowlerRef.value, - mute: muteRefGlobal.value, - - }); - messageBus.dispatch('startLoading'); - trigger(); - } - } - }); + let srcOrId: string | number = ''; + return customRef((track, trigger) => { + return { + get() { + track(); + return srcOrId; + }, + set(value) { + srcOrId = value; + //清除播放失败后要播放下一首的的定时器 + AudioMaster.clearPlayToNextTimeoutWhenError(); + //同时清除播放失败消息提示 + messageBus.dispatch('destroyAllMessage'); + playSound({ + src: AudioMaster.getSoundUrl(srcOrId), + autoplay: playingRefGlobal.value, + rate: rateRefGlobal.value, + loop: AudioMaster.isSingleLoopOrder, + volume: volumeHowlerRef.value, + mute: muteRefGlobal.value, + }); + messageBus.dispatch('startLoading'); + trigger(); + }, + }; + }); })(); export const nextSeekTimeRefGlobal = (() => { - let nextSeekTime = currentTimeRefGlobal.value; - currentTimeHowlerRef.value = nextSeekTime; - return customRef((track, trigger) => { - return { - get() { - track(); - return nextSeekTime; - }, - set(value) { - currentTimeHowlerRef.value = nextSeekTime = value; - trigger(); - } - } - }); + let nextSeekTime = currentTimeRefGlobal.value; + currentTimeHowlerRef.value = nextSeekTime; + return customRef((track, trigger) => { + return { + get() { + track(); + return nextSeekTime; + }, + set(value) { + currentTimeHowlerRef.value = nextSeekTime = value; + trigger(); + }, + }; + }); })(); export const durationRefGlobal = ref(0); -on("load", () => { - messageBus.dispatch('finishLoading'); - durationRefGlobal.value = durationHowlerRef.value; - AudioMaster.clearTimeUpdateInterval(); - AudioMaster.setTimeUpdateInterval(() => { - if (!playingHowlerRef.value) return; - currentTimeRefGlobal.value = currentTimeHowlerRef.value; - }, 500); +on('load', () => { + messageBus.dispatch('finishLoading'); + durationRefGlobal.value = durationHowlerRef.value; + AudioMaster.clearTimeUpdateInterval(); + AudioMaster.setTimeUpdateInterval(() => { + if (!playingHowlerRef.value) return; + currentTimeRefGlobal.value = currentTimeHowlerRef.value; + }, 500); }); const failureDuration = 4000; -on("loaderror", () => { - messageBus.dispatch('errorLoading'); - messageBus.dispatch( - 'errorMessage', - `歌曲加载失败啦~将在${failureDuration / 1000}秒后播放下一首喔~${UNICODE_CHAR.pensive}`, - { - duration: failureDuration, - } - ); - AudioMaster.setPlayToNextTimeoutWhenError(() => { - toNext(); - }, failureDuration); +on('loaderror', () => { + messageBus.dispatch('errorLoading'); + messageBus.dispatch( + 'errorMessage', + `歌曲加载失败啦~将在${failureDuration / 1000}秒后播放下一首喔~${ + UNICODE_CHAR.pensive + }`, + { + duration: failureDuration, + }, + ); + AudioMaster.setPlayToNextTimeoutWhenError(() => { + toNext(); + }, failureDuration); }); /** @@ -249,61 +265,62 @@ on("loaderror", () => { * 个人理解:此处所谓的end事件,是指上一首歌被终止,而砸门惯常理解是歌曲自动播放完成 */ on('end', () => { - //歌曲自动播放完成时 - if (stateHowlerRef.value === 'loaded') { - if (AudioMaster.isSingleLoopOrder) { - return; - } - toNext(); - } + //歌曲自动播放完成时 + if (stateHowlerRef.value === 'loaded') { + if (AudioMaster.isSingleLoopOrder) { + return; + } + toNext(); + } }); export class AudioMaster extends EventDispatcher { + static playToNextTimeoutWhenError: ReturnType; + static timeUpdateInterval: ReturnType; - static playToNextTimeoutWhenError: ReturnType; - static timeUpdateInterval: ReturnType; - - static clearPlayToNextTimeoutWhenError() { - clearTimeout(this.playToNextTimeoutWhenError); - this.playToNextTimeoutWhenError = null!; - } - - static setPlayToNextTimeoutWhenError(...args: FuncParamsType) { - this.playToNextTimeoutWhenError = setTimeout(...args); - } + static clearPlayToNextTimeoutWhenError() { + clearTimeout(this.playToNextTimeoutWhenError); + this.playToNextTimeoutWhenError = null!; + } - static clearTimeUpdateInterval() { - clearInterval(this.timeUpdateInterval); - this.timeUpdateInterval = null!; - } + static setPlayToNextTimeoutWhenError(...args: FuncParamsType) { + this.playToNextTimeoutWhenError = setTimeout(...args); + } - static setTimeUpdateInterval(...args: FuncParamsType) { - this.timeUpdateInterval = setInterval(...args); - } + static clearTimeUpdateInterval() { + clearInterval(this.timeUpdateInterval); + this.timeUpdateInterval = null!; + } - static resetAudioStatus() { - durationRefGlobal.value = currentTimeRefGlobal.value = nextSeekTimeRefGlobal.value = 0; - } + static setTimeUpdateInterval(...args: FuncParamsType) { + this.timeUpdateInterval = setInterval(...args); + } - static get isByOrder() { - return orderRefGlobal.value === 'order'; - } + static resetAudioStatus() { + durationRefGlobal.value = + currentTimeRefGlobal.value = + nextSeekTimeRefGlobal.value = + 0; + } - static get isSingleLoopOrder() { - return orderRefGlobal.value === 'singleLoop'; - } + static get isByOrder() { + return orderRefGlobal.value === 'order'; + } - static get isRandomOrder() { - return orderRefGlobal.value === 'random'; - } + static get isSingleLoopOrder() { + return orderRefGlobal.value === 'singleLoop'; + } - /** - * 获取音乐的url地址 - */ - static getSoundUrl = (srcOrId: number | string) => { - srcOrId = String(srcOrId); - return !isURL(srcOrId) - ? `${location.protocol}//music.163.com/song/media/outer/url?id=${srcOrId}.mp3` - : srcOrId; - }; + static get isRandomOrder() { + return orderRefGlobal.value === 'random'; + } + /** + * 获取音乐的url地址 + */ + static getSoundUrl = (srcOrId: number | string) => { + srcOrId = String(srcOrId); + return !isURL(srcOrId) + ? `${location.protocol}//music.163.com/song/media/outer/url?id=${srcOrId}.mp3` + : srcOrId; + }; } diff --git a/src/stores/index.ts b/src/stores/index.ts index 2fdb984..5807994 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,3 +1,5 @@ +/** @format */ + export * from './player'; export * from './audio'; -export * from './user'; \ No newline at end of file +export * from './user'; diff --git a/src/stores/initStore.ts b/src/stores/initStore.ts index a4d8010..0e3932b 100644 --- a/src/stores/initStore.ts +++ b/src/stores/initStore.ts @@ -1,3 +1,4 @@ +/** @format */ import usePlayerStore, { playerQueue } from './player'; import { getOrPutPlayQueue, playerDB } from '@/database'; @@ -6,12 +7,12 @@ import { getOrPutPlayQueue, playerDB } from '@/database'; * 从DB中获取playQueue */ export async function initPlayQueueFromDB() { - const playQueue = await getOrPutPlayQueue(); - if (playQueue) { - playerQueue.value = playQueue; - } + const playQueue = await getOrPutPlayQueue(); + if (playQueue) { + playerQueue.value = playQueue; + } } playerDB.transaction('rw', [playerDB.playQueue], () => { - initPlayQueueFromDB(); + initPlayQueueFromDB(); }); diff --git a/src/stores/player.ts b/src/stores/player.ts index 8a675b4..d073a90 100644 --- a/src/stores/player.ts +++ b/src/stores/player.ts @@ -1,263 +1,270 @@ -import { EMPTY_OBJ, getRandomList, LyricParser } from "@/utils"; -import { defineStore } from "pinia"; -import { AudioMaster, playingRefGlobal, srcOrIdRefGlobal } from "./audio"; -import { getMusicDetail, getLyric } from "@api/music"; -import { SongLyricItem } from "@/types/lyric"; -import { CurrentSongInfo, getModifiedSongInfo } from "@/utils/apiSpecial"; -import { getOrPutCurrentSong, getOrPutPlayQueue, playerDB } from "@/database"; -import { customRef, toRefs, ref, watchEffect } from "vue"; -import { PreferenceNames } from "@/utils/preference"; -import { VideoBeLiked } from "@/types/video"; -import { Mv } from "@/types/mv"; +/** @format */ + +import { EMPTY_OBJ, getRandomList, LyricParser } from '@/utils'; +import { defineStore } from 'pinia'; +import { AudioMaster, playingRefGlobal, srcOrIdRefGlobal } from './audio'; +import { getMusicDetail, getLyric } from '@api/music'; +import { SongLyricItem } from '@/types/lyric'; +import { CurrentSongInfo, getModifiedSongInfo } from '@/utils/apiSpecial'; +import { getOrPutCurrentSong, getOrPutPlayQueue, playerDB } from '@/database'; +import { customRef, toRefs, ref, watchEffect } from 'vue'; +import { PreferenceNames } from '@/utils/preference'; +import { VideoBeLiked } from '@/types/video'; +import { Mv } from '@/types/mv'; export const defaultPlayerPreferences = { - //主题色 - [PreferenceNames.theme]: "#ff7875", - //播放队列是否显示 - [PreferenceNames.playerQueueShow]: false, -} + //主题色 + [PreferenceNames.theme]: '#ff7875', + //播放队列是否显示 + [PreferenceNames.playerQueueShow]: false, +}; export const theme = (() => { - let theme = localStorage.getItem(PreferenceNames.theme) || defaultPlayerPreferences[PreferenceNames.theme]; - return customRef((track, trigger) => { - return { - get() { - track(); - return theme; - }, - set(value) { - theme = value; - localStorage.setItem(PreferenceNames.theme, value); - trigger(); - } - } - }) + let theme = + localStorage.getItem(PreferenceNames.theme) || + defaultPlayerPreferences[PreferenceNames.theme]; + return customRef((track, trigger) => { + return { + get() { + track(); + return theme; + }, + set(value) { + theme = value; + localStorage.setItem(PreferenceNames.theme, value); + trigger(); + }, + }; + }); })(); export const playerQueueShow = (() => { - let playerQueueShow = localStorage.getItem(PreferenceNames.playerQueueShow) === 'true' || defaultPlayerPreferences[PreferenceNames.playerQueueShow]; - return customRef((track, trigger) => { - return { - get() { - track(); - return playerQueueShow; - }, - set(value) { - playerQueueShow = !!value; - localStorage.setItem(PreferenceNames.playerQueueShow, String(playerQueueShow)); - trigger(); - } - } - }) + let playerQueueShow = + localStorage.getItem(PreferenceNames.playerQueueShow) === 'true' || + defaultPlayerPreferences[PreferenceNames.playerQueueShow]; + return customRef((track, trigger) => { + return { + get() { + track(); + return playerQueueShow; + }, + set(value) { + playerQueueShow = !!value; + localStorage.setItem(PreferenceNames.playerQueueShow, String(playerQueueShow)); + trigger(); + }, + }; + }); })(); export const currentSongRefGlobal = (() => { - //初始的currentSongInfo - const initialCurrentSongInfo: CurrentSongInfo = { - id: 0, - duration: 0, - artists: [], - alias: [], - name: "", - albumName: '', - musicName: "", - publishTime: 0, - mark: 0, - starred: false, - album: { id: 0, name: "", picUrl: "" }, - singers: [{ id: 0, name: "" }], - localedDuration: "", - localedMark: "", - localedPublishTime: "", - }; - let currentSong = getOrPutCurrentSong() || initialCurrentSongInfo; - if (currentSong.id) { - setTimeout(() => { - //先初始化加载音乐播放相关资源或数据 - usePlayerStore().handlePlaySoundNeededData(currentSong.id, { - force: true, - needSave: false, - immediate: false - }); - }); - } - return customRef((track, trigger) => { - return { - get() { - track(); - return currentSong; - }, - set(value) { - if (!value) return; - currentSong = value; - trigger(); - } - } - }); + //初始的currentSongInfo + const initialCurrentSongInfo: CurrentSongInfo = { + id: 0, + duration: 0, + artists: [], + alias: [], + name: '', + albumName: '', + musicName: '', + publishTime: 0, + mark: 0, + starred: false, + album: { id: 0, name: '', picUrl: '' }, + singers: [{ id: 0, name: '' }], + localedDuration: '', + localedMark: '', + localedPublishTime: '', + }; + let currentSong = getOrPutCurrentSong() || initialCurrentSongInfo; + if (currentSong.id) { + setTimeout(() => { + //先初始化加载音乐播放相关资源或数据 + usePlayerStore().handlePlaySoundNeededData(currentSong.id, { + force: true, + needSave: false, + immediate: false, + }); + }); + } + return customRef((track, trigger) => { + return { + get() { + track(); + return currentSong; + }, + set(value) { + if (!value) return; + currentSong = value; + trigger(); + }, + }; + }); })(); //实际使用的currentSongInfo的类型 export type PlayerStoreStateType = { - personalFM: { - songList: any[]; - isFM: boolean; - }; - lyric: { - common: string; - translation: string; - }; - video: { - isPlay: boolean; - beLiked: VideoBeLiked[]; - }, - mv: { - beCollected: Mv[]; - } + personalFM: { + songList: any[]; + isFM: boolean; + }; + lyric: { + common: string; + translation: string; + }; + video: { + isPlay: boolean; + beLiked: VideoBeLiked[]; + }; + mv: { + beCollected: Mv[]; + }; }; export const playerQueue = ref([]); export const randomPlayerQueue = ref([]); watchEffect(() => { - if (AudioMaster.isRandomOrder) { - randomPlayerQueue.value = getRandomList(playerQueue.value); - } + if (AudioMaster.isRandomOrder) { + randomPlayerQueue.value = getRandomList(playerQueue.value); + } }); const realPlaylist = { - get value() { - return AudioMaster.isRandomOrder ? randomPlayerQueue.value : playerQueue.value; - } -} + get value() { + return AudioMaster.isRandomOrder ? randomPlayerQueue.value : playerQueue.value; + }, +}; const currentPlayIndex = customRef((track, trigger) => ({ - get() { - track(); - const { id: currentSongId } = currentSongRefGlobal.value; - return realPlaylist.value.findIndex(({ id }) => id == currentSongId) || 0 - }, - set(idx) { - const targetId = realPlaylist.value[idx].id - usePlayerStore().handlePlaySoundNeededData(targetId); - trigger(); - } + get() { + track(); + const { id: currentSongId } = currentSongRefGlobal.value; + return realPlaylist.value.findIndex(({ id }) => id == currentSongId) || 0; + }, + set(idx) { + const targetId = realPlaylist.value[idx].id; + usePlayerStore().handlePlaySoundNeededData(targetId); + trigger(); + }, })); /** * 切换至下一首 */ export function toNext() { - let value = currentPlayIndex.value; - if (++value >= realPlaylist.value.length) { - value = 0; - } - currentPlayIndex.value = value; + let value = currentPlayIndex.value; + if (++value >= realPlaylist.value.length) { + value = 0; + } + currentPlayIndex.value = value; } /** * 切换至上一首 */ export function toPrevious() { - let value = currentPlayIndex.value; - if (--value < 0) { - value = realPlaylist.value.length - 1; - } - currentPlayIndex.value = value; + let value = currentPlayIndex.value; + if (--value < 0) { + value = realPlaylist.value.length - 1; + } + currentPlayIndex.value = value; } - const usePlayerStore = defineStore({ - id: "playerStore", - state() { - const playerState: PlayerStoreStateType = { - lyric: { - common: "", - translation: "", - }, - personalFM: { - isFM: false, - songList: [], - }, - video: { - isPlay: !playingRefGlobal.value, - beLiked: [], - }, - mv: { - beCollected: [], - } - }; - return playerState; - }, - getters: { - lyricParsed(state: PlayerStoreStateType) { - const { common, translation } = toRefs(state.lyric); - const lyricData = new LyricParser(common.value, translation.value); - return lyricData; - }, - }, - actions: { - - //发布后置执行的标识 - publishAfterMark(mark: any) { - return mark; - }, + id: 'playerStore', + state() { + const playerState: PlayerStoreStateType = { + lyric: { + common: '', + translation: '', + }, + personalFM: { + isFM: false, + songList: [], + }, + video: { + isPlay: !playingRefGlobal.value, + beLiked: [], + }, + mv: { + beCollected: [], + }, + }; + return playerState; + }, + getters: { + lyricParsed(state: PlayerStoreStateType) { + const { common, translation } = toRefs(state.lyric); + const lyricData = new LyricParser(common.value, translation.value); + return lyricData; + }, + }, + actions: { + //发布后置执行的标识 + publishAfterMark(mark: any) { + return mark; + }, - setVideoIsPlay(value: boolean) { - this.video.isPlay = value; - if (value === true) { - playingRefGlobal.value = false; - } - }, + setVideoIsPlay(value: boolean) { + this.video.isPlay = value; + if (value === true) { + playingRefGlobal.value = false; + } + }, - //处理播放歌曲需要的数据 - handlePlaySoundNeededData( - id: number, - options?: { - force?: boolean; - needSave?: boolean; - immediate?: boolean; - } - ) { - const { force = false, needSave = true, immediate = true } = options || EMPTY_OBJ; - //如果已经是当前播放的歌曲了,就return - const currengSongId = currentSongRefGlobal.value.id; - if (!force && currengSongId === id) return; - immediate && (playingRefGlobal.value = true); - //设置全局音频src,以便howler加载mp3的url - srcOrIdRefGlobal.value = id; - //获取音乐详细信息,因为存在偶现型songItem中picUrl不存在 - getMusicDetail({ ids: String(id) }).then( - ({ songs: [songDetailData] }) => { - const willSetCurrentSongInfo = getModifiedSongInfo(songDetailData); - //如果没有在播放队列中, 就添加该歌曲到播放队列中 - const queueSongList = playerQueue.value; - if (!queueSongList.some(({ id: queueSongId }) => id === queueSongId)) { - const currentSongIndex = queueSongList.findIndex(({ id: queueSongId }) => currengSongId === queueSongId); - queueSongList.splice(currentSongIndex + 1, 0, willSetCurrentSongInfo); - } - //设置当前要播放歌曲的信息 - currentSongRefGlobal.value = willSetCurrentSongInfo; - //保存当前播放的歌曲到IndexedDB - if (needSave) { - getOrPutCurrentSong(willSetCurrentSongInfo); - getOrPutPlayQueue(willSetCurrentSongInfo); - } - } - ); - //获取歌词 - getLyric({ id }).then(({ lrc, tlyric, nolyric }: SongLyricItem) => { - this.lyric.common = lrc?.lyric || ''; - this.lyric.translation = tlyric?.lyric || ''; - }); - }, + //处理播放歌曲需要的数据 + handlePlaySoundNeededData( + id: number, + options?: { + force?: boolean; + needSave?: boolean; + immediate?: boolean; + }, + ) { + const { force = false, needSave = true, immediate = true } = options || EMPTY_OBJ; + //如果已经是当前播放的歌曲了,就return + const currengSongId = currentSongRefGlobal.value.id; + if (!force && currengSongId === id) return; + immediate && (playingRefGlobal.value = true); + //设置全局音频src,以便howler加载mp3的url + srcOrIdRefGlobal.value = id; + //获取音乐详细信息,因为存在偶现型songItem中picUrl不存在 + getMusicDetail({ ids: String(id) }).then(({ songs: [songDetailData] }) => { + const willSetCurrentSongInfo = getModifiedSongInfo(songDetailData); + //如果没有在播放队列中, 就添加该歌曲到播放队列中 + const queueSongList = playerQueue.value; + if (!queueSongList.some(({ id: queueSongId }) => id === queueSongId)) { + const currentSongIndex = queueSongList.findIndex( + ({ id: queueSongId }) => currengSongId === queueSongId, + ); + queueSongList.splice(currentSongIndex + 1, 0, willSetCurrentSongInfo); + } + //设置当前要播放歌曲的信息 + currentSongRefGlobal.value = willSetCurrentSongInfo; + //保存当前播放的歌曲到IndexedDB + if (needSave) { + getOrPutCurrentSong(willSetCurrentSongInfo); + getOrPutPlayQueue(willSetCurrentSongInfo); + } + }); + //获取歌词 + getLyric({ id }).then(({ lrc, tlyric, nolyric }: SongLyricItem) => { + this.lyric.common = lrc?.lyric || ''; + this.lyric.translation = tlyric?.lyric || ''; + }); + }, - //视频是否已被收藏过 - isVideoBeLiked(videoId: string | number, description?: string) { - return this.video.beLiked.some(({ mlogBaseData: { id, text, desc } }) => id == videoId || description?.includes(desc)); - }, + //视频是否已被收藏过 + isVideoBeLiked(videoId: string | number, description?: string) { + return this.video.beLiked.some( + ({ mlogBaseData: { id, text, desc } }) => + id == videoId || description?.includes(desc), + ); + }, - //视频(mv)是否已被收藏 - isMvBeCollected(mvId: string | number) { - return this.mv.beCollected.some(({ id }) => id == mvId); - } - }, + //视频(mv)是否已被收藏 + isMvBeCollected(mvId: string | number) { + return this.mv.beCollected.some(({ id }) => id == mvId); + }, + }, }); -export default usePlayerStore; \ No newline at end of file +export default usePlayerStore; diff --git a/src/stores/user.ts b/src/stores/user.ts index b99dfb5..74d090a 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -1,96 +1,98 @@ -import { loginStatus, logout } from "@/api/auth"; -import { userAccount } from "@/api/user"; -import { LoginWithPhoneResult, UserSubCount } from "@/types/auth"; -import { EMPTY_OBJ, is, UNICODE_CHAR } from "@/utils"; -import { loginCookie } from "@/utils/auth"; -import { messageBus } from "@/utils/event/register"; -import { defineStore } from "pinia"; -import { nextTick } from "vue"; +/** @format */ + +import { loginStatus, logout } from '@/api/auth'; +import { userAccount } from '@/api/user'; +import { LoginWithPhoneResult, UserSubCount } from '@/types/auth'; +import { EMPTY_OBJ, is, UNICODE_CHAR } from '@/utils'; +import { loginCookie } from '@/utils/auth'; +import { messageBus } from '@/utils/event/register'; +import { defineStore } from 'pinia'; +import { nextTick } from 'vue'; export type UserStateType = { - cookie: string; - userId: string | number; - subCount: UserSubCount; - detail: any; - // 是否已登录 - isLogin: boolean; - // 是否显示登录对话框 - showLoginDialog?: boolean; - //我喜欢的音乐ids - myLoveListIds: number[]; - playlist: { - myCreated: any[]; - myCollection: any[]; - } -} + cookie: string; + userId: string | number; + subCount: UserSubCount; + detail: any; + // 是否已登录 + isLogin: boolean; + // 是否显示登录对话框 + showLoginDialog?: boolean; + //我喜欢的音乐ids + myLoveListIds: number[]; + playlist: { + myCreated: any[]; + myCollection: any[]; + }; +}; const useUserStore = defineStore({ - id: "userStore", - state() { - const userState: UserStateType = { - cookie: '', - userId: '', - detail: { - profile: { - - }, - - } as any, - subCount: {} as any, - myLoveListIds: [], - isLogin: false, - showLoginDialog: false, - playlist: { - myCreated: [], - myCollection: [], - } - }; - return userState; - }, - getters: { - - }, - actions: { - async judgeAndSetAccountInfo(options: { - isFirstRefresh?: boolean - } = EMPTY_OBJ) { - const { isFirstRefresh = false } = options; - await userAccount().then(({ account, profile }) => { - let topic: string; - let tip: string; - if (!account || !profile) { - if (isFirstRefresh) { - topic = 'warnMessage'; - tip = `阿娜达~快去登录噢~${UNICODE_CHAR.hugface}`; - } else { - topic = 'errorMessage'; - tip = `阿娜达~登录失败啦,再试一次⑧~${UNICODE_CHAR.pensive}`; - } - } else { - this.userId = account.id; - this.isLogin = true; - topic = 'successMessage'; - tip = `阿娜达~登录成功啦~${UNICODE_CHAR.smile}` - } - if (!isFirstRefresh) { - messageBus.dispatch(topic, tip, { - duration: 4000, - }); - } - }); - }, - async logOut() { - const res = await logout(); - if (res.code === 200) { - loginCookie.remove(); - this.isLogin = false; - messageBus.dispatch('successMessage', `账号已退出,快重新登录⑧ ${UNICODE_CHAR.smile}`) - await nextTick(); - this.showLoginDialog = true; - return true - } - } - }, + id: 'userStore', + state() { + const userState: UserStateType = { + cookie: '', + userId: '', + detail: { + profile: {}, + } as any, + subCount: {} as any, + myLoveListIds: [], + isLogin: false, + showLoginDialog: false, + playlist: { + myCreated: [], + myCollection: [], + }, + }; + return userState; + }, + getters: {}, + actions: { + async judgeAndSetAccountInfo( + options: { + isFirstRefresh?: boolean; + } = EMPTY_OBJ, + ) { + const { isFirstRefresh = false } = options; + await userAccount().then(({ account, profile }) => { + let topic: string; + let tip: string; + if (!account || !profile) { + if (isFirstRefresh) { + topic = 'warnMessage'; + tip = `阿娜达~快去登录噢~${UNICODE_CHAR.hugface}`; + } else { + topic = 'errorMessage'; + tip = `阿娜达~登录失败啦,再试一次⑧~${UNICODE_CHAR.pensive}`; + } + } else { + this.userId = account.id; + this.isLogin = true; + topic = 'successMessage'; + tip = `阿娜达~登录成功啦~${UNICODE_CHAR.smile}`; + } + if (!isFirstRefresh) { + messageBus.dispatch(topic, tip, { + duration: 4000, + }); + } + }); + }, + async logOut() { + const res = await logout(); + if (res.code === 200) { + loginCookie.remove(); + this.isLogin = false; + messageBus.dispatch( + 'successMessage', + `账号已退出,快重新登录⑧ ${UNICODE_CHAR.smile}`, + ); + await nextTick(); + this.showLoginDialog = true; + return true; + } + }, + }, }); export default useUserStore; diff --git a/src/sw.ts b/src/sw.ts index e5f9388..47e5f5f 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -1,4 +1,6 @@ -import { precacheAndRoute, cleanupOutdatedCaches, } from 'workbox-precaching'; +/** @format */ + +import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching'; import { clientsClaim } from 'workbox-core'; declare let self: ServiceWorkerGlobalScope; @@ -12,6 +14,5 @@ self.skipWaiting(); clientsClaim(); self.addEventListener('message', (event) => { - if (event.data && event.data.type === 'SKIP_WAITING') - self.skipWaiting(); -}); \ No newline at end of file + if (event.data && event.data.type === 'SKIP_WAITING') self.skipWaiting(); +}); diff --git a/src/types/album.d.ts b/src/types/album.d.ts index ffc6379..e96022a 100644 --- a/src/types/album.d.ts +++ b/src/types/album.d.ts @@ -1,20 +1,22 @@ +/** @format */ + export declare type AlbumInfo = { - name: string; - alias: string[]; - briefDesc: string; - mark: number; - id: number; - description: string; - picUrl: string; - subType: string; - songs: any[]; - transNames: string[]; - type: string; - company: string; - onSale: boolean; - publishTime: number; - tags: string; + name: string; + alias: string[]; + briefDesc: string; + mark: number; + id: number; + description: string; + picUrl: string; + subType: string; + songs: any[]; + transNames: string[]; + type: string; + company: string; + onSale: boolean; + publishTime: number; + tags: string; - //额外加的 - publishTimeStr: string; + //额外加的 + publishTimeStr: string; }; diff --git a/src/types/auth.d.ts b/src/types/auth.d.ts index d57288e..221339a 100644 --- a/src/types/auth.d.ts +++ b/src/types/auth.d.ts @@ -1,62 +1,64 @@ +/** @format */ + export type LoginWithPhoneResult = { - account: { - createTime: number; - id: number; - userName: string; - vipType: number; - anonimousUser: boolean; - }, - bindings: Array<{ - bindingTime: number; - expired: boolean; - expiresIn: number; - id: number; - refreshTime: number; - tokenJsonStr: string; - type: number; - url: string; - uerId: number; - }>, - cookie: string; - token: string; - loginType: number; - profile: { - avatarUrl: string; - authStatus: number; - //已认证 - authenticated: boolean; - authority: number; - backgroundUrl: string; - birthday: number; - city: number; - description: string; - detailDescription: string; - eventCount: number; - expertTags: null | any; - experts: PlainObject; - mutual: boolean; - follows: number; - followeds: number; - followed: boolean; - nickname: string; - playlistCount: number; - province: number; - remarkName: string; - signature: string; - userId: number; - vipType: number; - userType: number; - playlistBeSubscribedCount: number; - } -} + account: { + createTime: number; + id: number; + userName: string; + vipType: number; + anonimousUser: boolean; + }; + bindings: Array<{ + bindingTime: number; + expired: boolean; + expiresIn: number; + id: number; + refreshTime: number; + tokenJsonStr: string; + type: number; + url: string; + uerId: number; + }>; + cookie: string; + token: string; + loginType: number; + profile: { + avatarUrl: string; + authStatus: number; + //已认证 + authenticated: boolean; + authority: number; + backgroundUrl: string; + birthday: number; + city: number; + description: string; + detailDescription: string; + eventCount: number; + expertTags: null | any; + experts: PlainObject; + mutual: boolean; + follows: number; + followeds: number; + followed: boolean; + nickname: string; + playlistCount: number; + province: number; + remarkName: string; + signature: string; + userId: number; + vipType: number; + userType: number; + playlistBeSubscribedCount: number; + }; +}; export type UserSubCount = { - artistCount:number; - createDjRadioCount: number; - createdPlaylistCount: number; - djRadioCount: number; - mvCount: number; - newProgramCount: number; - programCount: number; - subPlaylistCount: number; -} \ No newline at end of file + artistCount: number; + createDjRadioCount: number; + createdPlaylistCount: number; + djRadioCount: number; + mvCount: number; + newProgramCount: number; + programCount: number; + subPlaylistCount: number; +}; diff --git a/src/types/lyric.d.ts b/src/types/lyric.d.ts index b6e8375..b18233f 100644 --- a/src/types/lyric.d.ts +++ b/src/types/lyric.d.ts @@ -1,46 +1,48 @@ +/** @format */ + export type SearchLyricItem = { - //专辑 - al: { - id: number; - name: number; - picUrl: string; - tns: any[]; - }; - //别名 - alia: string[]; - //作者 - ar: { - id: number; - alia: string[]; - name: string; - tns: any[]; - }[]; - cd: string; - copyright: number; - //歌曲时长 - dt: number; - fee: number; - id: number; - mark: number; - lyrics: string[]; - name: string; - mv: number; - pop: number; - publishTime: number; - single: number; - v: number; + //专辑 + al: { + id: number; + name: number; + picUrl: string; + tns: any[]; + }; + //别名 + alia: string[]; + //作者 + ar: { + id: number; + alia: string[]; + name: string; + tns: any[]; + }[]; + cd: string; + copyright: number; + //歌曲时长 + dt: number; + fee: number; + id: number; + mark: number; + lyrics: string[]; + name: string; + mv: number; + pop: number; + publishTime: number; + single: number; + v: number; }; /** * 歌词提供者、翻译者信息 */ export type LyricUserInfo = { - id: number; - demand: number; - nickname: string; - status: number; - uptime: number; - userid: number; + id: number; + demand: number; + nickname: string; + status: number; + uptime: number; + userid: number; }; /** @@ -51,11 +53,9 @@ export type LyricUserInfo = { * - nolyric/needDesc 存在于没有歌词时,如纯音乐等 */ export type SongLyricItem = Partial<{ - transUser?: LyricUserInfo; - lyricUser: LyricUserInfo; + transUser?: LyricUserInfo; + lyricUser: LyricUserInfo; }> & - Record<"qfy" | "sfy" | "sgc", boolean> & - Partial< - Record<"klyric" | "tlyric" | "lrc", { version: number; lyric: string }> - > & - Partial>; + Record<'qfy' | 'sfy' | 'sgc', boolean> & + Partial> & + Partial>; diff --git a/src/types/mv.d.ts b/src/types/mv.d.ts index b6c0ea7..47c812e 100644 --- a/src/types/mv.d.ts +++ b/src/types/mv.d.ts @@ -1,33 +1,35 @@ +/** @format */ + export declare type Mv = { - artistName: string; - id: number; - duration: number; - imgurl: string; - imgurl16v9: string; - name: string; - publishTime: string; - status: number; - subed: boolean; - playCount: number; - artist: any; + artistName: string; + id: number; + duration: number; + imgurl: string; + imgurl16v9: string; + name: string; + publishTime: string; + status: number; + subed: boolean; + playCount: number; + artist: any; - //额外的 - playCountStr?: string; + //额外的 + playCountStr?: string; }; export declare type SearchMv = { - alias: null | any; - artistId: number; - artistName: string; - artists: any[]; - desc: string | null; - briefDesc: string | null; - cover: string; - duration: number; - id: number; - mark: number; - name: number; - playCount: number; - playCountStr: string; - transNames: string[] | null -} \ No newline at end of file + alias: null | any; + artistId: number; + artistName: string; + artists: any[]; + desc: string | null; + briefDesc: string | null; + cover: string; + duration: number; + id: number; + mark: number; + name: number; + playCount: number; + playCountStr: string; + transNames: string[] | null; +}; diff --git a/src/types/singer.d.ts b/src/types/singer.d.ts index c64fbe8..2982885 100644 --- a/src/types/singer.d.ts +++ b/src/types/singer.d.ts @@ -1,24 +1,26 @@ +/** @format */ + export declare type SingerInfo = { - alias: string[]; - name: string; - briefDesc: string; - id: number; - followed: boolean; - picUrl: string; - fullName: string; + alias: string[]; + name: string; + briefDesc: string; + id: number; + followed: boolean; + picUrl: string; + fullName: string; }; export declare type SearchSingerItem = { - accountId: number; - albumSize: number; - alia: string[]; - alias: string[]; - followed: boolean; - id: number; - picUrl: string; - img1v1Url: string; - mvSize: number; - name: string; - trans: string | null; - // transNames: string[]; + accountId: number; + albumSize: number; + alia: string[]; + alias: string[]; + followed: boolean; + id: number; + picUrl: string; + img1v1Url: string; + mvSize: number; + name: string; + trans: string | null; + // transNames: string[]; }; diff --git a/src/types/song.ts b/src/types/song.ts index 6ca3937..1066e6a 100644 --- a/src/types/song.ts +++ b/src/types/song.ts @@ -1,190 +1,192 @@ -import { OriginCoverType, SongFee } from "@/dependency/enum"; -import { SongCommentUser } from "./user"; +/** @format */ + +import { OriginCoverType, SongFee } from '@/dependency/enum'; +import { SongCommentUser } from './user'; export declare type SongChargeInfo = { - rate: 128000 | 192000 | 32000 | 999000 | number; - chargeUrl: string | null; + rate: 128000 | 192000 | 32000 | 999000 | number; + chargeUrl: string | null; }; export declare type PlayRecord = { - playCount: number; - score: number; - song: SongInfo; + playCount: number; + score: number; + song: SongInfo; }[]; export declare type SongInfo = { - //歌曲id - id: number; - //歌曲名字 - name: string; - //歌曲别名,如xxx游戏宣传曲 - alia: string[]; - //单曲时长,单位:毫秒 - dt: number; - // 小数,常取[0.0, 100.0]中离散的几个数值, 表示歌曲热度 - pop: number; - //歌手信息 - ar: { - id: number; - name: string; - alias?: string[]; - }[]; - // album专辑 - al: { - id: number; - name: string; - picUrl: string; - }; - //也许没有喜欢 - starred?: boolean; - // 费用 - fee: SongFee; - //评论数量 - mark: number; - // 歌曲发布时间 - publishTime: number; - originCoverType: OriginCoverType; - privilege: { - id: number; - chargeInfoList: SongChargeInfo[]; - }; -} & MediaQualityCategory + //歌曲id + id: number; + //歌曲名字 + name: string; + //歌曲别名,如xxx游戏宣传曲 + alia: string[]; + //单曲时长,单位:毫秒 + dt: number; + // 小数,常取[0.0, 100.0]中离散的几个数值, 表示歌曲热度 + pop: number; + //歌手信息 + ar: { + id: number; + name: string; + alias?: string[]; + }[]; + // album专辑 + al: { + id: number; + name: string; + picUrl: string; + }; + //也许没有喜欢 + starred?: boolean; + // 费用 + fee: SongFee; + //评论数量 + mark: number; + // 歌曲发布时间 + publishTime: number; + originCoverType: OriginCoverType; + privilege: { + id: number; + chargeInfoList: SongChargeInfo[]; + }; +} & MediaQualityCategory; /** * 最新音乐 */ export declare type NewestSongInfo = { - bMusic: { - bitrate: number; - extension: string; - id: number; - playTime: number; - size: number; - sr: number; - name: null | string; - }; - artists: { - id: number; - alias: string[]; - name: string; - musicSize: number; - albumSize: number; - picUrl: string; - brifDesc: string; - followed: boolean; - trans: string; - }[]; - alias: string[]; - duration: number; - id: number; - fee: SongFee; - name: string; - mvid: number; - no: number; - playedNum: number; - popularity: number; - singer: string; - starred: boolean; - starredNum: number; - mp3Url: string; + bMusic: { + bitrate: number; + extension: string; + id: number; + playTime: number; + size: number; + sr: number; + name: null | string; + }; + artists: { + id: number; + alias: string[]; + name: string; + musicSize: number; + albumSize: number; + picUrl: string; + brifDesc: string; + followed: boolean; + trans: string; + }[]; + alias: string[]; + duration: number; + id: number; + fee: SongFee; + name: string; + mvid: number; + no: number; + playedNum: number; + popularity: number; + singer: string; + starred: boolean; + starredNum: number; + mp3Url: string; - album: { - alias: string[]; - picUrl: string; - blurPicUrl: string; - description: string; - artist: { - img1v1Id: number; - topicPerson: number; - followed: boolean; - musicSize: number; - alias: string[]; - }; - artists: NewestSongInfo["album"]["artist"][]; - briefDesc: string; - company: string; - companyId: number; - copyrightId: number; - id: number; - name: string; - onSale: boolean; - paid: boolean; - publishTime: number; - size: number; - songs: any[]; - status: number; - subType: string; - tags: string; - type: string; - }; + album: { + alias: string[]; + picUrl: string; + blurPicUrl: string; + description: string; + artist: { + img1v1Id: number; + topicPerson: number; + followed: boolean; + musicSize: number; + alias: string[]; + }; + artists: NewestSongInfo['album']['artist'][]; + briefDesc: string; + company: string; + companyId: number; + copyrightId: number; + id: number; + name: string; + onSale: boolean; + paid: boolean; + publishTime: number; + size: number; + songs: any[]; + status: number; + subType: string; + tags: string; + type: string; + }; }; export declare type SongUrlInfo = { - type: "mp3" | "m4a"; - encodeType: "mp3"; - canExtend: boolean; - fee: SongFee; - url: string; - size: number; - id: number; - md5: string; - gain: 0 | number; - level: "standard" | string; - played: 0 | 1; - //免费试听时间段,单位:秒 - freeTrialInfo: { - start: number; - end: number; - } | null; + type: 'mp3' | 'm4a'; + encodeType: 'mp3'; + canExtend: boolean; + fee: SongFee; + url: string; + size: number; + id: number; + md5: string; + gain: 0 | number; + level: 'standard' | string; + played: 0 | 1; + //免费试听时间段,单位:秒 + freeTrialInfo: { + start: number; + end: number; + } | null; }; export declare type SongCommentItem = { - commentId: string; - content: string; - time: number; - timeStr: string; - needDisplayTime: boolean; - likedCount: number; - liked: boolean; - user: SongCommentUser; - beReplied: Array<{ - content: string; - beRepliedCommentId: number; - user: SongCommentUser; - }>; -} + commentId: string; + content: string; + time: number; + timeStr: string; + needDisplayTime: boolean; + likedCount: number; + liked: boolean; + user: SongCommentUser; + beReplied: Array<{ + content: string; + beRepliedCommentId: number; + user: SongCommentUser; + }>; +}; export declare type SongComment = { - total: number; - moreHot: boolean; - more: boolean; - userId: number; - isMusician: boolean; - commentBanner: null | any; - comments: Array; - hotComments: Array; - topComments: Array; -} + total: number; + moreHot: boolean; + more: boolean; + userId: number; + isMusician: boolean; + commentBanner: null | any; + comments: Array; + hotComments: Array; + topComments: Array; +}; /** * getMusicDetail api 返回值 */ export declare type SongsDetail = { - privileges: SongInfo['privilege'][]; - songs: Exclude[]; -} + privileges: SongInfo['privilege'][]; + songs: Exclude[]; +}; /** * 媒体资源文件质量信息 */ export type MediaQuality = { - br: number; - fid: number; - size: number; - vd: number; -} + br: number; + fid: number; + size: number; + vd: number; +}; /** * 媒体资源文件质量信息(高、中、低) */ -export type MediaQualityCategory = Record<'h' | 'm' | 'l', MediaQuality>; \ No newline at end of file +export type MediaQualityCategory = Record<'h' | 'm' | 'l', MediaQuality>; diff --git a/src/types/songlist.d.ts b/src/types/songlist.d.ts index a9670c4..10ad408 100644 --- a/src/types/songlist.d.ts +++ b/src/types/songlist.d.ts @@ -1,203 +1,205 @@ +/** @format */ + //歌单分类子类 export declare type CatListSub = { - category: number; - imgId: number; - imgUrl: any; - name: string; - type: number; - hot: boolean; - activity: boolean; - resourceType: number; - resourceCount: number; + category: number; + imgId: number; + imgUrl: any; + name: string; + type: number; + hot: boolean; + activity: boolean; + resourceType: number; + resourceCount: number; }; export declare type SubscriberList = { - more: boolean; - reason: "needLogin" | string; - subscribers: Subscriber[]; - total: number; + more: boolean; + reason: 'needLogin' | string; + subscribers: Subscriber[]; + total: number; }; //歌单订阅者 export declare type Subscriber = { - avatarUrl: string; - birthday: number; - description: string; - defaultAvatar: string; - detailDescription: string; - followed: boolean; - nickname: string; - province: number; - remarkName: string; - signature: string; - userId: number; - vipType: number; - userType: number; - mutual: boolean; - city: number; - backgroundUrl: string; + avatarUrl: string; + birthday: number; + description: string; + defaultAvatar: string; + detailDescription: string; + followed: boolean; + nickname: string; + province: number; + remarkName: string; + signature: string; + userId: number; + vipType: number; + userType: number; + mutual: boolean; + city: number; + backgroundUrl: string; }; //音频id数据 -export declare type TrackId = Record<"at" | "id" | "lr" | "t" | "v", number>; +export declare type TrackId = Record<'at' | 'id' | 'lr' | 't' | 'v', number>; //音频轨道数据 export declare type Track = { - //album - al: { - id: number; - name: string; - picUrl: string; - tns: any[]; - }; - alias: string[]; - //artist - ar: { - alias: string[]; - id: number; - name: string; - }[]; - mark: number; - mv: number; - name: string; - publishTime: number; + //album + al: { + id: number; + name: string; + picUrl: string; + tns: any[]; + }; + alias: string[]; + //artist + ar: { + alias: string[]; + id: number; + name: string; + }[]; + mark: number; + mv: number; + name: string; + publishTime: number; }; //排行榜列表 export declare type Playlist = { - name: string; - id: number; - ToplistType: string; - adType: number; - coverImgUrl: string; - description: string; - ordered: boolean; - subscribed: boolean; - subscribers: Subscriber[]; - tags: string[]; - tracks: Track[]; - trackCount: number; - trackIds: TrackId[]; - trackNumberUpdateTime: number; - trackUpdateTime: number; - updateFrequency: null | any; - userId: number; - videos: null | any; - videoIds: null | any; - createTime: number; - updateTime: number; - shareCount: number; - playCount: number; - subscribedCount: number; + name: string; + id: number; + ToplistType: string; + adType: number; + coverImgUrl: string; + description: string; + ordered: boolean; + subscribed: boolean; + subscribers: Subscriber[]; + tags: string[]; + tracks: Track[]; + trackCount: number; + trackIds: TrackId[]; + trackNumberUpdateTime: number; + trackUpdateTime: number; + updateFrequency: null | any; + userId: number; + videos: null | any; + videoIds: null | any; + createTime: number; + updateTime: number; + shareCount: number; + playCount: number; + subscribedCount: number; - //额外 - createTimeStr: string; - updateTimeStr: string; - playCountStr: string; - subscribedCountStr: string; - shareCountStr: string; + //额外 + createTimeStr: string; + updateTimeStr: string; + playCountStr: string; + subscribedCountStr: string; + shareCountStr: string; }; //排行榜 export declare type Toplist = { - playlist: Playlist; - privileges?: any[]; - relatedVideos?: null | any; - urls?: null | any; + playlist: Playlist; + privileges?: any[]; + relatedVideos?: null | any; + urls?: null | any; }; //歌单创建者 export declare type SongListCreator = { - avatarUrl: string; //发布者头像 - backgroundUrl: string; //主页背景图片 - birthday: number; - city: number; - defaultAvatar: boolean | string; - description: string; - detailDescription: string; - expertTags: string[]; - followed: boolean; //是否已关注 - mutual: boolean; //是否互相关注 - userId: number; - nickname: string; - vipType: number; - userType: number; - signature: string; - remarkName: null | string; - gender: number; //性别 - avatarDetail: { - indentityIconUrl: string; //身份图标 - identity: number; - userType: number; - }; + avatarUrl: string; //发布者头像 + backgroundUrl: string; //主页背景图片 + birthday: number; + city: number; + defaultAvatar: boolean | string; + description: string; + detailDescription: string; + expertTags: string[]; + followed: boolean; //是否已关注 + mutual: boolean; //是否互相关注 + userId: number; + nickname: string; + vipType: number; + userType: number; + signature: string; + remarkName: null | string; + gender: number; //性别 + avatarDetail: { + indentityIconUrl: string; //身份图标 + identity: number; + userType: number; + }; }; //歌单列表 export declare type PlaylistCommon = { - coverImgUrl: string;//如普通歌单的图片 - picUrl: string;//如个人推荐歌单的图片 - createTime: number; - description: string; - id: number; - userId: number; - tags: string[]; - ordered: boolean; - privacy: number; - recommendInfo: null | any; - subscribed: null | any; - subscribers: Subscriber[]; - tracks: null | any; - newImported: boolean; - name: string; - creator: SongListCreator; - authenticationTypes: number; //身份认证 + coverImgUrl: string; //如普通歌单的图片 + picUrl: string; //如个人推荐歌单的图片 + createTime: number; + description: string; + id: number; + userId: number; + tags: string[]; + ordered: boolean; + privacy: number; + recommendInfo: null | any; + subscribed: null | any; + subscribers: Subscriber[]; + tracks: null | any; + newImported: boolean; + name: string; + creator: SongListCreator; + authenticationTypes: number; //身份认证 - trackNumberUpdateTime: number; - trackUpdateTime: number; - trackCount: number; - subscribedCount: number; - commentCount: number; - shareCount: number; - playCount: number; + trackNumberUpdateTime: number; + trackUpdateTime: number; + trackCount: number; + subscribedCount: number; + commentCount: number; + shareCount: number; + playCount: number; }; export declare type PlaylistDynamic = { - bookedCount: number; - commentCount: number; - followed: boolean; - playCount: number; - remarkName: null | any; - shareCount: number; - subscribed: boolean; + bookedCount: number; + commentCount: number; + followed: boolean; + playCount: number; + remarkName: null | any; + shareCount: number; + subscribed: boolean; - //额外的 - commentCountStr: string; - playCountStr: string; + //额外的 + commentCountStr: string; + playCountStr: string; }; //搜索数据中的歌单 export declare type SearchPlaylist = { - //收藏量 - bookCount: number; - //播放量 - playCount: number; - //包含的歌曲数量 - trackCount: number; - coverImgUrl: string; - creator: { - authStatus: 0; - expertTags: null; - experts: null; - nickname: string; - userId: number; - userType: number; - }; - description: null | string; - highQuality: boolean; - specialType: number; - subscribed: boolean; - id: number; - userId: number; - officialTags: null | any; - name: string; + //收藏量 + bookCount: number; + //播放量 + playCount: number; + //包含的歌曲数量 + trackCount: number; + coverImgUrl: string; + creator: { + authStatus: 0; + expertTags: null; + experts: null; + nickname: string; + userId: number; + userType: number; + }; + description: null | string; + highQuality: boolean; + specialType: number; + subscribed: boolean; + id: number; + userId: number; + officialTags: null | any; + name: string; }; diff --git a/src/types/user.d.ts b/src/types/user.d.ts index 1e4cfa4..1b3e3fc 100644 --- a/src/types/user.d.ts +++ b/src/types/user.d.ts @@ -1,112 +1,114 @@ +/** @format */ + export declare type UserProfile = { - allSubscribedCount: number; - avatarUrl: string; - backgroundUrl: string; - birthday: number; - blacklist: boolean; - createTime: number; - city: number; - description: string; - detailDescription: string; - followMe: boolean; - followTime: null | number | string; - followeds: number; //粉丝数 - newFollows: number; //新关注数 - follows: number; //关注数 - gender: number; //性别 - mutual: boolean; //是否互相关注 - nickname?: string; - playlistBeSubscribedCount: number; - playlistCount: number; - province: number; - userId: number; - remarkName: null | string; - signature: string; - eventCount: number; //动态 - userType: number; - vipType: number; // 11表示vip、0表示普通用户 + allSubscribedCount: number; + avatarUrl: string; + backgroundUrl: string; + birthday: number; + blacklist: boolean; + createTime: number; + city: number; + description: string; + detailDescription: string; + followMe: boolean; + followTime: null | number | string; + followeds: number; //粉丝数 + newFollows: number; //新关注数 + follows: number; //关注数 + gender: number; //性别 + mutual: boolean; //是否互相关注 + nickname?: string; + playlistBeSubscribedCount: number; + playlistCount: number; + province: number; + userId: number; + remarkName: null | string; + signature: string; + eventCount: number; //动态 + userType: number; + vipType: number; // 11表示vip、0表示普通用户 }; export declare type UserDetail = { - adValid: boolean; - bindings: { - expired: boolean; - type: number; - userId: number; - url: string; - bindingTime: number; - id: number; - refreshTime: number; - }[]; - // 认证 - identify?: { - actionUrl: string; - imageDesc: string; - imageUrl: string; - }; - createDays: number; - createTime: number; - level: number; //等级 - listenSongs: number; - mobileSign?: boolean; - pcSign?: boolean; - peopleCanSeeMyPlayRecord: boolean; //播放记录是否可见 - profile: Partial; - //积分 - userPoint?: { - balance: number; - blockBalance: number; - updateTime: number; - userId: number; - version: number; - status: number; - }; + adValid: boolean; + bindings: { + expired: boolean; + type: number; + userId: number; + url: string; + bindingTime: number; + id: number; + refreshTime: number; + }[]; + // 认证 + identify?: { + actionUrl: string; + imageDesc: string; + imageUrl: string; + }; + createDays: number; + createTime: number; + level: number; //等级 + listenSongs: number; + mobileSign?: boolean; + pcSign?: boolean; + peopleCanSeeMyPlayRecord: boolean; //播放记录是否可见 + profile: Partial; + //积分 + userPoint?: { + balance: number; + blockBalance: number; + updateTime: number; + userId: number; + version: number; + status: number; + }; }; export declare type SearchUserProfileItem = { - accountStatus: number; - anchor: boolean; - authStatus: number; - avatarUrl: string; - backgroundUrl: string; - birthday: number; - city: number; - defaultAvatar: boolean; - description: string; - detailDescription: string; - followed: string; - followeds: number; - follows: number; - //是否互相关注 - mutual: boolean; - nickname: string; - playlistBeSubscribedCount: number; - //歌单数量 - playlistCount: number; - province: number; - remarkName: null; - signature: string; - userId: number; - userType: number; - vipType: number; + accountStatus: number; + anchor: boolean; + authStatus: number; + avatarUrl: string; + backgroundUrl: string; + birthday: number; + city: number; + defaultAvatar: boolean; + description: string; + detailDescription: string; + followed: string; + followeds: number; + follows: number; + //是否互相关注 + mutual: boolean; + nickname: string; + playlistBeSubscribedCount: number; + //歌单数量 + playlistCount: number; + province: number; + remarkName: null; + signature: string; + userId: number; + userType: number; + vipType: number; }; export declare type SongCommentUser = { - anonym: number; - authStatus: number; - avatarDetail: null | any; - avatarUrl: string; - commonIdentity: null | any; - expertTags: null | any[]; - experts: null | any; - followed: boolean; - liveInfo: null | any; - locationInfo: null | any; - mutual: boolean; - nickname: string; - remarkName: null | any; - userId: number; - userType: number; - vipRights: null | any; - vipType: number; -} \ No newline at end of file + anonym: number; + authStatus: number; + avatarDetail: null | any; + avatarUrl: string; + commonIdentity: null | any; + expertTags: null | any[]; + experts: null | any; + followed: boolean; + liveInfo: null | any; + locationInfo: null | any; + mutual: boolean; + nickname: string; + remarkName: null | any; + userId: number; + userType: number; + vipRights: null | any; + vipType: number; +}; diff --git a/src/types/video.d.ts b/src/types/video.d.ts index b6b5c75..9a2cf4a 100644 --- a/src/types/video.d.ts +++ b/src/types/video.d.ts @@ -1,179 +1,180 @@ -import { SingerInfo } from "./singer" +/** @format */ + +import { SingerInfo } from './singer'; //视频标签和分类列表对象 export type VideoTagItem = { - abExtInfo: null | string; - id: number; - name: string; - relatedVideoType: null | string; - selectTab: boolean; - url: null | string; -} + abExtInfo: null | string; + id: number; + name: string; + relatedVideoType: null | string; + selectTab: boolean; + url: null | string; +}; //全部视频列表 export type allVideoItem = { - datas: allVideoDatasItem[]; - hasmore: boolean; - msg: string; - rcmdLimit: number; -} + datas: allVideoDatasItem[]; + hasmore: boolean; + msg: string; + rcmdLimit: number; +}; //全部视频列表-datas export type allVideoDatasItem = { - alg: string; - displayed: boolean; - extAlg: null | string; - type: number; - data: { - alg: string; - commentCount: number; - coverUrl: string; - creator: any; - description: null | string; - durationms: number; - hasRelatedGameAd: boolean; - width: number; - height: number; - markTypes: null | any[]; - playTime: number; - praised: boolean; - praisedCount: number; - previewDurationms: number; - previewUrl: null | string; - relateSong: RelativeVideoItem[]; - relatedInfo: null | any; - resolutions: { resolution: number, size: number }[] - scm: string; - shareCount: number; - subscribed: boolean; - threadId: string; - title: string; - urlInfo: null | any; - vid: string; - videoGroup: { id: 58101, name: "听BGM", alg: null }[] - videoUserLiveInfo: null; - } -} + alg: string; + displayed: boolean; + extAlg: null | string; + type: number; + data: { + alg: string; + commentCount: number; + coverUrl: string; + creator: any; + description: null | string; + durationms: number; + hasRelatedGameAd: boolean; + width: number; + height: number; + markTypes: null | any[]; + playTime: number; + praised: boolean; + praisedCount: number; + previewDurationms: number; + previewUrl: null | string; + relateSong: RelativeVideoItem[]; + relatedInfo: null | any; + resolutions: { resolution: number; size: number }[]; + scm: string; + shareCount: number; + subscribed: boolean; + threadId: string; + title: string; + urlInfo: null | any; + vid: string; + videoGroup: { id: 58101; name: '听BGM'; alg: null }[]; + videoUserLiveInfo: null; + }; +}; //所有相关视频对象 export type RelativeVideoItem = { - alg: string; - aliaName: null; - coverUrl: string; - creator: { - userId: string | number; - userName: string; - }[]; - durationms: number; - liveRoom: null; - markTypes: null; - playTime: umber; - title: string; - transName: string; - type: number; - vid: string; -} + alg: string; + aliaName: null; + coverUrl: string; + creator: { + userId: string | number; + userName: string; + }[]; + durationms: number; + liveRoom: null; + markTypes: null; + playTime: umber; + title: string; + transName: string; + type: number; + vid: string; +}; //视频点赞、转发、评论数信息对象 export type VideoRelativedInfoItem = { - commentCount: number; - liked: boolean; - likedCount: number; - shareCount: number; -} + commentCount: number; + liked: boolean; + likedCount: number; + shareCount: number; +}; //视频播放源地址 export type VideoPlaybackSourceItem = { - id: string; - payInfo: null; - needPay: boolean; - r: number; - size: number; - url: string; - validityTime: number; -} + id: string; + payInfo: null; + needPay: boolean; + r: number; + size: number; + url: string; + validityTime: number; +}; //视频详细信息 export type VideoDetailInfoItem = { - - advertisement: boolean; - authType: number; - avatarUrl: string; - commentCount: number; - coverUrl: string; - creator: { - accountStatus: number; - authStatus: number; - avatarDetail: null; - avatarUrl: string; - expertTags: null; - experts: ArrayLike; - followed: boolean; - nickname: string; - userId: number; - userType: number; - }; - description: string; - durationms: number; - hasRelatedGameAd: boolean; - width: number; - height: number; - markTypes: any[]; - playTime: number; - praisedCount: number; - publishTime: number; - shareCount: number; - subscribeCount: number; - threadId: string; - title: string; - vid: string; - videoUserLiveInfo: null; - resolutions: Array<{ - size: number; - resolution: number - }>; - videoGroup: Array<{ - id: number | string; - name: string; - alg: null - }>; -} + advertisement: boolean; + authType: number; + avatarUrl: string; + commentCount: number; + coverUrl: string; + creator: { + accountStatus: number; + authStatus: number; + avatarDetail: null; + avatarUrl: string; + expertTags: null; + experts: ArrayLike; + followed: boolean; + nickname: string; + userId: number; + userType: number; + }; + description: string; + durationms: number; + hasRelatedGameAd: boolean; + width: number; + height: number; + markTypes: any[]; + playTime: number; + praisedCount: number; + publishTime: number; + shareCount: number; + subscribeCount: number; + threadId: string; + title: string; + vid: string; + videoUserLiveInfo: null; + resolutions: Array<{ + size: number; + resolution: number; + }>; + videoGroup: Array<{ + id: number | string; + name: string; + alg: null; + }>; +}; export type VideoBeLiked = { - mlogBaseData: { - audioDTO: null; - coverColor: number; - coverDynamicUrl: null; - coverHeight: number; - coverPicKey: string; - coverUrl: string; - coverWidth: number; - desc: string; - duration: number; - greatCover: boolean; - id: string; - interveneText: null; - pubTime: number; - talk: null; - text: string; - threadId: string; - type: number; - }, - mlogExtVO: { - algSong: null; - artistName: string; - artists: SingerInfo[]; - canCollect: boolean; - channelTag: null; - commentCount: number; - likedCount: number; - playCount: number; - shareCount: number; - song: any; - specialTag: null | any; - videoStartPlayTime: null | number; - }, - shareUrl: string; - status: number; - userProfile: null | any; -} \ No newline at end of file + mlogBaseData: { + audioDTO: null; + coverColor: number; + coverDynamicUrl: null; + coverHeight: number; + coverPicKey: string; + coverUrl: string; + coverWidth: number; + desc: string; + duration: number; + greatCover: boolean; + id: string; + interveneText: null; + pubTime: number; + talk: null; + text: string; + threadId: string; + type: number; + }; + mlogExtVO: { + algSong: null; + artistName: string; + artists: SingerInfo[]; + canCollect: boolean; + channelTag: null; + commentCount: number; + likedCount: number; + playCount: number; + shareCount: number; + song: any; + specialTag: null | any; + videoStartPlayTime: null | number; + }; + shareUrl: string; + status: number; + userProfile: null | any; +}; diff --git a/src/utils/apiSpecial.ts b/src/utils/apiSpecial.ts index 5f63571..bb2fc03 100644 --- a/src/utils/apiSpecial.ts +++ b/src/utils/apiSpecial.ts @@ -1,81 +1,77 @@ -import usePlayerStore from "@/stores/player"; -import { NewestSongInfo, SongInfo } from "@/types/song"; -import { is } from "@utils/index"; -import { getLocaleCount } from "./calc"; -import { getLocaleDate, second2TimeStr } from "./time"; +/** @format */ -export const getFullName = ({ - name, - alias, -}: { - name: string; - alias?: string[]; -}) => { - return `${name}${alias?.length ? `(${alias.join("、")})` : ""}`; +import usePlayerStore from '@/stores/player'; +import { NewestSongInfo, SongInfo } from '@/types/song'; +import { is } from '@utils/index'; +import { getLocaleCount } from './calc'; +import { getLocaleDate, second2TimeStr } from './time'; + +export const getFullName = ({ name, alias }: { name: string; alias?: string[] }) => { + return `${name}${alias?.length ? `(${alias.join('、')})` : ''}`; }; export const getFullNames = (args: FuncParamsType[0][]) => { - return args.map(getFullName).join("、"); + return args.map(getFullName).join('、'); }; /** * 获取歌曲附加的信息 */ export type ExtraSongInfo = { - artists: SongInfo["ar"]; - publishTime?: number; - mark?: number; - name: string; - duration: number; - alias: string[]; - album: { - name: string; - alias?: string[]; - }; + artists: SongInfo['ar']; + publishTime?: number; + mark?: number; + name: string; + duration: number; + alias: string[]; + album: { + name: string; + alias?: string[]; + }; }; export const getSongExtraInfo = ( - param: ExtraSongInfo + param: ExtraSongInfo, ): { - musicName: string; //完整的音乐名称(本名 + 别名) - albumName: string; //完整的专辑名称(本名 + 别名) - localedMark?: string; //评论数(本地化版) - localedDuration: string; //时长(本地化版) - localedPublishTime?: string; //发布时间(本地化版) - singers: Pick[]; //音乐的作者(歌手)信息 + musicName: string; //完整的音乐名称(本名 + 别名) + albumName: string; //完整的专辑名称(本名 + 别名) + localedMark?: string; //评论数(本地化版) + localedDuration: string; //时长(本地化版) + localedPublishTime?: string; //发布时间(本地化版) + singers: Pick[]; //音乐的作者(歌手)信息 } => { - const { name, mark, alias, duration, publishTime, artists, album } = param; - const musicName = getFullName({ name, alias }); - const albumName = getFullName(album); - const localedMark = is.number(mark) ? getLocaleCount(mark) : void 0; - const singers = artists.map(({ id, name, alias }) => ({ - id, - name: getFullName({ name, alias }), - })); - const localedDuration = second2TimeStr(duration / 1000); - const localedPublishTime = - publishTime != null - ? getLocaleDate(publishTime, { - delimiter: "-", - divide: "day", - }) - : "未知"; - return { - musicName, - albumName, - localedMark, - singers, - localedDuration, - localedPublishTime, - }; + const { name, mark, alias, duration, publishTime, artists, album } = param; + const musicName = getFullName({ name, alias }); + const albumName = getFullName(album); + const localedMark = is.number(mark) ? getLocaleCount(mark) : void 0; + const singers = artists.map(({ id, name, alias }) => ({ + id, + name: getFullName({ name, alias }), + })); + const localedDuration = second2TimeStr(duration / 1000); + const localedPublishTime = + publishTime != null + ? getLocaleDate(publishTime, { + delimiter: '-', + divide: 'day', + }) + : '未知'; + return { + musicName, + albumName, + localedMark, + singers, + localedDuration, + localedPublishTime, + }; }; /** * 当前播放歌曲信息 */ export type CurrentSongInfo = - | ReturnType - | ReturnType; + | ReturnType + | ReturnType; /** * 获取修正后的songInfo @@ -84,38 +80,38 @@ export type CurrentSongInfo = */ export const getModifiedSongInfo = (songInfo: SongInfo) => { - const { - ar: artists, - name, - alia: alias, - dt: duration, - publishTime, - mark, - id, - starred, - al: album, - } = songInfo; + const { + ar: artists, + name, + alia: alias, + dt: duration, + publishTime, + mark, + id, + starred, + al: album, + } = songInfo; - return { - id, - name, - mark, - alias, - album, - artists, - starred, - duration, - publishTime, - ...getSongExtraInfo({ - artists, - alias, - duration, - publishTime, - mark, - name, - album, - }), - }; + return { + id, + name, + mark, + alias, + album, + artists, + starred, + duration, + publishTime, + ...getSongExtraInfo({ + artists, + alias, + duration, + publishTime, + mark, + name, + album, + }), + }; }; /** @@ -124,35 +120,35 @@ export const getModifiedSongInfo = (songInfo: SongInfo) => { * @returns */ export const getModifiedNewestSongInfo = (newestSongInfo: NewestSongInfo) => { - const { - artists, - name, - alias, - duration, - id, - fee, - album, - starredNum, - starred, - mvid, - playedNum, - popularity, - } = newestSongInfo; - const { publishTime } = album; - return { - id, - fee, - name, - mvid, - alias, - album, - artists, - starred, - starredNum, - duration, - publishTime, - playedNum, - popularity, - ...getSongExtraInfo({ artists, name, alias, duration, publishTime, album }), - }; + const { + artists, + name, + alias, + duration, + id, + fee, + album, + starredNum, + starred, + mvid, + playedNum, + popularity, + } = newestSongInfo; + const { publishTime } = album; + return { + id, + fee, + name, + mvid, + alias, + album, + artists, + starred, + starredNum, + duration, + publishTime, + playedNum, + popularity, + ...getSongExtraInfo({ artists, name, alias, duration, publishTime, album }), + }; }; diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 5f35ea2..518c190 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,34 +1,38 @@ +/** @format */ + import Cookies from 'js-cookie'; import { is, trim } from './common'; export const loginCookie = (() => { - let loginCookie: string; - const cookieKey = 'RefrainMusic' - return { - get value() { - if (!loginCookie) { - const cookie = Cookies.get(cookieKey); - const MUSIC_C = cookie?.split(/\s*;\s*/).find(v => v.trim().startsWith('MUSIC_U')); - return loginCookie = !MUSIC_C ? '' : encodeURIComponent(MUSIC_C); - } - return loginCookie; - }, - set value(val: string) { - if (!is.string(val)) { - return; - } - loginCookie = val; - if (!val) { - this.remove(); - return; - } - // 给手动保存的登陆cookie设置180天过期 - Cookies.set(cookieKey, val, { - expires: 180, - }); - }, - remove() { - Cookies.remove(cookieKey) - } - } + let loginCookie: string; + const cookieKey = 'RefrainMusic'; + return { + get value() { + if (!loginCookie) { + const cookie = Cookies.get(cookieKey); + const MUSIC_C = cookie + ?.split(/\s*;\s*/) + .find((v) => v.trim().startsWith('MUSIC_U')); + return (loginCookie = !MUSIC_C ? '' : encodeURIComponent(MUSIC_C)); + } + return loginCookie; + }, + set value(val: string) { + if (!is.string(val)) { + return; + } + loginCookie = val; + if (!val) { + this.remove(); + return; + } + // 给手动保存的登陆cookie设置180天过期 + Cookies.set(cookieKey, val, { + expires: 180, + }); + }, + remove() { + Cookies.remove(cookieKey); + }, + }; })(); diff --git a/src/utils/battery.ts b/src/utils/battery.ts index f0d2cd7..4474389 100644 --- a/src/utils/battery.ts +++ b/src/utils/battery.ts @@ -1,19 +1,20 @@ -import EventDispatcher from "./event/event"; +/** @format */ + +import EventDispatcher from './event/event'; export type BatteryManagerEvents = - "onlevel" | - "onchargingchange" | - "onchargingtimechange" | - "ondischargingtimechange"; - -export interface BatteryManager extends EventTarget, Record< - BatteryManagerEvents, - null | ((battery: BatteryManager) => void) -> { - charging: boolean; - chargingTime: number; - dischargingTime: number; - level: number; + | 'onlevel' + | 'onchargingchange' + | 'onchargingtimechange' + | 'ondischargingtimechange'; + +export interface BatteryManager + extends EventTarget, + Record void)> { + charging: boolean; + chargingTime: number; + dischargingTime: number; + level: number; } /** @@ -22,77 +23,62 @@ export interface BatteryManager extends EventTarget, Record< */ export const getBattery = async () => { - let battery: PlainObject = {}; - try { - battery = await navigator.getBattery(); - } catch (err) { - battery = - navigator.battery || navigator.mozBattery || navigator.webkitBattery; - } - return battery; + let battery: PlainObject = {}; + try { + battery = await navigator.getBattery(); + } catch (err) { + battery = navigator.battery || navigator.mozBattery || navigator.webkitBattery; + } + return battery; }; export const BATTERY_CHARGSTATUS_HOOKS = [ - "levelChange", - "chargingChange", - "chargingTimeChange", - "dischargingTimeChange", + 'levelChange', + 'chargingChange', + 'chargingTimeChange', + 'dischargingTimeChange', ]; export class BatteryMaster extends EventDispatcher { + public battery!: PlainObject; - public battery!: PlainObject; - - public getBattery: () => Promise = getBattery; - - public isSupported!: boolean; - - //电池是否正在充电 - public charging!: boolean; - - //距离充满还要多久时间(单位:秒。为0时,表示已充满) - public chargingTime!: number; - - //表示距离电池还可用多久时间(单位:秒) - public dischargingTime!: number; - - //电池还剩多少容量(范围:0-1) - public level!: number; - - constructor() { - super('battery'); - } - - public async asyncInitialize() { + public getBattery: () => Promise = getBattery; - const battery = await this.getBattery(); + public isSupported!: boolean; - if ((this.isSupported = !!battery)) { + //电池是否正在充电 + public charging!: boolean; - const { level, charging, chargingTime, dischargingTime } = this.battery = battery; - this.level = level; - this.charging = charging; - this.chargingTime = chargingTime; - this.dischargingTime = dischargingTime; + //距离充满还要多久时间(单位:秒。为0时,表示已充满) + public chargingTime!: number; - BATTERY_CHARGSTATUS_HOOKS.forEach((hook) => { + //表示距离电池还可用多久时间(单位:秒) + public dischargingTime!: number; - battery.addEventListener( - hook.toLocaleLowerCase(), - (e: Event) => { - const attr = hook.replace("Change", ""); - Reflect.set( - this, - attr, - Reflect.get(e.target as BatteryManager, attr) - ); - this.dispatch(hook, this); - }); + //电池还剩多少容量(范围:0-1) + public level!: number; - }); + constructor() { + super('battery'); + } - } + public async asyncInitialize() { + const battery = await this.getBattery(); - } + if ((this.isSupported = !!battery)) { + const { level, charging, chargingTime, dischargingTime } = (this.battery = battery); + this.level = level; + this.charging = charging; + this.chargingTime = chargingTime; + this.dischargingTime = dischargingTime; + BATTERY_CHARGSTATUS_HOOKS.forEach((hook) => { + battery.addEventListener(hook.toLocaleLowerCase(), (e: Event) => { + const attr = hook.replace('Change', ''); + Reflect.set(this, attr, Reflect.get(e.target as BatteryManager, attr)); + this.dispatch(hook, this); + }); + }); + } + } } diff --git a/src/utils/browser.ts b/src/utils/browser.ts index 44cbc51..54f5f1b 100644 --- a/src/utils/browser.ts +++ b/src/utils/browser.ts @@ -1,67 +1,65 @@ -import { isPrototypeOf } from "./constants"; -import { typeOf } from "./index"; +/** @format */ + +import { isPrototypeOf } from './constants'; +import { typeOf } from './index'; export const closest = ( - ele: Element, - tar: Element | ArrayLike | string + ele: Element, + tar: Element | ArrayLike | string, ): Element | null | never => { - //不写Element.prototype.isPrototypeOf(ele)原因见: - // http://eslint.cn/docs/rules/no-prototype-builtins - - if (!isPrototypeOf.call(Element.prototype, ele)) { - throw new TypeError(`${ele} is not a Element!`); - } - - const elArr = (() => { - if (tar instanceof Element) { - return [tar]; - } - try { - tar = document.querySelectorAll(tar as string); - } finally { - const tarTypeOptions = ["nodelist", "htmlcollection", "array"]; - if (tarTypeOptions.includes(typeOf(tar))) { - return Array.from(tar as ArrayLike); - } - return []; - } - })(); - do { - if (elArr.includes(ele)) return ele; - ele = ele.parentNode as Element; - } while (ele !== null); - - return null; + //不写Element.prototype.isPrototypeOf(ele)原因见: + // http://eslint.cn/docs/rules/no-prototype-builtins + + if (!isPrototypeOf.call(Element.prototype, ele)) { + throw new TypeError(`${ele} is not a Element!`); + } + + const elArr = (() => { + if (tar instanceof Element) { + return [tar]; + } + try { + tar = document.querySelectorAll(tar as string); + } finally { + const tarTypeOptions = ['nodelist', 'htmlcollection', 'array']; + if (tarTypeOptions.includes(typeOf(tar))) { + return Array.from(tar as ArrayLike); + } + return []; + } + })(); + do { + if (elArr.includes(ele)) return ele; + ele = ele.parentNode as Element; + } while (ele !== null); + + return null; }; //获取元素宽高、以及距离页面边缘的距离 export const getElmRectInfo = (el: Element | null): PlainObject => { - if (!(el instanceof Element)) { - throw new TypeError(`${el} is not a Element!`); - } - - const elm = el as Element; - if (!elm.getClientRects().length) { - return { left: 0, top: 0, width: 0, height: 0 }; - } - const elmRectInfo = elm.getBoundingClientRect(); - const win = elm.ownerDocument.defaultView as typeof globalThis; - return { - left: elmRectInfo.left + win.pageXOffset, - top: elmRectInfo.top + win.pageYOffset, - width: elmRectInfo.width, - height: elmRectInfo.height, - }; + if (!(el instanceof Element)) { + throw new TypeError(`${el} is not a Element!`); + } + + const elm = el as Element; + if (!elm.getClientRects().length) { + return { left: 0, top: 0, width: 0, height: 0 }; + } + const elmRectInfo = elm.getBoundingClientRect(); + const win = elm.ownerDocument.defaultView as typeof globalThis; + return { + left: elmRectInfo.left + win.pageXOffset, + top: elmRectInfo.top + win.pageYOffset, + width: elmRectInfo.width, + height: elmRectInfo.height, + }; }; -export const computedStyle = ( - el: Element, - attr: string, - pseudo: string | null = null -) => - (el.ownerDocument.defaultView as typeof globalThis) - .getComputedStyle(el, pseudo) - .getPropertyValue(attr); +export const computedStyle = (el: Element, attr: string, pseudo: string | null = null) => + (el.ownerDocument.defaultView as typeof globalThis) + .getComputedStyle(el, pseudo) + .getPropertyValue(attr); /** * 获取当前鼠标位置距离某DOM元素边缘的距离 @@ -70,99 +68,94 @@ export const computedStyle = ( * @returns */ export const getPointerOffsetElm = (ev: MouseEvent, elm: Element) => { - const { pageX, pageY } = ev; - const { left, top, width, height } = getElmRectInfo(elm); - return { - offsetX: pageX - left, - offsetY: pageY - top, - width, - height, - }; + const { pageX, pageY } = ev; + const { left, top, width, height } = getElmRectInfo(elm); + return { + offsetX: pageX - left, + offsetY: pageY - top, + width, + height, + }; }; /** * 获取elm2到elm1的边缘距离 - * @param elm1 - * @param elm2 - * @returns + * @param elm1 + * @param elm2 + * @returns */ export const getElmOffsetToElm = (elm1: Element, elm2: Element) => { - const { left: left1, top: top1 } = getElmRectInfo(elm1); - const { left: left2, top: top2 } = getElmRectInfo(elm2); - return { - offsetLeft: left2 - left1, - offsetTop: top2 - top1, - } -} + const { left: left1, top: top1 } = getElmRectInfo(elm1); + const { left: left2, top: top2 } = getElmRectInfo(elm2); + return { + offsetLeft: left2 - left1, + offsetTop: top2 - top1, + }; +}; /** * 获取路由地址(不包含origin) - * @returns + * @returns */ export const getHrefWithoutOrigin = () => { - const { href, origin } = location; - return decodeURIComponent(href).replace(origin, ''); -} + const { href, origin } = location; + return decodeURIComponent(href).replace(origin, ''); +}; /** * 元素是否具有滚动条 - * @param el - * @param isVertical - * @returns + * @param el + * @param isVertical + * @returns */ export const isScroll = ( - el: HTMLElement, - isVertical?: boolean + el: HTMLElement, + isVertical?: boolean, ): RegExpMatchArray | null => { - const determinedDirection = isVertical === null || isVertical === undefined - const overflow = determinedDirection - ? computedStyle(el, 'overflow') - : isVertical - ? computedStyle(el, 'overflow-y') - : computedStyle(el, 'overflow-x') - return overflow.match(/(scroll|auto|overlay)/); -} + const determinedDirection = isVertical === null || isVertical === undefined; + const overflow = determinedDirection + ? computedStyle(el, 'overflow') + : isVertical + ? computedStyle(el, 'overflow-y') + : computedStyle(el, 'overflow-x'); + return overflow.match(/(scroll|auto|overlay)/); +}; /** * 获取具有scroll滚动条的父元素 - * @param el - * @param isVertical - * @returns + * @param el + * @param isVertical + * @returns */ export const getScrollContainer = ( - el: HTMLElement, - isVertical?: boolean + el: HTMLElement, + isVertical?: boolean, ): Window | HTMLElement => { - let parent: HTMLElement = el - while (parent) { - if ([window, document, document.documentElement].includes(parent)) { - return window - } - if (isScroll(parent, isVertical)) { - return parent - } - parent = parent.parentNode as HTMLElement - } - return parent -} + let parent: HTMLElement = el; + while (parent) { + if ([window, document, document.documentElement].includes(parent)) { + return window; + } + if (isScroll(parent, isVertical)) { + return parent; + } + parent = parent.parentNode as HTMLElement; + } + return parent; +}; export const getOffsetTop = (el: HTMLElement) => { - let offset = 0 - let parent = el - - while (parent) { - offset += parent.offsetTop - parent = parent.offsetParent as HTMLElement - } + let offset = 0; + let parent = el; - return offset -} - -export const getOffsetTopDistance = ( - el: HTMLElement, - containerEl: HTMLElement -) => { - return Math.abs(getOffsetTop(el) - getOffsetTop(containerEl)) -} + while (parent) { + offset += parent.offsetTop; + parent = parent.offsetParent as HTMLElement; + } + return offset; +}; +export const getOffsetTopDistance = (el: HTMLElement, containerEl: HTMLElement) => { + return Math.abs(getOffsetTop(el) - getOffsetTop(containerEl)); +}; diff --git a/src/utils/calc.ts b/src/utils/calc.ts index 8d6e7e1..8632214 100644 --- a/src/utils/calc.ts +++ b/src/utils/calc.ts @@ -1,43 +1,42 @@ -import { - is -} from './common'; +/** @format */ + +import { is } from './common'; export const getRanInteger = (min: number, max: number) => - ~~(Math.random() * (max - min + 1) + min); + ~~(Math.random() * (max - min + 1) + min); export const getRanDigit = (min: number, max: number) => - Math.random() * (max - min) + min; + Math.random() * (max - min) + min; export const decimalToPercent = (decimal: number, fixed = 0, sign = true) => { - if (!is.number(+decimal)) { - throw new TypeError(`the ${decimal} is an invalid parameter`); - } - return `${(decimal * 100).toFixed(fixed)}${sign ? "%" : ""}`; + if (!is.number(+decimal)) { + throw new TypeError(`the ${decimal} is an invalid parameter`); + } + return `${(decimal * 100).toFixed(fixed)}${sign ? '%' : ''}`; }; export const percentToDecimal = (percent: string | number) => { - percent = String(percent).trim(); - const percentPatt = percent.match(/(%)$/); - !is.null(percentPatt) && - (percent = percent.replace((percentPatt as RegExpMatchArray)[1], "")); - return +percent / 100; + percent = String(percent).trim(); + const percentPatt = percent.match(/(%)$/); + !is.null(percentPatt) && + (percent = percent.replace((percentPatt as RegExpMatchArray)[1], '')); + return +percent / 100; }; -export const rmDemicalInPercent = (percent: string) => - percent.replace(/\.\d*/, ""); +export const rmDemicalInPercent = (percent: string) => percent.replace(/\.\d*/, ''); export const getLocaleCount = (count: number, fixed = 2) => { - const arr = [ - { key: "亿", digit: 8 }, - { key: "万", digit: 4 }, - // { key: '千', digit: 3 }, - // { key: '百', digit: 2 }, - ]; - for (const { key, digit } of arr) { - const val = count / +`1${"0".repeat(digit)}`; - if (val >= 1) { - return val.toFixed(fixed) + key; - } - } - return String(count); + const arr = [ + { key: '亿', digit: 8 }, + { key: '万', digit: 4 }, + // { key: '千', digit: 3 }, + // { key: '百', digit: 2 }, + ]; + for (const { key, digit } of arr) { + const val = count / +`1${'0'.repeat(digit)}`; + if (val >= 1) { + return val.toFixed(fixed) + key; + } + } + return String(count); }; diff --git a/src/utils/common.ts b/src/utils/common.ts index 55edaa0..67a191b 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -1,253 +1,249 @@ -import { toString, isPrototypeOf, isExtensible, EMPTY_OBJ, hasOwnProperty } from "./constants"; +/** @format */ + +import { + toString, + isPrototypeOf, + isExtensible, + EMPTY_OBJ, + hasOwnProperty, +} from './constants'; import ColorThief from '@assets/js/colorthief'; -export const typeOf = (ins: any): string => - toString.call(ins).slice(8, -1).toLowerCase(); +export const typeOf = (ins: any): string => toString.call(ins).slice(8, -1).toLowerCase(); export const is = { - string: (ins: any): ins is string => typeof ins === "string", - undefined: (ins: any): ins is undefined => ins === void 0, - data: (ins: any): ins is Date => typeOf(ins) === "data", - regexp: (ins: any): ins is RegExp => typeOf(ins) === "regexp", - boolean: (ins: any): ins is boolean => typeof ins === "boolean", - null: (ins: any): ins is null => ins === null, - function: (ins: any): ins is (...args: any[]) => any => typeof ins === "function", - number: (ins: any): ins is number => typeof ins === "number", - emptyArray: (ins: any): ins is Array => - is.array(ins) && ins.length === 0, - emptyObject: (ins: any): ins is PlainObject => - is.object(ins) && ins.length === 0, - object: (ins: any): ins is PlainObject => - typeof ins === "object" && ins !== null, - array: (ins: any): ins is Array => Array.isArray(ins), + string: (ins: any): ins is string => typeof ins === 'string', + undefined: (ins: any): ins is undefined => ins === void 0, + data: (ins: any): ins is Date => typeOf(ins) === 'data', + regexp: (ins: any): ins is RegExp => typeOf(ins) === 'regexp', + boolean: (ins: any): ins is boolean => typeof ins === 'boolean', + null: (ins: any): ins is null => ins === null, + function: (ins: any): ins is (...args: any[]) => any => typeof ins === 'function', + number: (ins: any): ins is number => typeof ins === 'number', + emptyArray: (ins: any): ins is Array => is.array(ins) && ins.length === 0, + emptyObject: (ins: any): ins is PlainObject => is.object(ins) && ins.length === 0, + object: (ins: any): ins is PlainObject => + typeof ins === 'object' && ins !== null, + array: (ins: any): ins is Array => Array.isArray(ins), }; export const isURL = (url: string) => - /(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/.test(url); + /(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/.test(url); //trim去除空格。注:'\uFEFF'即''空字符串 export const trim = function (s: string) { - return (s || "").replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, ""); + return (s || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, ''); }; export const filterUselessKey = (obj: PlainObject = EMPTY_OBJ) => { - Object.keys(obj).forEach((key) => { - if (obj[key] === void 0) { - delete obj[key]; - } - }); - return obj; + Object.keys(obj).forEach((key) => { + if (obj[key] === void 0) { + delete obj[key]; + } + }); + return obj; }; export function deepCopy>( - obj: T, - hash: WeakMap = new WeakMap() + obj: T, + hash: WeakMap = new WeakMap(), ): Writeable { - if (hash.has(obj)) return hash.get(obj); - const types = [Set, Map, Date, RegExp]; - const Ctor = obj.constructor as any; - if (types.includes(Ctor)) return new Ctor(obj); - const attrsDesc = Object.getOwnPropertyDescriptors(obj); - //如果是不可拓展的(比如为只读属性的),就设为可读 - if (!isExtensible(obj)) { - for (let i = 0, len = attrsDesc.length.value; i < len; ++i) { - const curAttrDesc = attrsDesc[i]; - curAttrDesc.configurable = true; - curAttrDesc.writable = true; - } - attrsDesc.length.writable = true; - } - const copyObj = Object.create(Object.getPrototypeOf(obj), attrsDesc); - hash.set(obj, copyObj); - for (const key of Reflect.ownKeys(copyObj)) { - const curVal = copyObj[key]; - if (Object(curVal) !== curVal || is.function(curVal)) { - continue; - } - copyObj[key] = deepCopy(curVal, hash); - } - return copyObj; + if (hash.has(obj)) return hash.get(obj); + const types = [Set, Map, Date, RegExp]; + const Ctor = obj.constructor as any; + if (types.includes(Ctor)) return new Ctor(obj); + const attrsDesc = Object.getOwnPropertyDescriptors(obj); + //如果是不可拓展的(比如为只读属性的),就设为可读 + if (!isExtensible(obj)) { + for (let i = 0, len = attrsDesc.length.value; i < len; ++i) { + const curAttrDesc = attrsDesc[i]; + curAttrDesc.configurable = true; + curAttrDesc.writable = true; + } + attrsDesc.length.writable = true; + } + const copyObj = Object.create(Object.getPrototypeOf(obj), attrsDesc); + hash.set(obj, copyObj); + for (const key of Reflect.ownKeys(copyObj)) { + const curVal = copyObj[key]; + if (Object(curVal) !== curVal || is.function(curVal)) { + continue; + } + copyObj[key] = deepCopy(curVal, hash); + } + return copyObj; } //-连接符形式转成小驼峰 export const camelize = (str: string) => - str - .replace(/^[_.\- ]+/, "") - .toLowerCase() - .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase()); + str + .replace(/^[_.\- ]+/, '') + .toLowerCase() + .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase()); export const merge = (defaults: PlainObject, options: PlainObject) => { - for (const key in defaults) { - if (hasOwnProperty.call(defaults, key)) { - options[key] === void 0 && (options[key] = defaults[key]); - } - } - return options; + for (const key in defaults) { + if (hasOwnProperty.call(defaults, key)) { + options[key] === void 0 && (options[key] = defaults[key]); + } + } + return options; }; -export const override = ( - target: PlainObject, - source: PlainObject -): PlainObject => { - Object.keys(source).forEach((key) => (target[key] = source[key])); - return target; +export const override = (target: PlainObject, source: PlainObject): PlainObject => { + Object.keys(source).forEach((key) => (target[key] = source[key])); + return target; }; //extend function extendFn(target: PlainObject): PlainObject; function extendFn(target: PlainObject, source: PlainObject): PlainObject; function extendFn(target?: PlainObject, source?: PlainObject): PlainObject { - is.undefined(target) && (target = {}); - is.undefined(source) && (source = {}); - const cloneSource = { ...source }; - const cloneTarget = { ...target }; - Object.keys(cloneSource).forEach((key) => { - const val = cloneSource[key]; - if (cloneTarget[key] === void 0) { - cloneTarget[key] = val; - } else if (is.object(val)) { - extendFn(cloneTarget[key], val); - } - }); - return override(target as PlainObject, cloneTarget); + is.undefined(target) && (target = {}); + is.undefined(source) && (source = {}); + const cloneSource = { ...source }; + const cloneTarget = { ...target }; + Object.keys(cloneSource).forEach((key) => { + const val = cloneSource[key]; + if (cloneTarget[key] === void 0) { + cloneTarget[key] = val; + } else if (is.object(val)) { + extendFn(cloneTarget[key], val); + } + }); + return override(target as PlainObject, cloneTarget); } export const extend = extendFn; export const isAllChinChar = (char: string) => /^[\u4e00-\u9fa5]+$/.test(char); -export const containChinChar = (char: string) => - /.*[\u4e00-\u9fa5]+.*/.test(char); +export const containChinChar = (char: string) => /.*[\u4e00-\u9fa5]+.*/.test(char); //给字符串的每一位前添加指定内容 export const padStartEveryChar = (str: string, char: string) => - str.replace(/(.){1}/g, `${char}$1`); + str.replace(/(.){1}/g, `${char}$1`); export const throttle = (fn: (...args: any[]) => any, interval = 1000) => { - let prev = 0; - return function (this: any, ...args: any[]) { - const now = performance.now(); - if (now - prev > interval) { - fn.apply(this, args); - } else { - prev = now; - } - }; + let prev = 0; + return function (this: any, ...args: any[]) { + const now = performance.now(); + if (now - prev > interval) { + fn.apply(this, args); + } else { + prev = now; + } + }; }; export const fileDownloader = (url: any, filename: string) => { - return fetch(url) - .then( - (res) => res.blob(), - (err) => false - ) - .then((blob) => { - if (is.boolean(blob)) return false; - const a = document.createElement("a"); - a.download = filename; - a.hidden = true; - document.body.appendChild(a); - const downloadUrl = URL.createObjectURL(blob); - a.href = downloadUrl; - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(downloadUrl); - return true; - }); + return fetch(url) + .then( + (res) => res.blob(), + (err) => false, + ) + .then((blob) => { + if (is.boolean(blob)) return false; + const a = document.createElement('a'); + a.download = filename; + a.hidden = true; + document.body.appendChild(a); + const downloadUrl = URL.createObjectURL(blob); + a.href = downloadUrl; + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(downloadUrl); + return true; + }); }; export const objToPathname = (obj: PlainObject, prefix = true) => - is.emptyObject(obj) - ? "" - : `${prefix ? '/' : ''}${Object.values(obj).join('/')}`; - + is.emptyObject(obj) ? '' : `${prefix ? '/' : ''}${Object.values(obj).join('/')}`; export const objToQuery = (obj: PlainObject, prefix = false) => - is.emptyObject(obj) - ? "" - : `${prefix ? "?" : ""}${Object.entries(obj) - .map((arr) => arr.join("=")) - .join("&")}`; - -export const padPicCrop = ( - picUrl: string, - { x, y }: Record<"x" | "y", number> -) => picUrl + `?param=${x}y${y}`; - -export const randomSorter = (a: any, b: any) => Math.random() - .5; - -export const getRandomList = (list: T[], sorter: (a: T, b: T) => any = randomSorter) => { - return [...list].sort(sorter); -} + is.emptyObject(obj) + ? '' + : `${prefix ? '?' : ''}${Object.entries(obj) + .map((arr) => arr.join('=')) + .join('&')}`; + +export const padPicCrop = (picUrl: string, { x, y }: Record<'x' | 'y', number>) => + picUrl + `?param=${x}y${y}`; + +export const randomSorter = (a: any, b: any) => Math.random() - 0.5; + +export const getRandomList = ( + list: T[], + sorter: (a: T, b: T) => any = randomSorter, +) => { + return [...list].sort(sorter); +}; /** * 是否为复杂数据类型 - * @param data - * @returns + * @param data + * @returns */ export const isReferenceType = (data: any) => { - return Object(data) === data; -} + return Object(data) === data; +}; /** * 判断两个参数是否相同(宽松型比对) - * @param args - * @returns + * @param args + * @returns * @introduction 相同,即长得一样,而不是内存地址一致 */ export const isLooseEqual = (...args: [any, any]) => { - if (args.every(isReferenceType)) { - return JSON.stringify(args[0]) === JSON.stringify(args[1]); - } - else { - return Object.is(...args); - } -} + if (args.every(isReferenceType)) { + return JSON.stringify(args[0]) === JSON.stringify(args[1]); + } else { + return Object.is(...args); + } +}; /** * 获取图片的主色(数组形式) - * @param imgUrl - * @returns + * @param imgUrl + * @returns */ const colorthief = new ColorThief(); export const getImageMainColor = (imgUrl: string) => { - return new Promise>( - (resolve, reject) => { - if (!imgUrl) { - resolve([]); - return; - } - const img = new Image(); - img.src = imgUrl; - img.crossOrigin = "anonymous"; - img.onload = () => { - resolve(colorthief.getColor(img)); - }; - img.onerror = () => { - reject("load error!"); - }; - } - ); + return new Promise>((resolve, reject) => { + if (!imgUrl) { + resolve([]); + return; + } + const img = new Image(); + img.src = imgUrl; + img.crossOrigin = 'anonymous'; + img.onload = () => { + resolve(colorthief.getColor(img)); + }; + img.onerror = () => { + reject('load error!'); + }; + }); }; /** * 获取图片的主色(rgb形式) - * @param imgUrl - * @returns + * @param imgUrl + * @returns */ export const getImageMainColorString = async (imgUrl: string) => { - const colorArr = await getImageMainColor(imgUrl); - return `rgb(${colorArr.join(',')})`; -} + const colorArr = await getImageMainColor(imgUrl); + return `rgb(${colorArr.join(',')})`; +}; /** * 生成formData表单数据 - * @param params - * @returns + * @param params + * @returns */ export const generateFormData = (params: PlainObject) => { - const formData = new FormData(); - Object.entries(filterUselessKey(params)).forEach(([key, value]) => { - formData.append(key, value); - }); - return formData; -} \ No newline at end of file + const formData = new FormData(); + Object.entries(filterUselessKey(params)).forEach(([key, value]) => { + formData.append(key, value); + }); + return formData; +}; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 0b5b02d..6cf838d 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,6 +1,8 @@ -import { getCountryCode } from "@api/other"; +/** @format */ -export const NOOP = function (a?: any, b?: any, c?: any): any { }; +import { getCountryCode } from '@api/other'; + +export const NOOP = function (a?: any, b?: any, c?: any): any {}; export const EMPTY_OBJ = Object.create(null); @@ -8,27 +10,29 @@ export const EMPTY_ARR = []; export const { toString, isPrototypeOf, hasOwnProperty } = Object.prototype; -export const { freeze, seal, preventExtensions, isExtensible, } = Object; +export const { freeze, seal, preventExtensions, isExtensible } = Object; export const UNICODE_CHAR = { - smile: `\u{1F60A}`, - pensive: `\u{1F614}`, - hugface: `\u{1F917}`, - registed: `\u{00AE}`, + smile: `\u{1F60A}`, + pensive: `\u{1F614}`, + hugface: `\u{1F917}`, + registed: `\u{00AE}`, }; /** * 电话号码正则 */ -export const phoneVerifyPatt = /^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[01356789]\d{2}|4(?:0\d|1[0-2]|9\d))|9[189]\d{2}|6[567]\d{2}|4(?:[14]0\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/; +export const phoneVerifyPatt = + /^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[01356789]\d{2}|4(?:0\d|1[0-2]|9\d))|9[189]\d{2}|6[567]\d{2}|4(?:[14]0\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/; /** * 邮箱正则 */ -export const emailVerifyPatt = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +export const emailVerifyPatt = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; //国家编号(用于电话号码前缀) export const countryCodes = (async () => { - const { data = EMPTY_OBJ } = await getCountryCode(); - return data; -})(); \ No newline at end of file + const { data = EMPTY_OBJ } = await getCountryCode(); + return data; +})(); diff --git a/src/utils/event/event.ts b/src/utils/event/event.ts index bdc7942..3ba6a81 100644 --- a/src/utils/event/event.ts +++ b/src/utils/event/event.ts @@ -1,82 +1,84 @@ -import { is } from "../common"; +/** @format */ + +import { is } from '../common'; export interface EventFun { - (...args: any[]): any; + (...args: any[]): any; } export interface EventFunOnce extends EventFun { - source: EventFun; + source: EventFun; } export type EventStore = PlainObject<(EventFun | EventFunOnce)[]>; interface EventDispatcherClass - extends Record< - "on" | "off" | "once" | "dispatch", - (type: string, cb: EventFun) => void - > { } + extends Record< + 'on' | 'off' | 'once' | 'dispatch', + (type: string, cb: EventFun) => void + > {} export default class EventDispatcher implements EventDispatcherClass { - public store: EventStore = {}; - - constructor(public name: string) { - this.name = name; - } - - on(type: string, cb: EventFun | EventFunOnce) { - if (!is.function(cb)) { - throw new TypeError(`The parameter of 'on' must be a function`); - } - - const { store } = this; - const targetStore = store[type]; - - if (is.undefined(targetStore)) { - store[type] = [cb]; - return; - } - - !targetStore.includes(cb) && targetStore.push(cb); - } - - off(type: string, cb: EventFun) { - if (!is.function(cb)) { - throw new TypeError(`The parameter of 'on' must be a function`); - } - - const { store } = this; - const targetStore = store[type]; - - if (is.emptyArray(targetStore) || is.undefined(targetStore)) { - return; - } - - store[type] = targetStore.filter( - (callback) => callback !== cb || (callback as EventFunOnce).source !== cb - ); - } - - once(type: string, cb: EventFun) { - if (!is.function(cb)) { - throw new TypeError(`The parameter of 'on' must be a function`); - } - - const once: EventFunOnce = () => { - cb.apply(this); - this.off(type, once); - }; - once.source = cb; - - this.on(type, once); - } - - dispatch(type: string, ...args: any[]) { - const { store } = this; - const targetStore = store[type]; - - if (is.emptyArray(targetStore) || is.undefined(targetStore)) { - return; - } - targetStore.forEach((cb) => cb.apply(this, args)); - } + public store: EventStore = {}; + + constructor(public name: string) { + this.name = name; + } + + on(type: string, cb: EventFun | EventFunOnce) { + if (!is.function(cb)) { + throw new TypeError(`The parameter of 'on' must be a function`); + } + + const { store } = this; + const targetStore = store[type]; + + if (is.undefined(targetStore)) { + store[type] = [cb]; + return; + } + + !targetStore.includes(cb) && targetStore.push(cb); + } + + off(type: string, cb: EventFun) { + if (!is.function(cb)) { + throw new TypeError(`The parameter of 'on' must be a function`); + } + + const { store } = this; + const targetStore = store[type]; + + if (is.emptyArray(targetStore) || is.undefined(targetStore)) { + return; + } + + store[type] = targetStore.filter( + (callback) => callback !== cb || (callback as EventFunOnce).source !== cb, + ); + } + + once(type: string, cb: EventFun) { + if (!is.function(cb)) { + throw new TypeError(`The parameter of 'on' must be a function`); + } + + const once: EventFunOnce = () => { + cb.apply(this); + this.off(type, once); + }; + once.source = cb; + + this.on(type, once); + } + + dispatch(type: string, ...args: any[]) { + const { store } = this; + const targetStore = store[type]; + + if (is.emptyArray(targetStore) || is.undefined(targetStore)) { + return; + } + targetStore.forEach((cb) => cb.apply(this, args)); + } } diff --git a/src/utils/event/register.ts b/src/utils/event/register.ts index ed8b70f..64159c9 100644 --- a/src/utils/event/register.ts +++ b/src/utils/event/register.ts @@ -1,3 +1,5 @@ -import EventDispatcher from './event' +/** @format */ + +import EventDispatcher from './event'; export const messageBus = new EventDispatcher('playerBus'); diff --git a/src/utils/index.ts b/src/utils/index.ts index 4991d35..1e41f6d 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,8 +1,10 @@ -export * from "./common"; -export * from "./calc"; -export * from "./time"; -export * from "./constants"; -export * from "./browser"; -export * from "./timeSlice"; -export * from "./event/event"; -export * from "./lyricParser"; +/** @format */ + +export * from './common'; +export * from './calc'; +export * from './time'; +export * from './constants'; +export * from './browser'; +export * from './timeSlice'; +export * from './event/event'; +export * from './lyricParser'; diff --git a/src/utils/lyricParser.ts b/src/utils/lyricParser.ts index 213bf2f..1c5fb83 100644 --- a/src/utils/lyricParser.ts +++ b/src/utils/lyricParser.ts @@ -1,96 +1,98 @@ -import { spliceTime2Second } from "./time"; +/** @format */ + +import { spliceTime2Second } from './time'; export type LrcCommonOrTranslateItem = { - time: number; - timeStr: string; - text: string; + time: number; + timeStr: string; + text: string; }; export interface LrcItem extends LrcCommonOrTranslateItem { - translation: string; + translation: string; } export interface LyricType { - lrcArr: LrcItem[]; - commonLrcArr: LrcCommonOrTranslateItem[]; - translationLrcArr: LrcCommonOrTranslateItem[]; - exist: boolean; - canTranslate: boolean; + lrcArr: LrcItem[]; + commonLrcArr: LrcCommonOrTranslateItem[]; + translationLrcArr: LrcCommonOrTranslateItem[]; + exist: boolean; + canTranslate: boolean; } export interface LyricParserType extends LyricType { - parse(lrc: string): LrcCommonOrTranslateItem[]; + parse(lrc: string): LrcCommonOrTranslateItem[]; } export class LyricParser implements LyricParserType { - static readonly lrcTimeReg = /\[(\d+):(\d+)\.?(\d+)?\]/; + static readonly lrcTimeReg = /\[(\d+):(\d+)\.?(\d+)?\]/; - public exist!: boolean; - public lrcArr: LrcItem[] = []; - public translationLrcArr: LrcCommonOrTranslateItem[] = []; - public commonLrcArr: LrcCommonOrTranslateItem[] = []; - public canTranslate = false; + public exist!: boolean; + public lrcArr: LrcItem[] = []; + public translationLrcArr: LrcCommonOrTranslateItem[] = []; + public commonLrcArr: LrcCommonOrTranslateItem[] = []; + public canTranslate = false; - constructor(lrc: string, transLrc = "") { - this.init(lrc, transLrc); - } + constructor(lrc: string, transLrc = '') { + this.init(lrc, transLrc); + } - init(lrc: string, transLrc: string) { - //移除掉()()等括号,因为一些英文歌词存在格式错误 - const bracketReg = /\(|\)|(|)/g; - this.commonLrcArr = this.parse(lrc.replace(bracketReg, "")); - this.translationLrcArr = this.parse(transLrc.replace(bracketReg, "")); - this.exist = this.commonLrcArr.length !== 0; - this.canTranslate = this.translationLrcArr.length !== 0; - this.mergeToLrcArr(); - } + init(lrc: string, transLrc: string) { + //移除掉()()等括号,因为一些英文歌词存在格式错误 + const bracketReg = /\(|\)|(|)/g; + this.commonLrcArr = this.parse(lrc.replace(bracketReg, '')); + this.translationLrcArr = this.parse(transLrc.replace(bracketReg, '')); + this.exist = this.commonLrcArr.length !== 0; + this.canTranslate = this.translationLrcArr.length !== 0; + this.mergeToLrcArr(); + } - mergeToLrcArr() { - const { commonLrcArr, translationLrcArr } = this; - const lrcArr: LrcItem[] = []; - for (let i = 0, { length } = commonLrcArr; i < length; ++i) { - const { time, text, timeStr } = commonLrcArr[i]; - // const translation = - // translationLrcArr.find(({ time: transTime }) => transTime === time) - // ?.text || ""; - //此处改为判断timeStr是否相等。因为接口返回值可能存在错误,如: - //正常:'01:34:640' 翻译:'01:34:64',毫秒数不对 - const replaceFrontEndZero = (str: string) => str.replace(/^0|0$/g, ""); - const translation = - translationLrcArr.find( - ({ timeStr: transTimeStr }) => - replaceFrontEndZero(transTimeStr) === replaceFrontEndZero(timeStr) - )?.text || ""; - lrcArr.push({ time, text, timeStr, translation }); - } - this.lrcArr = lrcArr; - } + mergeToLrcArr() { + const { commonLrcArr, translationLrcArr } = this; + const lrcArr: LrcItem[] = []; + for (let i = 0, { length } = commonLrcArr; i < length; ++i) { + const { time, text, timeStr } = commonLrcArr[i]; + // const translation = + // translationLrcArr.find(({ time: transTime }) => transTime === time) + // ?.text || ""; + //此处改为判断timeStr是否相等。因为接口返回值可能存在错误,如: + //正常:'01:34:640' 翻译:'01:34:64',毫秒数不对 + const replaceFrontEndZero = (str: string) => str.replace(/^0|0$/g, ''); + const translation = + translationLrcArr.find( + ({ timeStr: transTimeStr }) => + replaceFrontEndZero(transTimeStr) === replaceFrontEndZero(timeStr), + )?.text || ''; + lrcArr.push({ time, text, timeStr, translation }); + } + this.lrcArr = lrcArr; + } - parse(lrc: string): LrcCommonOrTranslateItem[] { - const lyricArr = []; - const lrcSplitArr = lrc.split("\n"); - const { length } = lrcSplitArr; - const { lrcTimeReg } = LyricParser; + parse(lrc: string): LrcCommonOrTranslateItem[] { + const lyricArr = []; + const lrcSplitArr = lrc.split('\n'); + const { length } = lrcSplitArr; + const { lrcTimeReg } = LyricParser; - for (let i = 0; i < length; i++) { - const lrcStr = lrcSplitArr[i]; - const timePatt = lrcTimeReg.exec(lrcStr); - if (timePatt === null) { - continue; - } - const timeText = timePatt[0]; - const timeStr = timeText.slice(1, -1); - const time = +spliceTime2Second - .apply(null, [timePatt[3], timePatt[2], timePatt[1]]) - .toFixed(3); + for (let i = 0; i < length; i++) { + const lrcStr = lrcSplitArr[i]; + const timePatt = lrcTimeReg.exec(lrcStr); + if (timePatt === null) { + continue; + } + const timeText = timePatt[0]; + const timeStr = timeText.slice(1, -1); + const time = +spliceTime2Second + .apply(null, [timePatt[3], timePatt[2], timePatt[1]]) + .toFixed(3); - const text = lrcStr.replace(timeText, "").trim(); - if (text.length === 0) { - continue; - } - lyricArr.push({ time, timeStr, text }); - } + const text = lrcStr.replace(timeText, '').trim(); + if (text.length === 0) { + continue; + } + lyricArr.push({ time, timeStr, text }); + } - return lyricArr; - } + return lyricArr; + } } diff --git a/src/utils/preference.ts b/src/utils/preference.ts index 2b05652..5490be4 100644 --- a/src/utils/preference.ts +++ b/src/utils/preference.ts @@ -1,115 +1,114 @@ /** * 一些组件名称 + * + * @format */ -export enum COMPONENT_NAME { - - ARTIST_ALLSONGS = 'ArtistAllSongs', - ARTIST_ALBUM = 'ArtistAlbum', - ARTIST_MV = 'ArtistMv', - MUSICHALL_SONGLIST = 'MusichallSonglist', - MUSICHALL_ARTIST = 'MusichallArtist', +export enum COMPONENT_NAME { + ARTIST_ALLSONGS = 'ArtistAllSongs', + ARTIST_ALBUM = 'ArtistAlbum', + ARTIST_MV = 'ArtistMv', - SEARCH_VIDEO = 'SearchVideo', - SEARCH_MV = 'SearchMV', - SEARCH_LYRIC = 'SearchLyric', - SEARCH_SONGLIST = 'SearchSonglist', - SEARCH_ALBUM = 'SearchAlbum', + MUSICHALL_SONGLIST = 'MusichallSonglist', + MUSICHALL_ARTIST = 'MusichallArtist', - USER_SONGLIST = 'UserSonglist', - USER_COLLECTION = 'UserCollection', + SEARCH_VIDEO = 'SearchVideo', + SEARCH_MV = 'SearchMV', + SEARCH_LYRIC = 'SearchLyric', + SEARCH_SONGLIST = 'SearchSonglist', + SEARCH_ALBUM = 'SearchAlbum', - SONGLIST_SUBSCRIBER = 'SonglistSubscriber', + USER_SONGLIST = 'UserSonglist', + USER_COLLECTION = 'UserCollection', + SONGLIST_SUBSCRIBER = 'SonglistSubscriber', } /** * 个人偏好(设置)的属性 */ export enum PreferenceNames { - theme = 'theme', - order = 'order', - playerQueueShow = 'playerQueueShow', - playing = 'playing', - currentTime = 'currentTime', - rate = 'rate', - volume = 'volume', - mute = 'mute', + theme = 'theme', + order = 'order', + playerQueueShow = 'playerQueueShow', + playing = 'playing', + currentTime = 'currentTime', + rate = 'rate', + volume = 'volume', + mute = 'mute', } /** * 各个组件列表的分页器基础数量配置 */ export const PAGE_SIZE = { - - /** - * 默认 - */ - DEFAULT: 30, - - /** - * 歌手详情-全部歌曲 - */ - [COMPONENT_NAME.ARTIST_ALLSONGS]: 30, - - /** - * 歌手详情-专辑 - */ - [COMPONENT_NAME.ARTIST_ALBUM]: 28, - - /** - * 歌手详情-MV - */ - [COMPONENT_NAME.ARTIST_MV]: 25, - - /** - * 音乐馆-歌单 - */ - [COMPONENT_NAME.MUSICHALL_SONGLIST]: 56, - - /** - * 音乐馆-歌手 - */ - [COMPONENT_NAME.MUSICHALL_ARTIST]: 50, - - /** - * 搜索-视频 - */ - [COMPONENT_NAME.SEARCH_VIDEO]: 50, - - /** - * 搜索-MV - */ - [COMPONENT_NAME.SEARCH_MV]: 40, - - /** - * 搜索-歌词 - */ - [COMPONENT_NAME.SEARCH_LYRIC]: 30, - - /** - * 搜索-歌单 - */ - [COMPONENT_NAME.SEARCH_SONGLIST]: 40, - - /** - * 搜索-专辑 - */ - [COMPONENT_NAME.SEARCH_ALBUM]: 35, - - /** - * 歌单-订阅者 - */ - [COMPONENT_NAME.SONGLIST_SUBSCRIBER]: 40, - - /** - * 用户-歌单 - */ - [COMPONENT_NAME.USER_SONGLIST]: 30, - - /** - * 用户-收藏 - */ - [COMPONENT_NAME.USER_COLLECTION]: 30, - -} \ No newline at end of file + /** + * 默认 + */ + DEFAULT: 30, + + /** + * 歌手详情-全部歌曲 + */ + [COMPONENT_NAME.ARTIST_ALLSONGS]: 30, + + /** + * 歌手详情-专辑 + */ + [COMPONENT_NAME.ARTIST_ALBUM]: 28, + + /** + * 歌手详情-MV + */ + [COMPONENT_NAME.ARTIST_MV]: 25, + + /** + * 音乐馆-歌单 + */ + [COMPONENT_NAME.MUSICHALL_SONGLIST]: 56, + + /** + * 音乐馆-歌手 + */ + [COMPONENT_NAME.MUSICHALL_ARTIST]: 50, + + /** + * 搜索-视频 + */ + [COMPONENT_NAME.SEARCH_VIDEO]: 50, + + /** + * 搜索-MV + */ + [COMPONENT_NAME.SEARCH_MV]: 40, + + /** + * 搜索-歌词 + */ + [COMPONENT_NAME.SEARCH_LYRIC]: 30, + + /** + * 搜索-歌单 + */ + [COMPONENT_NAME.SEARCH_SONGLIST]: 40, + + /** + * 搜索-专辑 + */ + [COMPONENT_NAME.SEARCH_ALBUM]: 35, + + /** + * 歌单-订阅者 + */ + [COMPONENT_NAME.SONGLIST_SUBSCRIBER]: 40, + + /** + * 用户-歌单 + */ + [COMPONENT_NAME.USER_SONGLIST]: 30, + + /** + * 用户-收藏 + */ + [COMPONENT_NAME.USER_COLLECTION]: 30, +}; diff --git a/src/utils/time.ts b/src/utils/time.ts index d03a480..85998de 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,139 +1,137 @@ +/** @format */ + export const getDate = (time?: number) => { - const date = time == void 0 ? new Date() : new Date(time); - return { - year: date.getFullYear(), - month: date.getMonth() + 1, - day: date.getDate(), - hour: date.getHours(), - minute: date.getMinutes(), - second: date.getSeconds(), - }; + const date = time == void 0 ? new Date() : new Date(time); + return { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate(), + hour: date.getHours(), + minute: date.getMinutes(), + second: date.getSeconds(), + }; }; //是否是标准格式时间(如2020-12-20) export const isStandardD = ( - d: string, - connector: "-" | "." | "/" | " " | "\0" = "-" + d: string, + connector: '-' | '.' | '/' | ' ' | '\0' = '-', ): boolean => - new RegExp( - String.raw`^\d{4}${connector}\d{1,2}${connector}\d{1,2}$`, - "g" - ).test(d); + new RegExp(String.raw`^\d{4}${connector}\d{1,2}${connector}\d{1,2}$`, 'g').test(d); export const toStandardTime = (content: string | number): string => { - const time = new Date(+content); - const year = time.getFullYear(); - const month = time.getMonth() + 1; - const day = time.getDate(); - return [year, month, day].join("-"); + const time = new Date(+content); + const year = time.getFullYear(); + const month = time.getMonth() + 1; + const day = time.getDate(); + return [year, month, day].join('-'); }; export const filterDate = (content: string | number): string => { - content = String(content); - return isStandardD(content) ? content : toStandardTime(content); + content = String(content); + return isStandardD(content) ? content : toStandardTime(content); }; // 音视频时间解析 export const spliceTime2Second = ( - ms: number | string, - s: number | string, - m: number | string, - h: number | string = 0 + ms: number | string, + s: number | string, + m: number | string, + h: number | string = 0, ) => +ms / 1000 + +s + +m * 60 + +h * 60 * 60; export const timeStr2Second = (timeStr: string): number => { - const timeobj = parseTimeStr(timeStr); - const values = Object.values(timeobj).reverse(); - const h2s = values.length === 3 ? values[2] * 60 * 60 : 0; - return values[0] + values[1] * 60 + h2s; + const timeobj = parseTimeStr(timeStr); + const values = Object.values(timeobj).reverse(); + const h2s = values.length === 3 ? values[2] * 60 * 60 : 0; + return values[0] + values[1] * 60 + h2s; }; export const second2Time = (second: number | string) => { - second = +second; - const day = ~~(second / (24 * 60 * 60)); - const hour = ~~((second / (60 * 60)) % 24); - const minute = ~~((second / 60) % 60); - const restSecond = ~~(second % 60); - return { - day, - hour, - minute, - restSecond, - }; + second = +second; + const day = ~~(second / (24 * 60 * 60)); + const hour = ~~((second / (60 * 60)) % 24); + const minute = ~~((second / 60) % 60); + const restSecond = ~~(second % 60); + return { + day, + hour, + minute, + restSecond, + }; }; export const msSecondToTimeStr = ( - msSecond: number | string, - separator = ":", - boundKey: "day" | "hour" | "minute" = "minute" + msSecond: number | string, + separator = ':', + boundKey: 'day' | 'hour' | 'minute' = 'minute', ) => second2TimeStr(+msSecond / 1000, separator, boundKey); export const second2TimeStr = ( - second: number | string, - separator = ":", - boundKey: "day" | "hour" | "minute" = "minute" + second: number | string, + separator = ':', + boundKey: 'day' | 'hour' | 'minute' = 'minute', ) => { - if (!["day", "hour", "minute"].includes(boundKey)) { - throw new TypeError( - `the 'boundKey' must be one of the three options: 'day'、'hour'、'minute'` - ); - } - const timeMap = second2Time(second); - const timeArr = Object.values(timeMap); - const realTimeStr = timeArr - .slice(Object.keys(timeMap).indexOf(boundKey)) - .map((val) => (val < 10 ? `0${val}` : String(val))) - .join(separator); - return realTimeStr; + if (!['day', 'hour', 'minute'].includes(boundKey)) { + throw new TypeError( + `the 'boundKey' must be one of the three options: 'day'、'hour'、'minute'`, + ); + } + const timeMap = second2Time(second); + const timeArr = Object.values(timeMap); + const realTimeStr = timeArr + .slice(Object.keys(timeMap).indexOf(boundKey)) + .map((val) => (val < 10 ? `0${val}` : String(val))) + .join(separator); + return realTimeStr; }; export const parseTimeStr = ( - timeStr: string -): Record<"hour" | "minute" | "second", number> => { - const splits = timeStr.split(":").map((_) => +_); - const { length } = splits; - const obj = {}; - if (length <= 2) { - splits.unshift(0); - } - return { - hour: splits[0], - minute: splits[1], - second: splits[2], - }; + timeStr: string, +): Record<'hour' | 'minute' | 'second', number> => { + const splits = timeStr.split(':').map((_) => +_); + const { length } = splits; + const obj = {}; + if (length <= 2) { + splits.unshift(0); + } + return { + hour: splits[0], + minute: splits[1], + second: splits[2], + }; }; export const localeDateKeyMaps: PlainObject = { - year: "年", - month: "月", - day: "日", - hour: "时", - minute: "分", - second: "秒", - milliSecond: "毫秒", + year: '年', + month: '月', + day: '日', + hour: '时', + minute: '分', + second: '秒', + milliSecond: '毫秒', }; -export const padSingalDigit = (val: string | number) => - `${+val < 10 ? "0" : ""}${val}`; +export const padSingalDigit = (val: string | number) => `${+val < 10 ? '0' : ''}${val}`; export const getLocaleDate = ( - time?: number, - factor: { - showZero?: boolean; - divide?: string; - delimiter?: string; - } = {} + time?: number, + factor: { + showZero?: boolean; + divide?: string; + delimiter?: string; + } = {}, ) => { - const { showZero, divide, delimiter } = factor; - let dateEntries = Object.entries(getDate(time)); - !showZero && (dateEntries = dateEntries.filter((arr) => arr[1])); - if (divide) { - const tarIdx = dateEntries.findIndex((_) => _[0] === divide); - ~tarIdx && (dateEntries = dateEntries.slice(0, tarIdx + 1)); - } - return delimiter != void 0 - ? dateEntries.map((arr) => padSingalDigit(arr[1])).join(delimiter) - : dateEntries - .map(([key, val]) => padSingalDigit(val) + localeDateKeyMaps[key]) - .join(""); + const { showZero, divide, delimiter } = factor; + let dateEntries = Object.entries(getDate(time)); + !showZero && (dateEntries = dateEntries.filter((arr) => arr[1])); + if (divide) { + const tarIdx = dateEntries.findIndex((_) => _[0] === divide); + ~tarIdx && (dateEntries = dateEntries.slice(0, tarIdx + 1)); + } + return delimiter != void 0 + ? dateEntries.map((arr) => padSingalDigit(arr[1])).join(delimiter) + : dateEntries + .map(([key, val]) => padSingalDigit(val) + localeDateKeyMaps[key]) + .join(''); }; diff --git a/src/utils/timeSlice.ts b/src/utils/timeSlice.ts index 9065525..45b8d62 100644 --- a/src/utils/timeSlice.ts +++ b/src/utils/timeSlice.ts @@ -1,59 +1,56 @@ -export type StepCbFactor = Record< - "priority" | "upperLimit" | "interval", - number ->; +/** @format */ + +export type StepCbFactor = Record<'priority' | 'upperLimit' | 'interval', number>; export class TimeSlice { - private priority = 0; - - private stepReq = 0; - - public upperLimit = 1; - - public interval = 1; - - constructor(factor: Partial>) { - const { interval = 1, upperLimit = 1 } = factor; - this.upperLimit = upperLimit; //加interval是为了保证多余的少于interval数量的内容正常显示 - this.interval = interval; - } - - public reset() { - this.priority = 0; - this.cancel(); - } - - public cancel() { - cancelAnimationFrame(this.stepReq); - } - - public step(cb: (this: TimeSlice, factor: StepCbFactor) => void) { - let { upperLimit, priority, interval } = this; - const realUpperLimit = upperLimit + interval; - let realPriority = priority; - return new Promise((resolve, reject) => { - const step = () => { - this.stepReq = requestAnimationFrame(() => { - realPriority += interval; - realPriority > upperLimit - ? (priority = upperLimit) - : (priority = realPriority); - - this.priority = priority; - const cbFactor = { priority, upperLimit, interval }; - if (realPriority < realUpperLimit) { - try { - cb.call(this, cbFactor); - } catch (err) { - reject(err); - } - step(); - } else { - resolve(cbFactor); - } - }); - }; - step(); - }); - } + private priority = 0; + + private stepReq = 0; + + public upperLimit = 1; + + public interval = 1; + + constructor(factor: Partial>) { + const { interval = 1, upperLimit = 1 } = factor; + this.upperLimit = upperLimit; //加interval是为了保证多余的少于interval数量的内容正常显示 + this.interval = interval; + } + + public reset() { + this.priority = 0; + this.cancel(); + } + + public cancel() { + cancelAnimationFrame(this.stepReq); + } + + public step(cb: (this: TimeSlice, factor: StepCbFactor) => void) { + let { upperLimit, priority, interval } = this; + const realUpperLimit = upperLimit + interval; + let realPriority = priority; + return new Promise((resolve, reject) => { + const step = () => { + this.stepReq = requestAnimationFrame(() => { + realPriority += interval; + realPriority > upperLimit ? (priority = upperLimit) : (priority = realPriority); + + this.priority = priority; + const cbFactor = { priority, upperLimit, interval }; + if (realPriority < realUpperLimit) { + try { + cb.call(this, cbFactor); + } catch (err) { + reject(err); + } + step(); + } else { + resolve(cbFactor); + } + }); + }; + step(); + }); + } } diff --git a/src/views/home/index.scss b/src/views/home/index.scss index 355ce1e..c6caf21 100644 --- a/src/views/home/index.scss +++ b/src/views/home/index.scss @@ -1,37 +1,39 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .yplayer-homepage { - position: relative; - height: 100%; - overflow: hidden; - - .home-main { - height: 100vh; - .main-header { - @include flexHbVc(); - height: $homeTopHeaderHeight; - padding-right: 35px; - padding-left: 30px; - backdrop-filter: drop-shadow(0px 0px 20px rgb(246, 246, 246)) - saturate(180%) blur(28px); - } + position: relative; + height: 100%; + overflow: hidden; + + .home-main { + height: 100vh; + .main-header { + @include flexHbVc(); + height: $homeTopHeaderHeight; + padding-right: 35px; + padding-left: 30px; + backdrop-filter: drop-shadow(0px 0px 20px rgb(246, 246, 246)) saturate(180%) + blur(28px); + } - .main-content { - position: relative; - height: calc(100% - #{$homeBottomControllerHeight + $homeTopHeaderHeight}); - padding-left: 30px; - overflow: hidden; + .main-content { + position: relative; + height: calc(100% - #{$homeBottomControllerHeight + $homeTopHeaderHeight}); + padding-left: 30px; + overflow: hidden; - .player-container { - height: calc(100% - 30px); - padding-right: 35px; - margin-bottom: 30px; - overflow-x: hidden; - } - } + .player-container { + height: calc(100% - 30px); + padding-right: 35px; + margin-bottom: 30px; + overflow-x: hidden; + } + } - .main-footer { - height: $homeBottomControllerHeight; - } - } + .main-footer { + height: $homeBottomControllerHeight; + } + } } diff --git a/src/views/home/index.tsx b/src/views/home/index.tsx index 922fd89..0fa2130 100644 --- a/src/views/home/index.tsx +++ b/src/views/home/index.tsx @@ -1,60 +1,59 @@ -import { defineComponent, onMounted, PropType, ref } from "vue"; -import HomeLeft from "@/components/home-left"; -import HomeHeader from "@components/home-header"; -import HomeController from "@/components/player-controller"; -import "./index.scss"; +/** @format */ -import PlayerQueue from "@/components/player-queue"; -import { NBackTop, NGrid, NGridItem } from "naive-ui"; -import { RouterView } from "vue-router"; +import { defineComponent, onMounted, PropType, ref } from 'vue'; +import HomeLeft from '@/components/home-left'; +import HomeHeader from '@components/home-header'; +import HomeController from '@/components/player-controller'; +import './index.scss'; -export default defineComponent({ - name: "Home", - setup(props, context) { - return () => { - return ( - <> - - - - - - - -
        -
        - -
        - -
        -
        - -
        - -
        +import PlayerQueue from '@/components/player-queue'; +import { NBackTop, NGrid, NGridItem } from 'naive-ui'; +import { RouterView } from 'vue-router'; -
        - -
        -
        -
        - -
        - - - - - ); - }; - }, +export default defineComponent({ + name: 'Home', + setup(props, context) { + return () => { + return ( + <> + + + + + + +
        +
        + +
        + +
        +
        + +
        + +
        + +
        + +
        +
        +
        +
        + + + + ); + }; + }, }); diff --git a/src/views/lyric-page/index.scss b/src/views/lyric-page/index.scss index f8f9f20..9713d5b 100644 --- a/src/views/lyric-page/index.scss +++ b/src/views/lyric-page/index.scss @@ -1,119 +1,120 @@ -@import "@scss/variable"; - -.lyric-page { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - max-width: 2600px; - min-width: 980px; - min-height: 600px; - overflow: hidden; - z-index: 150; - background-color: rgb(30, 40, 38); - transform: translateY(100%); - transition: transform .3s; +/** @format */ - &[show="true"] { - transform: translateY(0); - } +@import '@scss/variable'; - .player-bgcover-mask { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - z-index: 151; - background-size: cover; - background-position: center center; - background-repeat: no-repeat; - opacity: 0.65; - filter: blur(40px); - transition: background 0.25s, filter 0.2s; - } +.lyric-page { + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + max-width: 2600px; + min-width: 980px; + min-height: 600px; + overflow: hidden; + z-index: 150; + background-color: rgb(30, 40, 38); + transform: translateY(100%); + transition: transform 0.3s; - .player-back { - position: absolute; - left: 20px; - top: 20px; - z-index: 155; - color: #fff; - font-size: 28px; - cursor: pointer; - transition: color 0.12s; - &:hover { - color: var(--theme); - } - } + &[show='true'] { + transform: translateY(0); + } - .player-content { - @include flexHbC(); - position: relative; - z-index: 153; - width: 100%; - height: 100%; - padding: 80px 60px 0 60px; - backdrop-filter: blur(18px); - } + .player-bgcover-mask { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 151; + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + opacity: 0.65; + filter: blur(40px); + transition: background 0.25s, filter 0.2s; + } + + .player-back { + position: absolute; + left: 20px; + top: 20px; + z-index: 155; + color: #fff; + font-size: 28px; + cursor: pointer; + transition: color 0.12s; + &:hover { + color: var(--theme); + } + } + + .player-content { + @include flexHbC(); + position: relative; + z-index: 153; + width: 100%; + height: 100%; + padding: 80px 60px 0 60px; + backdrop-filter: blur(18px); + } } .player-content { - .player-body { - @include flexHc(); - flex: 1; - .player-cover { - flex: 3; - align-self: center; - @include flexHeVc(); - } - - .player-info { - flex: 5; - } + .player-body { + @include flexHc(); + flex: 1; + .player-cover { + flex: 3; + align-self: center; + @include flexHeVc(); + } - .player-song { - @include flexV(); - height: 100%; - padding-bottom: 40px; - color: $colorbase; - text-align: center; + .player-info { + flex: 5; + } - .player-title { - font-size: 22px; - font-weight: 500; - } + .player-song { + @include flexV(); + height: 100%; + padding-bottom: 40px; + color: $colorbase; + text-align: center; - .player-author { - @include flexHcVc(); - padding: 16px 0 40px 0; - font-size: 15px; + .player-title { + font-size: 22px; + font-weight: 500; + } - > span { - opacity: 0.6; - } + .player-author { + @include flexHcVc(); + padding: 16px 0 40px 0; + font-size: 15px; - &-text { - em { - opacity: 0.6; - } - span { - opacity: 0.6; - cursor: pointer; - transition: opacity 0.12s; - &:hover { - color: $colorbase; - opacity: 0.9; - } - } - } - } + > span { + opacity: 0.6; + } - .player-lyric-wrap { - flex: 1; - } + &-text { + em { + opacity: 0.6; + } + span { + opacity: 0.6; + cursor: pointer; + transition: opacity 0.12s; + &:hover { + color: $colorbase; + opacity: 0.9; + } + } + } + } - } - } + .player-lyric-wrap { + flex: 1; + } + } + } } diff --git a/src/views/lyric-page/index.tsx b/src/views/lyric-page/index.tsx index 22aa329..2b0a461 100644 --- a/src/views/lyric-page/index.tsx +++ b/src/views/lyric-page/index.tsx @@ -1,119 +1,119 @@ -import { defineComponent, watch, ref, computed } from "@vue/runtime-core"; -import { routerViewLocationKey, useRoute, useRouter } from "vue-router"; -import "./index.scss"; -import { onKeyStroke } from "@vueuse/core"; -import usePlayerStore, { currentSongRefGlobal } from "@/stores/player"; -import { padPicCrop } from "@/utils"; -import PlayerLyric from "@/components/lyric"; -import { MusicSinger } from "@/widgets/music-tiny-comp"; -import HomeController from "@/components/player-controller"; -import { Transition , withDirectives, vShow } from "vue"; +/** @format */ + +import { defineComponent, watch, ref, computed } from '@vue/runtime-core'; +import { routerViewLocationKey, useRoute, useRouter } from 'vue-router'; +import './index.scss'; +import { onKeyStroke } from '@vueuse/core'; +import usePlayerStore, { currentSongRefGlobal } from '@/stores/player'; +import { padPicCrop } from '@/utils'; +import PlayerLyric from '@/components/lyric'; +import { MusicSinger } from '@/widgets/music-tiny-comp'; +import HomeController from '@/components/player-controller'; +import { Transition, withDirectives, vShow } from 'vue'; export default defineComponent({ - name: "LyricPage", - setup(props, context) { - const route = useRoute(); - const router = useRouter(); - const playerDetailRef = ref(); - const isShow = ref(false); - const playerStore = usePlayerStore(); + name: 'LyricPage', + setup(props, context) { + const route = useRoute(); + const router = useRouter(); + const playerDetailRef = ref(); + const isShow = ref(false); + const playerStore = usePlayerStore(); - watch( - () => route.query.playerStatus, - (playerStatus) => { - isShow.value = !!+playerStatus!; - }, - { - immediate: true, - } - ); + watch( + () => route.query.playerStatus, + (playerStatus) => { + isShow.value = !!+playerStatus!; + }, + { + immediate: true, + }, + ); - const playbillRef = computed(() => { - return { - src: currentSongRefGlobal.value.album.picUrl, - size: [600, 600], - }; - }); + const playbillRef = computed(() => { + return { + src: currentSongRefGlobal.value.album.picUrl, + size: [600, 600], + }; + }); - const exitPlayerDetailPage = () => { - const newQuery = { ...route.query }; - if (!!newQuery.playerStatus) { - delete newQuery.playerStatus; - router.push({ - path: route.path, - query: newQuery, - }); - } - }; + const exitPlayerDetailPage = () => { + const newQuery = { ...route.query }; + if (!!newQuery.playerStatus) { + delete newQuery.playerStatus; + router.push({ + path: route.path, + query: newQuery, + }); + } + }; - onKeyStroke( - "Escape", - () => { - exitPlayerDetailPage(); - }, - { - eventName: "keyup", - target: playerDetailRef.value, - passive: true, - } - ); + onKeyStroke( + 'Escape', + () => { + exitPlayerDetailPage(); + }, + { + eventName: 'keyup', + target: playerDetailRef.value, + passive: true, + }, + ); - return () => { - const { musicName, singers } = currentSongRefGlobal.value; - const { size, src } = playbillRef.value; - const cropedSrc = padPicCrop(src, { x: 700, y: 700 }); - return ( -
        -
        + return () => { + const { musicName, singers } = currentSongRefGlobal.value; + const { size, src } = playbillRef.value; + const cropedSrc = padPicCrop(src, { x: 700, y: 700 }); + return ( +
        +
        -
        - -
        +
        + +
        -
        -
        -
        -
        - {src} -
        -
        +
        +
        +
        +
        + {src} +
        +
        -
        -
        -

        {musicName}

        +
        +
        +

        + {musicName} +

        -
        - 歌手 :  - -
        -
        - -
        -
        -
        -
        - -
        -
        - ); - }; - }, +
        + 歌手 :  + +
        +
        + +
        +
        +
        +
        + +
        +
        + ); + }; + }, }); diff --git a/src/webComponents/index.ts b/src/webComponents/index.ts index 24e4a73..d82a389 100644 --- a/src/webComponents/index.ts +++ b/src/webComponents/index.ts @@ -1 +1,3 @@ -export * from './textarea'; \ No newline at end of file +/** @format */ + +export * from './textarea'; diff --git a/src/webComponents/textarea/index.tsx b/src/webComponents/textarea/index.tsx index 8b4622e..1af6024 100644 --- a/src/webComponents/textarea/index.tsx +++ b/src/webComponents/textarea/index.tsx @@ -1,5 +1,15 @@ -import { is } from "@/utils"; -import { nextTick, watch, computed, defineCustomElement, PropType, ref, watchEffect } from "vue"; +/** @format */ + +import { is } from '@/utils'; +import { + nextTick, + watch, + computed, + defineCustomElement, + PropType, + ref, + watchEffect, +} from 'vue'; const styleString = ` @@ -26,133 +36,135 @@ const styleString = ` const styles = [styleString]; -/** +/** * 基于contenteditable特性实现的公共textarea组件 */ -const textareaCustomElm = defineCustomElement({ - styles, - props: { - placeholder: { - type: String as PropType, - required: false, - default: '', - }, - value: { - type: String as PropType, - required: false, - }, - focus: { - type: Boolean as PropType, - required: false, - }, - onInput: { - type: Function as PropType<(value: string) => void>, - required: false, - }, - onChange: { - type: Function as PropType<(value: string) => void>, - requried: false, - } - }, - emits: ['input', 'change'], - setup(props, context) { - - const constants = { - fontSize: 12, - lineHeight: 1.5, - _rowHeight: -1, - get rowHeight() { - if (~this._rowHeight) { - return this._rowHeight = this.fontSize * this.lineHeight - } - return this._rowHeight - } - } - - const textareaRef = ref(); - const placeHolderShow = ref(false); - const insideValue = ref(); - const textareaStyle = computed(() => { - return { - 'font-size': `${constants.fontSize}px`, - 'line-height': constants.lineHeight, - } - }); - - const inputHandler = ({ target }: Event) => { - props.onInput && props.onInput((target as HTMLElement).innerText); - } - - const focusHandler = () => { - placeHolderShow.value = false; - textareaRef.value!.focus(); - } - - const blurHandler = (ev: Event) => { - const target = ev.target as HTMLElement; - const { innerText } = target; - if (innerText === '') { - // 当输入框失焦时,最好再手动blur一次,因为对于使用alt+tab切换窗口后,会触发target的blur事件,但是focus的输入光标(caret)却不会消失 - target.blur(); - placeHolderShow.value = true; - } - if (insideValue.value !== innerText) { - insideValue.value = innerText; - props.onChange && props.onChange(innerText); - } - } - - watchEffect(() => { - insideValue.value = props.value; - }); - - watch(() => props.value, async (value) => { - await nextTick(); - const textareaElm = textareaRef.value!; - if (is.undefined(value)) { - placeHolderShow.value = true; - } else { - textareaElm.innerText = value; - } - }, { - immediate: true - }); - - watch(() => props.focus, async (value) => { - await nextTick(); - const textareaElm = textareaRef.value!; - if (value) { - textareaElm.parentElement!.dispatchEvent(new PointerEvent('pointerdown')); - } else { - textareaElm.blur(); - } - }, { - immediate: true - }) - - return () => { - return ( -
        - -
        -
        - ) - } - }, +const textareaCustomElm = defineCustomElement({ + styles, + props: { + placeholder: { + type: String as PropType, + required: false, + default: '', + }, + value: { + type: String as PropType, + required: false, + }, + focus: { + type: Boolean as PropType, + required: false, + }, + onInput: { + type: Function as PropType<(value: string) => void>, + required: false, + }, + onChange: { + type: Function as PropType<(value: string) => void>, + requried: false, + }, + }, + emits: ['input', 'change'], + setup(props, context) { + const constants = { + fontSize: 12, + lineHeight: 1.5, + _rowHeight: -1, + get rowHeight() { + if (~this._rowHeight) { + return (this._rowHeight = this.fontSize * this.lineHeight); + } + return this._rowHeight; + }, + }; + + const textareaRef = ref(); + const placeHolderShow = ref(false); + const insideValue = ref(); + const textareaStyle = computed(() => { + return { + 'font-size': `${constants.fontSize}px`, + 'line-height': constants.lineHeight, + }; + }); + + const inputHandler = ({ target }: Event) => { + props.onInput && props.onInput((target as HTMLElement).innerText); + }; + + const focusHandler = () => { + placeHolderShow.value = false; + textareaRef.value!.focus(); + }; + + const blurHandler = (ev: Event) => { + const target = ev.target as HTMLElement; + const { innerText } = target; + if (innerText === '') { + // 当输入框失焦时,最好再手动blur一次,因为对于使用alt+tab切换窗口后,会触发target的blur事件,但是focus的输入光标(caret)却不会消失 + target.blur(); + placeHolderShow.value = true; + } + if (insideValue.value !== innerText) { + insideValue.value = innerText; + props.onChange && props.onChange(innerText); + } + }; + + watchEffect(() => { + insideValue.value = props.value; + }); + + watch( + () => props.value, + async (value) => { + await nextTick(); + const textareaElm = textareaRef.value!; + if (is.undefined(value)) { + placeHolderShow.value = true; + } else { + textareaElm.innerText = value; + } + }, + { + immediate: true, + }, + ); + + watch( + () => props.focus, + async (value) => { + await nextTick(); + const textareaElm = textareaRef.value!; + if (value) { + textareaElm.parentElement!.dispatchEvent(new PointerEvent('pointerdown')); + } else { + textareaElm.blur(); + } + }, + { + immediate: true, + }, + ); + + return () => { + return ( +
        + +
        +
        + ); + }; + }, }); window.customElements.define(`custom-textarea`, textareaCustomElm); - - \ No newline at end of file diff --git a/src/widgets/album-list/index.scss b/src/widgets/album-list/index.scss index 1148d4e..056154c 100644 --- a/src/widgets/album-list/index.scss +++ b/src/widgets/album-list/index.scss @@ -1,50 +1,50 @@ -.album-container { - padding: 0 2rem; - .album-item { - transition: 0.1s ease-in; - cursor: pointer; +/** @format */ - // &:hover { - // filter: drop-shadow(0 0 20px rgba(123, 131, 120, 0.2)); - // transform: translateY(-10px); - // } +.album-container { + padding: 0 2rem; + .album-item { + transition: 0.1s ease-in; + cursor: pointer; - .album-pic { - position: relative; - .album-cover { - position: absolute; - width: 100%; - height: 100%; - background-position: center center; - background-size: cover; - transform: translateX(20px); - } - &-body { - position: relative; - overflow: hidden; - border-radius: 8px; - } + // &:hover { + // filter: drop-shadow(0 0 20px rgba(123, 131, 120, 0.2)); + // transform: translateY(-10px); + // } - &:hover { - img { - transform: scale(1.06); - } - } + .album-pic { + position: relative; + .album-cover { + position: absolute; + width: 100%; + height: 100%; + background-position: center center; + background-size: cover; + transform: translateX(20px); + } + &-body { + position: relative; + overflow: hidden; + border-radius: 8px; + } - img { - transition: 0.26s ease-out; - } - } + &:hover { + img { + transform: scale(1.06); + } + } - + img { + transition: 0.26s ease-out; + } + } - h5 { - font-size: 14px; - line-height: 2.2; - } + h5 { + font-size: 14px; + line-height: 2.2; + } - p { - font-size: 13px; - } - } + p { + font-size: 13px; + } + } } diff --git a/src/widgets/album-list/index.tsx b/src/widgets/album-list/index.tsx index 675b4bf..62485ca 100644 --- a/src/widgets/album-list/index.tsx +++ b/src/widgets/album-list/index.tsx @@ -1,212 +1,204 @@ -import { - computed, - defineComponent, - PropType, - reactive, - ref, - watchEffect, -} from "vue"; -import { - onBeforeRouteUpdate, - useRouter, -} from "vue-router"; -import { getLocaleDate, padPicCrop, is } from "@utils/index"; -import { NEmpty, NGrid, NGridItem } from "naive-ui"; +/** @format */ + +import { computed, defineComponent, PropType, reactive, ref, watchEffect } from 'vue'; +import { onBeforeRouteUpdate, useRouter } from 'vue-router'; +import { getLocaleDate, padPicCrop, is } from '@utils/index'; +import { NEmpty, NGrid, NGridItem } from 'naive-ui'; import AlbumCoverImg from '@assets/img/album-cover.png'; import AlbumCoverGoldImg from '@assets/img/album-cover-gold.png'; -import "./index.scss"; -import { PAGE_SIZE } from "@/utils/preference"; -import RoutePagination from "../route-pagination"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; -import YuanInfinityScroll from '@widgets/infinity-scroll/infinity-scroll'; +import './index.scss'; +import { PAGE_SIZE } from '@/utils/preference'; +import RoutePagination from '../route-pagination'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; +import YuanInfinityScroll from '@widgets/infinity-scroll/infinity-scroll'; const AlbumImg = defineComponent({ - name: 'AlbumImg', - props: { - imgUrl: { - type: String as PropType, - default: '', - }, - albumCoverStyle: { - type: String as PropType, - default: '', - } - }, - setup(props, {slots}) { - const coverShow = ref(false); - const showCover = () => { - coverShow.value = true; - } - return () => ( -
        - -
        - -
        -
        - ) - } -}) - + name: 'AlbumImg', + props: { + imgUrl: { + type: String as PropType, + default: '', + }, + albumCoverStyle: { + type: String as PropType, + default: '', + }, + }, + setup(props, { slots }) { + const coverShow = ref(false); + const showCover = () => { + coverShow.value = true; + }; + return () => ( +
        + +
        + +
        +
        + ); + }, +}); + export default defineComponent({ - name: "AlbumList", - props: { - albumList: { - type: Array as PropType, - required: true - }, - isNew: { - type: Boolean as PropType, - required: false, - default: false, - }, - gaps: { - type: Object as PropType>>, - required: false, - default: () => ({ x: 50, y: 50 }) - }, - cols: { - type: Number as PropType, - required: false, - default: 6 - }, - defaultLimit: { - type: Number as PropType, - required: false, - default: PAGE_SIZE.DEFAULT, - }, - total: { - type: Number as PropType, - required: false, - default: 0, - }, - hasMore: { - type: Boolean as PropType, - required: false, - default: true - }, - showPagination: { - type: Boolean, - required: false, - default: true - }, - needInfinityScroll: { - type: Boolean, - required: false, - default: false, - } - }, - setup(props, context) { - const router = useRouter(); - const { defaultLimit, isNew } = props; - const coverImg = isNew ? AlbumCoverGoldImg : AlbumCoverImg; - const albumCoverStyle = `background-image:url(${coverImg})`; + name: 'AlbumList', + props: { + albumList: { + type: Array as PropType, + required: true, + }, + isNew: { + type: Boolean as PropType, + required: false, + default: false, + }, + gaps: { + type: Object as PropType>>, + required: false, + default: () => ({ x: 50, y: 50 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 6, + }, + defaultLimit: { + type: Number as PropType, + required: false, + default: PAGE_SIZE.DEFAULT, + }, + total: { + type: Number as PropType, + required: false, + default: 0, + }, + hasMore: { + type: Boolean as PropType, + required: false, + default: true, + }, + showPagination: { + type: Boolean, + required: false, + default: true, + }, + needInfinityScroll: { + type: Boolean, + required: false, + default: false, + }, + }, + setup(props, context) { + const router = useRouter(); + const { defaultLimit, isNew } = props; + const coverImg = isNew ? AlbumCoverGoldImg : AlbumCoverImg; + const albumCoverStyle = `background-image:url(${coverImg})`; + + const albumPagiInfo = reactive({ + total: 0, + limit: defaultLimit, + offset: defaultLimit, + sizeArr: Array(3) + .fill(0) + .map((v, i) => defaultLimit * (i + 1)), + }); + const updateTolListInfo = (query: PlainObject) => { + const { total } = props; + const { limit = defaultLimit, offset = 0 } = query; + albumPagiInfo.limit = limit; + albumPagiInfo.offset = offset; + albumPagiInfo.total = total; + }; + updateTolListInfo(router.currentRoute.value.query as PlainObject); - const albumPagiInfo = reactive({ - total: 0, - limit: defaultLimit, - offset: defaultLimit, - sizeArr: Array(3) - .fill(0) - .map((v, i) => defaultLimit * (i + 1)), - }); - const updateTolListInfo = (query: PlainObject) => { - const { total } = props; - const { limit = defaultLimit, offset = 0 } = query; - albumPagiInfo.limit = limit; - albumPagiInfo.offset = offset; - albumPagiInfo.total = total; - }; - updateTolListInfo(router.currentRoute.value.query as PlainObject); - - onFilteredBeforeRouteUpdate((to, from) => { - updateTolListInfo(to.query as PlainObject); - }); + onFilteredBeforeRouteUpdate((to, from) => { + updateTolListInfo(to.query as PlainObject); + }); - const toAlbumDetailPage = (id: number) => { - router.push({ - path: '/album', - query: { - id - } - }) - } + const toAlbumDetailPage = (id: number) => { + router.push({ + path: '/album', + query: { + id, + }, + }); + }; - const albumListRender = (currentCount: number = props.albumList.length) => { - const { albumList, gaps: { x, y }, cols } = props; - return ( - - { - albumList - .slice(0, currentCount) - .map(({ blurPicUrl, name, artist, id, transNames, publishTime }) => { - return ( - -
        toAlbumDetailPage(id)} - > - -
        - {`${name} - ${artist?.name}`} -
        -

        - { getLocaleDate(publishTime, { delimiter: "-" }) } -

        -
        -
        - ) - }) - } -
        - ) + const albumListRender = (currentCount: number = props.albumList.length) => { + const { + albumList, + gaps: { x, y }, + cols, + } = props; + return ( + + {albumList + .slice(0, currentCount) + .map(({ blurPicUrl, name, artist, id, transNames, publishTime }) => { + return ( + +
        toAlbumDetailPage(id)} + > + +
        + {`${name} - ${artist?.name}`} +
        +

        {getLocaleDate(publishTime, { delimiter: '-' })}

        +
        +
        + ); + })} +
        + ); + }; - } - - const renderAlbumList = () => { - if(props.needInfinityScroll) { - return ( - - { - { - default: albumListRender - } - } - - ) - } - return albumListRender(); - } + const renderAlbumList = () => { + if (props.needInfinityScroll) { + return ( + + {{ + default: albumListRender, + }} + + ); + } + return albumListRender(); + }; - return () => { - if(is.emptyArray(props.albumList)) { - return - } - return ( -
        - { - renderAlbumList() - } - { - props.showPagination && ( -
        - -
        - ) - } -
        - ); - }; - }, + return () => { + if (is.emptyArray(props.albumList)) { + return ( + + ); + } + return ( +
        + {renderAlbumList()} + {props.showPagination && ( +
        + +
        + )} +
        + ); + }; + }, }); diff --git a/src/widgets/artist-list/index.scss b/src/widgets/artist-list/index.scss index aa0bbd0..a82f7a9 100644 --- a/src/widgets/artist-list/index.scss +++ b/src/widgets/artist-list/index.scss @@ -1,24 +1,25 @@ -@import "@/scss/variable"; +/** @format */ -.singer-list { - padding: 30px 20px; +@import '@/scss/variable'; - .singer-item { - cursor: pointer; +.singer-list { + padding: 30px 20px; - img { - &:hover { - transform: scale(1.08); - filter: drop-shadow(0 0 20px rgba(200, 140, 90, 0.18)); - } - border-radius: 100%; - transition: transform 0.15s ease-out, filter 0.22s; - } + .singer-item { + cursor: pointer; - .singer-name { - text-align: center; - line-height: 2.8; - } - - } + img { + &:hover { + transform: scale(1.08); + filter: drop-shadow(0 0 20px rgba(200, 140, 90, 0.18)); + } + border-radius: 100%; + transition: transform 0.15s ease-out, filter 0.22s; + } + + .singer-name { + text-align: center; + line-height: 2.8; + } + } } diff --git a/src/widgets/artist-list/index.tsx b/src/widgets/artist-list/index.tsx index 761273f..51e9654 100644 --- a/src/widgets/artist-list/index.tsx +++ b/src/widgets/artist-list/index.tsx @@ -1,81 +1,76 @@ -import { watch, shallowReactive, toRefs, computed, defineComponent, PropType } from "vue"; -import { useRouter, useRoute } from "vue-router"; -import { SingerInfo } from "@/types/singer"; -import "./index.scss"; -import { padPicCrop } from "@/utils"; +/** @format */ -import { - NGrid, - NGridItem -} from 'naive-ui'; +import { watch, shallowReactive, toRefs, computed, defineComponent, PropType } from 'vue'; +import { useRouter, useRoute } from 'vue-router'; +import { SingerInfo } from '@/types/singer'; +import './index.scss'; +import { padPicCrop } from '@/utils'; + +import { NGrid, NGridItem } from 'naive-ui'; export default defineComponent({ - name: "ArtistList", - props: { - singerList: { - type: Array as PropType, - required: true, - }, - gaps: { - type: Object as PropType>, - required: false, - default: () => ({ x: 45, y: 45 }), - }, - cols: { - type: Number as PropType, - required: false, - default: 7, - } - }, - setup(props, context) { - const router = useRouter(); + name: 'ArtistList', + props: { + singerList: { + type: Array as PropType, + required: true, + }, + gaps: { + type: Object as PropType>, + required: false, + default: () => ({ x: 45, y: 45 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 7, + }, + }, + setup(props, context) { + const router = useRouter(); - const singers = computed(() => { - const list = props.singerList as SingerInfo[]; - ;[...list].forEach((item) => { - const { alias, name } = item; - const aliasStr = alias.join("、"); - item.fullName = name + (aliasStr && `(${aliasStr})`); - }); - return list; - }); + const singers = computed(() => { + const list = props.singerList as SingerInfo[]; + [...list].forEach((item) => { + const { alias, name } = item; + const aliasStr = alias.join('、'); + item.fullName = name + (aliasStr && `(${aliasStr})`); + }); + return list; + }); - const singerItemClick = (item: SingerInfo) => { - router.push({ path: "/artist", query: { id: item.id } }); - }; + const singerItemClick = (item: SingerInfo) => { + router.push({ path: '/artist', query: { id: item.id } }); + }; - return () => { - const { cols, gaps: { x, y } } = props; - return ( -
        - - { - singers.value.map((item) => ( - -
        singerItemClick(item)} - > -
        - {item.picUrl} -
        -
        - {item.fullName} -
        -
        -
        - )) - } -
        -
        - ); - }; - }, + return () => { + const { + cols, + gaps: { x, y }, + } = props; + return ( +
        + + {singers.value.map((item) => ( + +
        singerItemClick(item)}> +
        + {item.picUrl} +
        +
        + {item.fullName} +
        +
        +
        + ))} +
        +
        + ); + }; + }, }); diff --git a/src/widgets/aside-route-list/index.scss b/src/widgets/aside-route-list/index.scss index 859455d..dfdc679 100644 --- a/src/widgets/aside-route-list/index.scss +++ b/src/widgets/aside-route-list/index.scss @@ -1,36 +1,38 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .yuan-router-menu { - padding-bottom: 40px; + padding-bottom: 40px; - h5 { - padding: 0 14px 18px 14px; - color: #999; - font-size: 15px; - } + h5 { + padding: 0 14px 18px 14px; + color: #999; + font-size: 15px; + } - .yuan-router-list { - li:not(:last-of-type) { - margin-bottom: 14px; - } + .yuan-router-list { + li:not(:last-of-type) { + margin-bottom: 14px; + } - .yuan-list-item { - height: 32px; - padding: 0 12px; - color: #444; - line-height: 32px; - border-radius: 6px; - overflow: hidden; - transition: background-color 0.05s ease-in, color 0.05s ease-out; + .yuan-list-item { + height: 32px; + padding: 0 12px; + color: #444; + line-height: 32px; + border-radius: 6px; + overflow: hidden; + transition: background-color 0.05s ease-in, color 0.05s ease-out; - &:not(.active-online-link):hover { - background-color: rgba(120, 120, 120, 0.16); - } + &:not(.active-online-link):hover { + background-color: rgba(120, 120, 120, 0.16); + } - &.active-online-link { - background-color: var(--theme); - color: #fff; - } - } - } + &.active-online-link { + background-color: var(--theme); + color: #fff; + } + } + } } diff --git a/src/widgets/aside-route-list/index.tsx b/src/widgets/aside-route-list/index.tsx index 60b921c..0627782 100644 --- a/src/widgets/aside-route-list/index.tsx +++ b/src/widgets/aside-route-list/index.tsx @@ -1,69 +1,89 @@ -import { EMPTY_OBJ, getHrefWithoutOrigin, is, isLooseEqual } from "@/utils"; -import { customRef, defineComponent, markRaw, onMounted, readonly, ref, triggerRef, unref } from "vue"; -import { RouterLink, useLink, stringifyQuery, LocationQueryRaw, useRoute, routerKey, useRouter, viewDepthKey, routeLocationKey, matchedRouteKey } from "vue-router"; -import "./index.scss"; +/** @format */ + +import { EMPTY_OBJ, getHrefWithoutOrigin, is, isLooseEqual } from '@/utils'; +import { + customRef, + defineComponent, + markRaw, + onMounted, + readonly, + ref, + triggerRef, + unref, +} from 'vue'; +import { + RouterLink, + useLink, + stringifyQuery, + LocationQueryRaw, + useRoute, + routerKey, + useRouter, + viewDepthKey, + routeLocationKey, + matchedRouteKey, +} from 'vue-router'; +import './index.scss'; export default defineComponent({ - name: "asideRouterList", - props: { - title: { - type: String, - default: "", - required: false, - }, - list: { - type: Array, - default: () => [], - required: true, - }, - }, - setup(props, context) { - return () => { - const { title, list } = props; - const router = useRouter(); - const getRouteLinkClasses = (isActive: boolean) => - [isActive && "active-online-link", "yuan-list-item"] - .filter(Boolean) - .join(" "); + name: 'asideRouterList', + props: { + title: { + type: String, + default: '', + required: false, + }, + list: { + type: Array, + default: () => [], + required: true, + }, + }, + setup(props, context) { + return () => { + const { title, list } = props; + const router = useRouter(); + const getRouteLinkClasses = (isActive: boolean) => + [isActive && 'active-online-link', 'yuan-list-item'].filter(Boolean).join(' '); - const renderRouteLists = () => { - return list.map(({ to, text }: any) => { - return ( -
      • - ) { - const { navigate, href, isActive, isExactActive, route } = data; - return ( -
        - {text} -
        - ); - }, - }} - >
        -
      • - ) - }); - }; + const renderRouteLists = () => { + return list.map(({ to, text }: any) => { + return ( +
      • + ) { + const { navigate, href, isActive, isExactActive, route } = data; + return ( +
        + {text} +
        + ); + }, + }} + >
        +
      • + ); + }); + }; - if (is.emptyArray(list)) { - return; - } + if (is.emptyArray(list)) { + return; + } - return ( -
        -
        {title}
        -
          {renderRouteLists()}
        -
        - ); - }; - }, + return ( +
        +
        {title}
        +
          {renderRouteLists()}
        +
        + ); + }; + }, }); diff --git a/src/widgets/battery/index.scss b/src/widgets/battery/index.scss index 6a94cd5..0e289eb 100644 --- a/src/widgets/battery/index.scss +++ b/src/widgets/battery/index.scss @@ -1,15 +1,17 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .icon-battery { - padding: 0 8px; - height: 30px + padding: 0 8px; + height: 30px; } .yplayer-head-battery { - @include flexHcVc; - margin-right: 40px; + @include flexHcVc; + margin-right: 40px; } .head-battery-text { - color: #555; + color: #555; } diff --git a/src/widgets/battery/index.tsx b/src/widgets/battery/index.tsx index d3f320e..1747b43 100644 --- a/src/widgets/battery/index.tsx +++ b/src/widgets/battery/index.tsx @@ -1,63 +1,62 @@ +/** @format */ import Vue, { - defineComponent, - reactive, - ref, - nextTick, - watch, - watchEffect, - computed, -} from "vue"; -import { decimalToPercent } from "@utils/calc"; -import "./index.scss"; + defineComponent, + reactive, + ref, + nextTick, + watch, + watchEffect, + computed, +} from 'vue'; +import { decimalToPercent } from '@utils/calc'; +import './index.scss'; -import { - useBattery -} from '@vueuse/core'; +import { useBattery } from '@vueuse/core'; export default defineComponent({ - name: "Battery", - setup(props, { emit, slots }) { - const batteryPercent = computed(() => decimalToPercent(level.value)) + name: 'Battery', + setup(props, { emit, slots }) { + const batteryPercent = computed(() => decimalToPercent(level.value)); - const { isSupported, charging, dischargingTime, level, chargingTime } = useBattery(); - if (!isSupported) { - return - } - const curBatteryStyle = computed(() => ({ - fill: "var(--theme)", - "clip-path": `polygon(0 0, ${batteryPercent.value} 0, ${batteryPercent.value} 100%, 0 100%)`, - })); + const { isSupported, charging, dischargingTime, level, chargingTime } = useBattery(); + if (!isSupported) { + return; + } + const curBatteryStyle = computed(() => ({ + fill: 'var(--theme)', + 'clip-path': `polygon(0 0, ${batteryPercent.value} 0, ${batteryPercent.value} 100%, 0 100%)`, + })); - return () => { - return ( -
        - - - - { + return ( +
        + + + + - -
        {batteryPercent.value} -
        - ); - }; - }, + fill="#555" + > + + {batteryPercent.value} + + ); + }; + }, }); diff --git a/src/widgets/common-renderer/index.scss b/src/widgets/common-renderer/index.scss index bc204f1..b546a16 100644 --- a/src/widgets/common-renderer/index.scss +++ b/src/widgets/common-renderer/index.scss @@ -1,7 +1,9 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .current-playtime { - font-size: 15px; - color: currentColor; - padding-right: 20px; + font-size: 15px; + color: currentColor; + padding-right: 20px; } diff --git a/src/widgets/common-renderer/index.tsx b/src/widgets/common-renderer/index.tsx index f6718a0..75d1e9f 100644 --- a/src/widgets/common-renderer/index.tsx +++ b/src/widgets/common-renderer/index.tsx @@ -1,37 +1,38 @@ -import { currentTimeRefGlobal, durationRefGlobal } from "@/stores"; -import { second2TimeStr } from "@/utils"; -import { defineComponent, KeepAlive } from "vue"; -import { RouterView } from "vue-router"; +/** @format */ + +import { currentTimeRefGlobal, durationRefGlobal } from '@/stores'; +import { second2TimeStr } from '@/utils'; +import { defineComponent, KeepAlive } from 'vue'; +import { RouterView } from 'vue-router'; import './index.scss'; /** * 渲染KeepAliveRouterView - * @returns + * @returns */ export const renderKeepAliveRouterView = () => ( - - - - ); - }, - }} - > -); - + + + + ); + }, + }} + > +); + /** * 渲染歌曲播放时间 - * @returns + * @returns */ export const renderCurrentPlayTime = () => ( -
        - {second2TimeStr(currentTimeRefGlobal.value)} - / - {second2TimeStr(durationRefGlobal.value )} -
        -); - \ No newline at end of file +
        + {second2TimeStr(currentTimeRefGlobal.value)} + / + {second2TimeStr(durationRefGlobal.value)} +
        +); diff --git a/src/widgets/common-router-list/index.scss b/src/widgets/common-router-list/index.scss index 0e8702e..113e65e 100644 --- a/src/widgets/common-router-list/index.scss +++ b/src/widgets/common-router-list/index.scss @@ -1,43 +1,45 @@ -@import "@scss/variable"; +/** @format */ -.common-router-list { - @include flexVc(); - margin: 10px 0 30px 0; - - li { - padding-right: 60px; +@import '@scss/variable'; - .common-list-item { - position: relative; - display: inline-block; - line-height: 3; - transition: color 0.18s; - cursor: pointer; +.common-router-list { + @include flexVc(); + margin: 10px 0 30px 0; - &:hover { - color: var(--theme); - } + li { + padding-right: 60px; - &::after { - opacity: 0; - content: ""; - position: absolute; - bottom: 0; - left: 50%; - width: 2em; - height: 2px; - background-color: var(--theme); - border-radius: 1px; - transform: translateX(-50%); - transition: opacity 0.16s; - } + .common-list-item { + position: relative; + display: inline-block; + line-height: 3; + transition: color 0.18s; + cursor: pointer; - &.active-online-link { - color: var(--theme); - &::after { - opacity: 1; - } - } - } - } + &:hover { + color: var(--theme); + } + + &::after { + opacity: 0; + content: ''; + position: absolute; + bottom: 0; + left: 50%; + width: 2em; + height: 2px; + background-color: var(--theme); + border-radius: 1px; + transform: translateX(-50%); + transition: opacity 0.16s; + } + + &.active-online-link { + color: var(--theme); + &::after { + opacity: 1; + } + } + } + } } diff --git a/src/widgets/common-router-list/index.tsx b/src/widgets/common-router-list/index.tsx index 42ae824..f413148 100644 --- a/src/widgets/common-router-list/index.tsx +++ b/src/widgets/common-router-list/index.tsx @@ -1,56 +1,54 @@ -import { defineComponent, unref } from "@vue/runtime-core"; -import { PropType } from "vue"; -import { RouteLocationRaw, RouterLink, useLink } from "vue-router"; -import "./index.scss"; +/** @format */ + +import { defineComponent, unref } from '@vue/runtime-core'; +import { PropType } from 'vue'; +import { RouteLocationRaw, RouterLink, useLink } from 'vue-router'; +import './index.scss'; export type RouteListProp = { - text: string, - to: RouteLocationRaw, + text: string; + to: RouteLocationRaw; }[]; export default defineComponent({ - name: "CommonRouterList", - props: { - routelist: { - type: Array as PropType, - required: true, - }, - }, - setup(props, { emit, slots }) { - const getRouteLinkClasses = (isActive: boolean) => - [isActive && "active-online-link", "common-list-item"] - .filter(Boolean) - .join(" "); + name: 'CommonRouterList', + props: { + routelist: { + type: Array as PropType, + required: true, + }, + }, + setup(props, { emit, slots }) { + const getRouteLinkClasses = (isActive: boolean) => + [isActive && 'active-online-link', 'common-list-item'].filter(Boolean).join(' '); - return () => { - const { routelist } = props; - return ( -
          - { - routelist.map((item: any) => -
        • - ) { - const { navigate, isActive } = data; - return ( -
          - {item.text} -
          - ); - }, - }} - >
          -
        • - ) - } -
        - ); - }; - }, + return () => { + const { routelist } = props; + return ( +
          + {routelist.map((item: any) => ( +
        • + ) { + const { navigate, isActive } = data; + return ( +
          + {item.text} +
          + ); + }, + }} + >
          +
        • + ))} +
        + ); + }; + }, }); diff --git a/src/widgets/follow-button/index.scss b/src/widgets/follow-button/index.scss index c909a31..14fc737 100644 --- a/src/widgets/follow-button/index.scss +++ b/src/widgets/follow-button/index.scss @@ -1,11 +1,13 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .follow-button { - i { - font-weight: 500; - font-size: 1.2rem; - } - em { - text-indent: .5rem; - } -} \ No newline at end of file + i { + font-weight: 500; + font-size: 1.2rem; + } + em { + text-indent: 0.5rem; + } +} diff --git a/src/widgets/follow-button/index.tsx b/src/widgets/follow-button/index.tsx index 1213e1e..57791b5 100644 --- a/src/widgets/follow-button/index.tsx +++ b/src/widgets/follow-button/index.tsx @@ -1,77 +1,84 @@ -import { artistSub } from "@/api/singer"; -import { unOrFocusUser } from "@/api/user"; -import useUserStore from "@/stores/user"; -import { NOOP, UNICODE_CHAR } from "@/utils"; -import { messageBus } from "@/utils/event/register"; -import { NxButton } from "naive-ui"; -import Vue, { defineComponent, PropType } from "vue"; -import "./index.scss"; +/** @format */ + +import { artistSub } from '@/api/singer'; +import { unOrFocusUser } from '@/api/user'; +import useUserStore from '@/stores/user'; +import { NOOP, UNICODE_CHAR } from '@/utils'; +import { messageBus } from '@/utils/event/register'; +import { NxButton } from 'naive-ui'; +import Vue, { defineComponent, PropType } from 'vue'; +import './index.scss'; export enum FollowType { - user = 'user', - artist = 'artist', + user = 'user', + artist = 'artist', } export default defineComponent({ - name: "FollowButton", - props: { - followed: { - type: Boolean, - required: false, - default: false, - }, - onUpdateFollow: { - type: Function, - required: false, - default: NOOP, - }, - userId: { - type: [Number, String] as PropType, - required: true, - }, - followType: { - type: String as PropType, - required: false, - default: FollowType.user, - } - }, - setup(props, { emit, slots }) { - const userStore = useUserStore(); - const targetRequestMethod = - props.followType === FollowType.artist - ? artistSub - : unOrFocusUser; + name: 'FollowButton', + props: { + followed: { + type: Boolean, + required: false, + default: false, + }, + onUpdateFollow: { + type: Function, + required: false, + default: NOOP, + }, + userId: { + type: [Number, String] as PropType, + required: true, + }, + followType: { + type: String as PropType, + required: false, + default: FollowType.user, + }, + }, + setup(props, { emit, slots }) { + const userStore = useUserStore(); + const targetRequestMethod = + props.followType === FollowType.artist ? artistSub : unOrFocusUser; - const toggleHandler = async (isFollow: boolean) => { - if (!userStore.isLogin) { - messageBus.dispatch('warnMessage', '阿娜达~要先登录账号噢~~'); - return; - } - const { code } = await targetRequestMethod({ - id: props.userId, - sure: isFollow - }); - const isSuccess = code === 200; - let messageType = 'warnMessage'; - let msg = '关注失败!'; - if (isSuccess) { - props.onUpdateFollow!(isFollow); - messageType = 'successMessage'; - msg = isFollow ? `关注成功~~${UNICODE_CHAR.smile}` : `取消关注成功~~${UNICODE_CHAR.hugface}`; - } - messageBus.dispatch(messageType, msg); - } + const toggleHandler = async (isFollow: boolean) => { + if (!userStore.isLogin) { + messageBus.dispatch('warnMessage', '阿娜达~要先登录账号噢~~'); + return; + } + const { code } = await targetRequestMethod({ + id: props.userId, + sure: isFollow, + }); + const isSuccess = code === 200; + let messageType = 'warnMessage'; + let msg = '关注失败!'; + if (isSuccess) { + props.onUpdateFollow!(isFollow); + messageType = 'successMessage'; + msg = isFollow + ? `关注成功~~${UNICODE_CHAR.smile}` + : `取消关注成功~~${UNICODE_CHAR.hugface}`; + } + messageBus.dispatch(messageType, msg); + }; - return () => { - const { followed } = props; - return ( - toggleHandler(!followed)}> -
        - - {followed ? '已关注' : '关注'} -
        -
        - ); - }; - }, + return () => { + const { followed } = props; + return ( + toggleHandler(!followed)} + > +
        + + {followed ? '已关注' : '关注'} +
        +
        + ); + }; + }, }); diff --git a/src/widgets/header-setting/index.scss b/src/widgets/header-setting/index.scss index 829dfe3..7754651 100644 --- a/src/widgets/header-setting/index.scss +++ b/src/widgets/header-setting/index.scss @@ -1,72 +1,69 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .yplayer-header-setting { - @include flexHcVc(); - margin-right: 150px; - transition: color 0.14s; - cursor: pointer; - - .icon-menu { - color: #555; - font-size: 20px; - } + @include flexHcVc(); + margin-right: 150px; + transition: color 0.14s; + cursor: pointer; + + .icon-menu { + color: #555; + font-size: 20px; + } - &:hover { - color: var(--theme); - } + &:hover { + color: var(--theme); + } } .setting-dropdown { - - .menu-option { - @include flexVc(); - height: 34px; - cursor: pointer; - - &:hover { - background-color: rgba(230, 230, 230, .95); - } + .menu-option { + @include flexVc(); + height: 34px; + cursor: pointer; - .option-icon { - width: calc(18px * 2 + 20px); - padding: 0 18px; - font-size: 20px; + &:hover { + background-color: rgba(230, 230, 230, 0.95); + } - .icon-shezhi { - font-size: inherit - } + .option-icon { + width: calc(18px * 2 + 20px); + padding: 0 18px; + font-size: 20px; - } - - p { - width: 150px; - font-size: 12px; - } - - } + .icon-shezhi { + font-size: inherit; + } + } + p { + width: 150px; + font-size: 12px; + } + } } - .setting-box { - @include flexHbC(); - display: none; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: #fff; - min-width: 700px; - min-height: 420px; - border-radius: 2px; - filter: drop-shadow(0 0 30px rgba(100, 100, 100, 0.8)); - z-index: 99999999; + @include flexHbC(); + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #fff; + min-width: 700px; + min-height: 420px; + border-radius: 2px; + filter: drop-shadow(0 0 30px rgba(100, 100, 100, 0.8)); + z-index: 99999999; - h6 { - padding: 28px 0; - border-bottom: 1px solid #ddd; - color: #555; - text-align: center; - font-size: 18px; - } + h6 { + padding: 28px 0; + border-bottom: 1px solid #ddd; + color: #555; + text-align: center; + font-size: 18px; + } } diff --git a/src/widgets/header-setting/index.tsx b/src/widgets/header-setting/index.tsx index eaf3e45..c2b2906 100644 --- a/src/widgets/header-setting/index.tsx +++ b/src/widgets/header-setting/index.tsx @@ -1,92 +1,98 @@ -import { defineComponent, h, nextTick, reactive, ref, Teleport } from "vue"; -import { useRouter } from "vue-router"; +/** @format */ + +import { defineComponent, h, nextTick, reactive, ref, Teleport } from 'vue'; +import { useRouter } from 'vue-router'; import { NDropdown, NModal, useDialog } from 'naive-ui'; -import { DropdownMixedOption } from "naive-ui/lib/dropdown/src/interface"; -import useUserStore from "@stores/user"; +import { DropdownMixedOption } from 'naive-ui/lib/dropdown/src/interface'; +import useUserStore from '@stores/user'; import { logout } from '@api/auth'; -import "./index.scss"; - +import './index.scss'; export type MenuOption = { - title: string; + title: string; }; const content = { - menu: '主菜单', - about: '关于', - setting: '设置', - toggleAccount: '切换账号', -} + menu: '主菜单', + about: '关于', + setting: '设置', + toggleAccount: '切换账号', +}; export default defineComponent({ - name: "MainMenu", + name: 'MainMenu', - setup(props, context) { - const router = useRouter(); + setup(props, context) { + const router = useRouter(); - const isDropDownShow = ref(false); + const isDropDownShow = ref(false); - const hideDropDown = () => isDropDownShow.value = false; - const showDropDown = () => isDropDownShow.value = true; + const hideDropDown = () => (isDropDownShow.value = false); + const showDropDown = () => (isDropDownShow.value = true); - const userStore = useUserStore(); + const userStore = useUserStore(); - const options: DropdownMixedOption[] = [ - { - key: content.setting, - type: 'render', - render() { - return - }, - props: { - onClick: () => { - router.push("/setting"); - hideDropDown(); - }, - title: content.setting - } - }, - { - key: content.toggleAccount, - type: 'render', - render() { - return userStore.isLogin && - }, - props: { - onClick: async () => { - hideDropDown(); - await userStore.logOut(); - }, - title: content.toggleAccount - } - }, - ]; + const options: DropdownMixedOption[] = [ + { + key: content.setting, + type: 'render', + render() { + return ( + + ); + }, + props: { + onClick: () => { + router.push('/setting'); + hideDropDown(); + }, + title: content.setting, + }, + }, + { + key: content.toggleAccount, + type: 'render', + render() { + return ( + userStore.isLogin && ( + + ) + ); + }, + props: { + onClick: async () => { + hideDropDown(); + await userStore.logOut(); + }, + title: content.toggleAccount, + }, + }, + ]; - return () => { - return ( -
        - - - -
        - ); - }; - }, + return () => { + return ( +
        + + + +
        + ); + }; + }, }); diff --git a/src/widgets/infinity-scroll/index.ts b/src/widgets/infinity-scroll/index.ts index 4f739b3..41b9c03 100644 --- a/src/widgets/infinity-scroll/index.ts +++ b/src/widgets/infinity-scroll/index.ts @@ -1,167 +1,161 @@ /** - * @copyfrom element-plus + * @format + * @copyfrom element-plus */ import { nextTick } from 'vue'; import type { ObjectDirective, ComponentPublicInstance } from 'vue'; import { throttle, is, getOffsetTopDistance, getScrollContainer } from '@utils/index'; -export const SCOPE = 'YuanInfiniteScroll' -export const CHECK_INTERVAL = 50 -export const DEFAULT_DELAY = 200 -export const DEFAULT_DISTANCE = 0 +export const SCOPE = 'YuanInfiniteScroll'; +export const CHECK_INTERVAL = 50; +export const DEFAULT_DELAY = 200; +export const DEFAULT_DISTANCE = 0; const attributes = { - delay: { - type: Number, - default: DEFAULT_DELAY, - }, - distance: { - type: Number, - default: DEFAULT_DISTANCE, - }, - disabled: { - type: Boolean, - default: false, - }, - immediate: { - type: Boolean, - default: true, - }, -} - -type Attrs = typeof attributes + delay: { + type: Number, + default: DEFAULT_DELAY, + }, + distance: { + type: Number, + default: DEFAULT_DISTANCE, + }, + disabled: { + type: Boolean, + default: false, + }, + immediate: { + type: Boolean, + default: true, + }, +}; + +type Attrs = typeof attributes; type ScrollOptions = { [K in keyof Attrs]: Attrs[K]['default'] } & PlainObject; -type InfiniteScrollCallback = () => void +type InfiniteScrollCallback = () => void; type InfiniteScrollEl = HTMLElement & { - [SCOPE]: { - container: HTMLElement | Window - containerEl: HTMLElement - instance: ComponentPublicInstance - delay: number // export for test - lastScrollTop: number - cb: InfiniteScrollCallback - onScroll: () => void - observer?: MutationObserver - } -} + [SCOPE]: { + container: HTMLElement | Window; + containerEl: HTMLElement; + instance: ComponentPublicInstance; + delay: number; // export for test + lastScrollTop: number; + cb: InfiniteScrollCallback; + onScroll: () => void; + observer?: MutationObserver; + }; +}; const getScrollOptions = ( - el: HTMLElement, - instance: ComponentPublicInstance + el: HTMLElement, + instance: ComponentPublicInstance, ): ScrollOptions => { - return Object.entries(attributes).reduce((acm, [name, option]) => { - const { type, default: defaultValue } = option - const attrVal = el.getAttribute(`infinite-scroll-${name}`)!; - let value = instance[attrVal] ?? attrVal ?? defaultValue - value = value === 'false' ? false : value - value = type(value) - acm[name] = Number.isNaN(value) ? defaultValue : value - return acm - }, {} as ScrollOptions) -} + return Object.entries(attributes).reduce((acm, [name, option]) => { + const { type, default: defaultValue } = option; + const attrVal = el.getAttribute(`infinite-scroll-${name}`)!; + let value = instance[attrVal] ?? attrVal ?? defaultValue; + value = value === 'false' ? false : value; + value = type(value); + acm[name] = Number.isNaN(value) ? defaultValue : value; + return acm; + }, {} as ScrollOptions); +}; const destroyObserver = (el: InfiniteScrollEl) => { - const { observer } = el[SCOPE] + const { observer } = el[SCOPE]; - if (observer) { - observer.disconnect() - delete el[SCOPE].observer - } -} + if (observer) { + observer.disconnect(); + delete el[SCOPE].observer; + } +}; const handleScroll = (el: InfiniteScrollEl, cb: InfiniteScrollCallback) => { - const { container, containerEl, instance, observer, lastScrollTop } = - el[SCOPE] - const { disabled, distance } = getScrollOptions(el, instance) - const { clientHeight, scrollHeight, scrollTop } = containerEl - const delta = scrollTop - lastScrollTop - - el[SCOPE].lastScrollTop = scrollTop; - - // trigger only if full check has done and not disabled and scroll down - if (observer || disabled || delta < 0) return - - let shouldTrigger = false - - if (container === el) { - shouldTrigger = scrollHeight - (clientHeight + scrollTop) <= distance - } else { - // get the scrollHeight since el might be visible overflow - const { clientTop, scrollHeight: height } = el - const offsetTop = getOffsetTopDistance(el, containerEl); - shouldTrigger = - scrollTop + clientHeight >= offsetTop + clientTop + height - distance - } - - if (shouldTrigger) { - cb.call(instance) - } -} + const { container, containerEl, instance, observer, lastScrollTop } = el[SCOPE]; + const { disabled, distance } = getScrollOptions(el, instance); + const { clientHeight, scrollHeight, scrollTop } = containerEl; + const delta = scrollTop - lastScrollTop; -function checkFull(el: InfiniteScrollEl, cb: InfiniteScrollCallback) { - const { containerEl, instance } = el[SCOPE] - const { disabled } = getScrollOptions(el, instance) + el[SCOPE].lastScrollTop = scrollTop; - if (disabled) return + // trigger only if full check has done and not disabled and scroll down + if (observer || disabled || delta < 0) return; - if (containerEl.scrollHeight <= containerEl.clientHeight) { - cb.call(instance) - } else { - destroyObserver(el) - } -} + let shouldTrigger = false; + + if (container === el) { + shouldTrigger = scrollHeight - (clientHeight + scrollTop) <= distance; + } else { + // get the scrollHeight since el might be visible overflow + const { clientTop, scrollHeight: height } = el; + const offsetTop = getOffsetTopDistance(el, containerEl); + shouldTrigger = scrollTop + clientHeight >= offsetTop + clientTop + height - distance; + } + + if (shouldTrigger) { + cb.call(instance); + } +}; + +function checkFull(el: InfiniteScrollEl, cb: InfiniteScrollCallback) { + const { containerEl, instance } = el[SCOPE]; + const { disabled } = getScrollOptions(el, instance); + + if (disabled) return; -const VInfiniteScroll: ObjectDirective< - InfiniteScrollEl, - InfiniteScrollCallback -> = { - async mounted(el, binding) { - const { instance, value: cb } = binding - - if (!is.function(cb)) { - console.error("'v-infinite-scroll' binding value must be a function") - } - - // ensure parentNode mounted - await nextTick() - - const { delay, immediate } = getScrollOptions(el, instance!) - const container = getScrollContainer(el, true) - const containerEl = - container === window - ? document.documentElement - : (container as HTMLElement) - const onScroll = throttle(handleScroll.bind(null, el, cb), delay) - - if (!container) return - - el[SCOPE] = { - instance: instance!, - container, - containerEl, - delay, - cb, - onScroll, - lastScrollTop: containerEl.scrollTop, - } - - if (immediate) { - const observer = new MutationObserver( - throttle(checkFull.bind(null, el, cb), CHECK_INTERVAL) - ) - el[SCOPE].observer = observer - observer.observe(el, { childList: true, subtree: true }) - checkFull(el, cb) - } - - container.addEventListener('scroll', onScroll) - }, - unmounted(el) { - const { container, onScroll } = el[SCOPE] - container?.removeEventListener('scroll', onScroll) - destroyObserver(el) - }, + if (containerEl.scrollHeight <= containerEl.clientHeight) { + cb.call(instance); + } else { + destroyObserver(el); + } } -export default VInfiniteScroll \ No newline at end of file +const VInfiniteScroll: ObjectDirective = { + async mounted(el, binding) { + const { instance, value: cb } = binding; + + if (!is.function(cb)) { + console.error("'v-infinite-scroll' binding value must be a function"); + } + + // ensure parentNode mounted + await nextTick(); + + const { delay, immediate } = getScrollOptions(el, instance!); + const container = getScrollContainer(el, true); + const containerEl = + container === window ? document.documentElement : (container as HTMLElement); + const onScroll = throttle(handleScroll.bind(null, el, cb), delay); + + if (!container) return; + + el[SCOPE] = { + instance: instance!, + container, + containerEl, + delay, + cb, + onScroll, + lastScrollTop: containerEl.scrollTop, + }; + + if (immediate) { + const observer = new MutationObserver( + throttle(checkFull.bind(null, el, cb), CHECK_INTERVAL), + ); + el[SCOPE].observer = observer; + observer.observe(el, { childList: true, subtree: true }); + checkFull(el, cb); + } + + container.addEventListener('scroll', onScroll); + }, + unmounted(el) { + const { container, onScroll } = el[SCOPE]; + container?.removeEventListener('scroll', onScroll); + destroyObserver(el); + }, +}; + +export default VInfiniteScroll; diff --git a/src/widgets/infinity-scroll/infinity-scroll.tsx b/src/widgets/infinity-scroll/infinity-scroll.tsx index 225140c..d40b989 100644 --- a/src/widgets/infinity-scroll/infinity-scroll.tsx +++ b/src/widgets/infinity-scroll/infinity-scroll.tsx @@ -1,88 +1,86 @@ -import { createVNode, ref, defineComponent, withDirectives, onDeactivated, onActivated, vShow } from "vue"; -import VInfiniteScroll from "."; +/** @format */ + +import { + createVNode, + ref, + defineComponent, + withDirectives, + onDeactivated, + onActivated, + vShow, +} from 'vue'; +import VInfiniteScroll from '.'; import { messageBus } from '@utils/event/register'; -import { UNICODE_CHAR } from "@/utils"; +import { UNICODE_CHAR } from '@/utils'; export default defineComponent({ - name: "YuanInfinityScroll", - props: { - sliceInterval: { - type: [Number, String], - required: false, - default: 20, - }, - total: { - type: [Number, String], - required: false, - default: Infinity, - }, - baseCount: { - type: [Number, String], - required: false, - default: 20, - }, - }, - setup(props, { emit, slots }) { - - const isloading = ref(false); - const curCount = ref(+props.baseCount); - const disabled = ref(false); - const deferLoader = () => { - isloading.value = true; - // messageBus.dispatch('startLoading'); - setTimeout(() => { - curCount.value += +props.sliceInterval; - isloading.value = false; - disabled.value = curCount.value >= props.total; - // messageBus.dispatch('finishLoading'); - }, 1000); - }; + name: 'YuanInfinityScroll', + props: { + sliceInterval: { + type: [Number, String], + required: false, + default: 20, + }, + total: { + type: [Number, String], + required: false, + default: Infinity, + }, + baseCount: { + type: [Number, String], + required: false, + default: 20, + }, + }, + setup(props, { emit, slots }) { + const isloading = ref(false); + const curCount = ref(+props.baseCount); + const disabled = ref(false); + const deferLoader = () => { + isloading.value = true; + // messageBus.dispatch('startLoading'); + setTimeout(() => { + curCount.value += +props.sliceInterval; + isloading.value = false; + disabled.value = curCount.value >= props.total; + // messageBus.dispatch('finishLoading'); + }, 1000); + }; - onDeactivated(() => { - disabled.value = true; - }); + onDeactivated(() => { + disabled.value = true; + }); - onActivated(() => { - disabled.value = false; - }); - - const renderWrapTemplate = () => ( - createVNode( - 'div', - { - class: "infinity-scrolling", - 'infinite-scroll-delay': 500, - 'infinite-scroll-immediate': false, - 'infinite-scroll-distance': 30, - 'infinite-scroll-disabled': disabled.value, - }, - [ - slots.default!(curCount.value), - createVNode('div', - { - class: "scroll-loading", - style: "padding: 2em 0;text-align: center", - visibility: isloading.value, - }, - `正在加载${UNICODE_CHAR.hugface}...` - ) - ] - ) - ); + onActivated(() => { + disabled.value = false; + }); - return () => { - return ( - withDirectives( - renderWrapTemplate(), - [ - [ - VInfiniteScroll, - deferLoader, - ] - ] - ) - ) - } + const renderWrapTemplate = () => + createVNode( + 'div', + { + class: 'infinity-scrolling', + 'infinite-scroll-delay': 500, + 'infinite-scroll-immediate': false, + 'infinite-scroll-distance': 30, + 'infinite-scroll-disabled': disabled.value, + }, + [ + slots.default!(curCount.value), + createVNode( + 'div', + { + class: 'scroll-loading', + style: 'padding: 2em 0;text-align: center', + visibility: isloading.value, + }, + `正在加载${UNICODE_CHAR.hugface}...`, + ), + ], + ); - } -}); \ No newline at end of file + return () => { + return withDirectives(renderWrapTemplate(), [[VInfiniteScroll, deferLoader]]); + }; + }, +}); diff --git a/src/widgets/music-list/index.scss b/src/widgets/music-list/index.scss index c10920f..83e1991 100644 --- a/src/widgets/music-list/index.scss +++ b/src/widgets/music-list/index.scss @@ -1,130 +1,127 @@ -@import "@scss/variable"; +/** @format */ -.music-list { - - position: relative; - min-height: 340px; - - .music-item { - position: relative; - @include flexVc(); - padding: 12px; - transition: background-color 0.04s; - border-radius: 6px; - - &:hover { - background-color: rgba(120, 102, 110, 0.1); - .tools { - opacity: 1; - } - } - - .playbill { - position: relative; - overflow: hidden; - border-radius: inherit; - cursor: pointer; - width: 20%; - height: 100%; - - &:hover { - img { - filter: brightness(88%); - height: 100%; - } - i { - opacity: 1; - } - } - - i { - opacity: 0; - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%,-50%); - transition: .04s; - color: #eee; - font-size: 22px; - &:hover { - filter:brightness(120%) - } - } - - } - - .info { - @include flexHe(); - flex-direction: column; - width: 50%; - // max-width: 180px; - padding: 0 12px; - h6 { - color: #333; - padding-bottom: 12px; - font-size: 14px; - font-weight: 500; - } - > div { - color: #777; - } - } - - .tools { - opacity: 0; - @include flexHeVc(); - width: 30%; - color: rgba(80, 80, 80, 0.65); - .tool-item { - font-size: 24px; - cursor: pointer; - &:hover { - color: var(--theme); - } - } - } - - } - - .suspension { - position: absolute; - left: 0; - top: 0; - max-width: 340px; - min-width: 240px; - filter: drop-shadow(0 0 20px rgba(80, 80, 80, 0.2)); - z-index: 80; - border-radius: 6px; - overflow:hidden; - - h6 { - padding: 8px 14px; - background-color: var(--theme); - color: $colorbase; - filter: brightness(115%); - // background-color: rgba(244, 243, 244, 0.95); - // color: #566; - font-size: 14px; - } - - .desc { - padding: 6px 14px; - background-color: $colorbase; - color: #666; - font-size: 13px; - - p { - @include flex(); - padding: 4px 0; - line-height: 1.4; - - > em { - width: 5em; - white-space: nowrap; - } - - } - } - } +@import '@scss/variable'; +.music-list { + position: relative; + min-height: 340px; + + .music-item { + position: relative; + @include flexVc(); + padding: 12px; + transition: background-color 0.04s; + border-radius: 6px; + + &:hover { + background-color: rgba(120, 102, 110, 0.1); + .tools { + opacity: 1; + } + } + + .playbill { + position: relative; + overflow: hidden; + border-radius: inherit; + cursor: pointer; + width: 20%; + height: 100%; + + &:hover { + img { + filter: brightness(88%); + height: 100%; + } + i { + opacity: 1; + } + } + + i { + opacity: 0; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + transition: 0.04s; + color: #eee; + font-size: 22px; + &:hover { + filter: brightness(120%); + } + } + } + + .info { + @include flexHe(); + flex-direction: column; + width: 50%; + // max-width: 180px; + padding: 0 12px; + h6 { + color: #333; + padding-bottom: 12px; + font-size: 14px; + font-weight: 500; + } + > div { + color: #777; + } + } + + .tools { + opacity: 0; + @include flexHeVc(); + width: 30%; + color: rgba(80, 80, 80, 0.65); + .tool-item { + font-size: 24px; + cursor: pointer; + &:hover { + color: var(--theme); + } + } + } + } + + .suspension { + position: absolute; + left: 0; + top: 0; + max-width: 340px; + min-width: 240px; + filter: drop-shadow(0 0 20px rgba(80, 80, 80, 0.2)); + z-index: 80; + border-radius: 6px; + overflow: hidden; + + h6 { + padding: 8px 14px; + background-color: var(--theme); + color: $colorbase; + filter: brightness(115%); + // background-color: rgba(244, 243, 244, 0.95); + // color: #566; + font-size: 14px; + } + + .desc { + padding: 6px 14px; + background-color: $colorbase; + color: #666; + font-size: 13px; + + p { + @include flex(); + padding: 4px 0; + line-height: 1.4; + + > em { + width: 5em; + white-space: nowrap; + } + } + } + } } diff --git a/src/widgets/music-list/index.tsx b/src/widgets/music-list/index.tsx index c0a1a62..156edfe 100644 --- a/src/widgets/music-list/index.tsx +++ b/src/widgets/music-list/index.tsx @@ -1,256 +1,238 @@ -import { - computed, - defineComponent, - PropType, - reactive, - ref, - shallowReactive, -} from "vue"; +/** @format */ + +import { computed, defineComponent, PropType, reactive, ref, shallowReactive } from 'vue'; -import { NGrid, NGridItem, NIcon } from "naive-ui"; -import "./index.scss"; -import { NewestSongInfo, SongInfo } from "@/types/song"; -import { closest, freeze, getPointerOffsetElm, hasOwnProperty, is, padPicCrop } from "@/utils"; +import { NGrid, NGridItem, NIcon } from 'naive-ui'; +import './index.scss'; +import { NewestSongInfo, SongInfo } from '@/types/song'; import { - CurrentSongInfo, - getModifiedNewestSongInfo, - getModifiedSongInfo, -} from "@/utils/apiSpecial"; -import { MusicLoveIcon, MusicSinger } from "../music-tiny-comp"; -import usePlayerStore from "@/stores/player"; -import { PAGE_SIZE } from "@/utils/preference"; + closest, + freeze, + getPointerOffsetElm, + hasOwnProperty, + is, + padPicCrop, +} from '@/utils'; +import { + CurrentSongInfo, + getModifiedNewestSongInfo, + getModifiedSongInfo, +} from '@/utils/apiSpecial'; +import { MusicLoveIcon, MusicSinger } from '../music-tiny-comp'; +import usePlayerStore from '@/stores/player'; +import { PAGE_SIZE } from '@/utils/preference'; import YuanInfinityScroll from '@widgets/infinity-scroll/infinity-scroll'; - export const MusicItem = defineComponent({ - name: "MusicItem", - props: { - musicInfo: { - type: Object as PropType, - required: true, - }, - }, - setup(props, { slots, emit }) { - const musicItemRef = ref(); - const playerStore = usePlayerStore(); - - const playBtnClickHandler = () => { - playerStore.handlePlaySoundNeededData(props.musicInfo.id); - }; - - return () => { - const { - album: { picUrl }, - musicName, - albumName, - singers, - localedDuration, - localedMark, - localedPublishTime, - } = props.musicInfo; - - return ( -
        -
        -
        - - playBtnClickHandler()}> -
        -
        -
        -
        - {musicName} -
        -
        - -
        -
        -
        -
        - -
        - { - - } -
        { }} - > - -
        -
        -
        - ); - }; - }, + name: 'MusicItem', + props: { + musicInfo: { + type: Object as PropType, + required: true, + }, + }, + setup(props, { slots, emit }) { + const musicItemRef = ref(); + const playerStore = usePlayerStore(); + + const playBtnClickHandler = () => { + playerStore.handlePlaySoundNeededData(props.musicInfo.id); + }; + + return () => { + const { + album: { picUrl }, + musicName, + albumName, + singers, + localedDuration, + localedMark, + localedPublishTime, + } = props.musicInfo; + + return ( +
        +
        +
        + + playBtnClickHandler()} + > +
        +
        +
        +
        + {musicName} +
        +
        + +
        +
        +
        +
        + +
        + {} +
        {}}> + +
        +
        +
        + ); + }; + }, }); export default defineComponent({ - name: "MusicList", - props: { - category: { - type: String as PropType<"common" | "newest">, - required: false, - default: "common", - }, - musiclists: { - type: Array as PropType<(SongInfo | NewestSongInfo)[]>, - required: false, - default: () => [], - }, - defaultLimit: { - type: Number as PropType, - required: false, - default: PAGE_SIZE.DEFAULT, - }, - total: { - type: Number as PropType, - required: false, - default: 0, - }, - gaps: { - type: Object as PropType>>, - required: false, - default: () => ({ x: 20, y: 20 }), - }, - cols: { - type: Number as PropType, - required: false, - default: 3, - }, - }, - setup(props, { slots, emit }) { - const category = props.category; - - const musicListRef = ref(); - - const songData = computed(() => { - const dataList = [...props.musiclists] || []; - let realSongInfo: CurrentSongInfo[]; - if (category === "newest") { - realSongInfo = dataList.map((info) => { - const realSongInfo = getModifiedNewestSongInfo( - info as NewestSongInfo - ); - return realSongInfo; - }); - } else { - realSongInfo = dataList.map((info) => { - const realSongInfo = getModifiedSongInfo(info as SongInfo); - return realSongInfo; - }); - } - return reactive(realSongInfo); - }); - const suspensionInfo = shallowReactive({ - x: 0, - y: 0, - show: false, - }); - - const currentHoverMusicInfo = ref({} as typeof songData.value[number]); - - const judgeModifySuspension = (ev: MouseEvent) => { - const tarMusicItemElm = closest(ev.target as Element, '.index-layer'); - if (!tarMusicItemElm) { - suspensionInfo.show = false; - return; - } - currentHoverMusicInfo.value = songData.value[+tarMusicItemElm.getAttribute('index')!]; - const { offsetX, offsetY } = getPointerOffsetElm(ev, musicListRef.value!); - suspensionInfo.show = true; - suspensionInfo.x = offsetX + 10; - suspensionInfo.y = offsetY + 10; - }; - const itemMouseEnter = (ev: MouseEvent) => { - judgeModifySuspension(ev); - }; - - const itemMouseMove = (ev: MouseEvent) => { - judgeModifySuspension(ev); - }; - - const itemMouseLeave = (ev: MouseEvent) => { - suspensionInfo.show = false; - }; - - // const renderCurrentHoverItemSuspension = () => { - // const { musicName, localedDuration, localedMark, localedPublishTime, albumName } = currentHoverMusicInfo.value; - // return ( - // - // ) - // } - - return () => { - const { - gaps: { x, y }, - cols, - } = props; - return ( - -
        - - { - { - default(currentCount: number) { - return ( - - { - songData.value.slice(0, currentCount).map((item, index) => { - return ( - -
        - -
        -
        - ); - }) - } -
        - ) - } - } - } -
        - {/* { + name: 'MusicList', + props: { + category: { + type: String as PropType<'common' | 'newest'>, + required: false, + default: 'common', + }, + musiclists: { + type: Array as PropType<(SongInfo | NewestSongInfo)[]>, + required: false, + default: () => [], + }, + defaultLimit: { + type: Number as PropType, + required: false, + default: PAGE_SIZE.DEFAULT, + }, + total: { + type: Number as PropType, + required: false, + default: 0, + }, + gaps: { + type: Object as PropType>>, + required: false, + default: () => ({ x: 20, y: 20 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 3, + }, + }, + setup(props, { slots, emit }) { + const category = props.category; + + const musicListRef = ref(); + + const songData = computed(() => { + const dataList = [...props.musiclists] || []; + let realSongInfo: CurrentSongInfo[]; + if (category === 'newest') { + realSongInfo = dataList.map((info) => { + const realSongInfo = getModifiedNewestSongInfo(info as NewestSongInfo); + return realSongInfo; + }); + } else { + realSongInfo = dataList.map((info) => { + const realSongInfo = getModifiedSongInfo(info as SongInfo); + return realSongInfo; + }); + } + return reactive(realSongInfo); + }); + const suspensionInfo = shallowReactive({ + x: 0, + y: 0, + show: false, + }); + + const currentHoverMusicInfo = ref({} as typeof songData.value[number]); + + const judgeModifySuspension = (ev: MouseEvent) => { + const tarMusicItemElm = closest(ev.target as Element, '.index-layer'); + if (!tarMusicItemElm) { + suspensionInfo.show = false; + return; + } + currentHoverMusicInfo.value = + songData.value[+tarMusicItemElm.getAttribute('index')!]; + const { offsetX, offsetY } = getPointerOffsetElm(ev, musicListRef.value!); + suspensionInfo.show = true; + suspensionInfo.x = offsetX + 10; + suspensionInfo.y = offsetY + 10; + }; + const itemMouseEnter = (ev: MouseEvent) => { + judgeModifySuspension(ev); + }; + + const itemMouseMove = (ev: MouseEvent) => { + judgeModifySuspension(ev); + }; + + const itemMouseLeave = (ev: MouseEvent) => { + suspensionInfo.show = false; + }; + + // const renderCurrentHoverItemSuspension = () => { + // const { musicName, localedDuration, localedMark, localedPublishTime, albumName } = currentHoverMusicInfo.value; + // return ( + // + // ) + // } + + return () => { + const { + gaps: { x, y }, + cols, + } = props; + return ( +
        + + {{ + default(currentCount: number) { + return ( + + {songData.value.slice(0, currentCount).map((item, index) => { + return ( + +
        + +
        +
        + ); + })} +
        + ); + }, + }} +
        + {/* { renderCurrentHoverItemSuspension() } */} -
        - ); - }; - }, +
        + ); + }; + }, }); diff --git a/src/widgets/music-tiny-comp/index.scss b/src/widgets/music-tiny-comp/index.scss index e58350b..ea33948 100644 --- a/src/widgets/music-tiny-comp/index.scss +++ b/src/widgets/music-tiny-comp/index.scss @@ -1,142 +1,139 @@ -@import "@scss/variable"; +/** @format */ +@import '@scss/variable'; .play-switch { - @include flexHcVc(); - position: relative; - cursor: pointer; + @include flexHcVc(); + position: relative; + cursor: pointer; } - + .controller-widget { - position: relative; - - &-duplicate { - color: #555; - } - - &-duplicate, - &-noumenon { - cursor: pointer; - i { - font-size: 22px; - } - } - - &-noumenon { - &:hover { - color: var(--theme); - } - } - - &-suspension { - position: absolute; - bottom: calc(100% + 18px); - left: 50%; - z-index: 99; - background-color: #fff; - transform: translateX(-50%); - padding: 8px 0; - border-radius: 8px; - filter: drop-shadow(0 0 28px rgba(117, 102, 98, 0.4)); - - &::after { - content: ""; - position: absolute; - left: 50%; - top: 100%; - border-top: 9px solid #fff; - border-left: 9px solid transparent; - border-right: 9px solid transparent; - transform: translateX(-50%); - } - } - -} + position: relative; + + &-duplicate { + color: #555; + } + + &-duplicate, + &-noumenon { + cursor: pointer; + i { + font-size: 22px; + } + } + + &-noumenon { + &:hover { + color: var(--theme); + } + } + + &-suspension { + position: absolute; + bottom: calc(100% + 18px); + left: 50%; + z-index: 99; + background-color: #fff; + transform: translateX(-50%); + padding: 8px 0; + border-radius: 8px; + filter: drop-shadow(0 0 28px rgba(117, 102, 98, 0.4)); + + &::after { + content: ''; + position: absolute; + left: 50%; + top: 100%; + border-top: 9px solid #fff; + border-left: 9px solid transparent; + border-right: 9px solid transparent; + transform: translateX(-50%); + } + } +} .play-order { - margin-left: 50px; - - &-noumenon{ - color: currentColor; - } - - &-duplicate { - &-item { - @include flexHcVc(); - padding: 8px 18px; - margin: 0 2px; - &:not(:last-of-type) { - border-bottom: 1px solid #eee; - } - - &:hover { - color: var(--theme); - background-color: rgb(248, 245, 244); - } - - span { - @include flexInline(); - width: 4rem; - margin-left: .5rem; - font-size: 14px; - } - } - } - + margin-left: 50px; + + &-noumenon { + color: currentColor; + } + + &-duplicate { + &-item { + @include flexHcVc(); + padding: 8px 18px; + margin: 0 2px; + &:not(:last-of-type) { + border-bottom: 1px solid #eee; + } + + &:hover { + color: var(--theme); + background-color: rgb(248, 245, 244); + } + + span { + @include flexInline(); + width: 4rem; + margin-left: 0.5rem; + font-size: 14px; + } + } + } } .volume { - position: relative; - margin-right: 50px; - - &-progressbar { - @include flexHc(); - height: 160px; - padding: 20px 0 14px 0; - } - - &-ratio { - padding-bottom: 14px; - color: #555; - font-size: 18px; - text-align: center; - } - - &-duplicate { - padding: 10px 28px 2px 28px; - border-top: 1px solid #eee; - &:hover { - color: var(--theme); - } - } - + position: relative; + margin-right: 50px; + + &-progressbar { + @include flexHc(); + height: 160px; + padding: 20px 0 14px 0; + } + + &-ratio { + padding-bottom: 14px; + color: #555; + font-size: 18px; + text-align: center; + } + + &-duplicate { + padding: 10px 28px 2px 28px; + border-top: 1px solid #eee; + &:hover { + color: var(--theme); + } + } } .music-love-icon { - font-size: 22px; - color: currentColor; - cursor: pointer; - transition: filter .1s, color .1s; - - &:hover { - filter: brightness(116%); - } - - &:hover, - &[loved="true"] { - color: var(--theme); - } - + font-size: 22px; + color: currentColor; + cursor: pointer; + transition: filter 0.1s, color 0.1s; + + &:hover { + filter: brightness(116%); + } + + &:hover, + &[loved='true'] { + color: var(--theme); + } } .music-singer { - font-size: 13px; - font-weight: 400; - & > span { - transition: 0.1s; - cursor: pointer; - &:hover { - color: var(--theme); - } - } + font-size: 13px; + font-weight: 400; + & > span { + transition: 0.1s; + cursor: pointer; + &:hover { + color: var(--theme); + } + } } diff --git a/src/widgets/music-tiny-comp/index.tsx b/src/widgets/music-tiny-comp/index.tsx index 90b8dee..9b47c95 100644 --- a/src/widgets/music-tiny-comp/index.tsx +++ b/src/widgets/music-tiny-comp/index.tsx @@ -1,333 +1,372 @@ -import { - computed, - customRef, - defineComponent, - PropType, - ref, -} from "vue"; -import "./index.scss"; -import { useRouter } from "vue-router"; -import useUserStore from "@stores/user"; -import { userLikeMusic } from "@/api/user"; -import usePlayerStore, { playerQueue, currentSongRefGlobal } from "@stores/player"; -import { orderRefGlobal } from '@stores/audio' -import { messageBus } from "@/utils/event/register"; -import { CurrentSongInfo, } from "@/utils/apiSpecial"; -import { onClickOutside, onKeyStroke, } from "@vueuse/core"; -import { decimalToPercent, is, UNICODE_CHAR } from "@/utils"; -import ProgressBar, { ProgressInfo, } from "@widgets/progress-bar"; -import { playingRefGlobal, volumeRefGlobal, muteRefGlobal, Order } from "@/stores/audio"; +/** @format */ + +import { computed, customRef, defineComponent, PropType, ref } from 'vue'; +import './index.scss'; +import { useRouter } from 'vue-router'; +import useUserStore from '@stores/user'; +import { userLikeMusic } from '@/api/user'; +import usePlayerStore, { playerQueue, currentSongRefGlobal } from '@stores/player'; +import { orderRefGlobal } from '@stores/audio'; +import { messageBus } from '@/utils/event/register'; +import { CurrentSongInfo } from '@/utils/apiSpecial'; +import { onClickOutside, onKeyStroke } from '@vueuse/core'; +import { decimalToPercent, is, UNICODE_CHAR } from '@/utils'; +import ProgressBar, { ProgressInfo } from '@widgets/progress-bar'; +import { playingRefGlobal, volumeRefGlobal, muteRefGlobal, Order } from '@/stores/audio'; export enum PlayOrderInfo { - order = '顺序播放', - random = '随机播放', - singleLoop = '单曲循环' + order = '顺序播放', + random = '随机播放', + singleLoop = '单曲循环', } export const PlayOrder = defineComponent({ - name: 'PlayOrder', - setup(props, { slots, emit }) { - const playOrderRef = ref() - const playerStore = usePlayerStore(); - const suspensionShow = ref(false); - - onClickOutside(playOrderRef, () => { - suspensionShow.value = false; - }) - - const switchOrderStatus = (orderStatus: Order) => { - orderRefGlobal.value = orderStatus; - suspensionShow.value = false; - } + name: 'PlayOrder', + setup(props, { slots, emit }) { + const playOrderRef = ref(); + const playerStore = usePlayerStore(); + const suspensionShow = ref(false); - const noumenonClick = () => { - suspensionShow.value = true; - } + onClickOutside(playOrderRef, () => { + suspensionShow.value = false; + }); - return () => { - const order = orderRefGlobal.value; - return ( -
        -
        - - - -
        -
        -
        -
        switchOrderStatus('random')}> - - {PlayOrderInfo.random} -
        -
        switchOrderStatus('order')}> - - {PlayOrderInfo.order} -
        -
        switchOrderStatus('singleLoop')}> - - {PlayOrderInfo.singleLoop} -
        -
        -
        -
        - ) + const switchOrderStatus = (orderStatus: Order) => { + orderRefGlobal.value = orderStatus; + suspensionShow.value = false; + }; - } + const noumenonClick = () => { + suspensionShow.value = true; + }; - } -}) + return () => { + const order = orderRefGlobal.value; + return ( +
        +
        + + + +
        +
        +
        +
        switchOrderStatus('random')} + > + + {PlayOrderInfo.random} +
        +
        switchOrderStatus('order')} + > + + {PlayOrderInfo.order} +
        +
        switchOrderStatus('singleLoop')} + > + + {PlayOrderInfo.singleLoop} +
        +
        +
        +
        + ); + }; + }, +}); export const PlayStatusSwitch = defineComponent({ - name: 'PlayStatusSwitch', - props: { - id: { - type: [String, Number] as PropType, - default: '', - } - }, - setup(props, { slots, emit }) { - - const playerStore = usePlayerStore(); - const playIconClass = computed(() => { - return props.id === '' ? 'icon-bofan' : 'icon-play' - }) - - //是否是当前正在播放的歌曲 - const isCurrentPlayingSong = computed(() => { - const id = props.id; - return id === '' || currentSongRefGlobal.value.id === id - }); - - //播放按钮状态 - const playing = computed(() => { - return isCurrentPlayingSong.value ? playingRefGlobal.value : false; - }); + name: 'PlayStatusSwitch', + props: { + id: { + type: [String, Number] as PropType, + default: '', + }, + }, + setup(props, { slots, emit }) { + const playerStore = usePlayerStore(); + const playIconClass = computed(() => { + return props.id === '' ? 'icon-bofan' : 'icon-play'; + }); - const switchHandler = () => { - if (isCurrentPlayingSong.value) { - if(is.emptyArray(playerQueue.value)) { - messageBus.dispatch('warnMessage', `阿娜达~快去添加音乐⑧${UNICODE_CHAR.smile}`, { - duration: 4000, - closable: true, - }); - return; - } - playingRefGlobal.value = !playingRefGlobal.value; - } - else { - playerStore.handlePlaySoundNeededData(+props.id); - } - } - return () => { - const playingValue = playing.value; - const playingIconClassValue = playIconClass.value; - return ( -
        - - -
        - ) - } + //是否是当前正在播放的歌曲 + const isCurrentPlayingSong = computed(() => { + const id = props.id; + return id === '' || currentSongRefGlobal.value.id === id; + }); - } -}) + //播放按钮状态 + const playing = computed(() => { + return isCurrentPlayingSong.value ? playingRefGlobal.value : false; + }); + const switchHandler = () => { + if (isCurrentPlayingSong.value) { + if (is.emptyArray(playerQueue.value)) { + messageBus.dispatch( + 'warnMessage', + `阿娜达~快去添加音乐⑧${UNICODE_CHAR.smile}`, + { + duration: 4000, + closable: true, + }, + ); + return; + } + playingRefGlobal.value = !playingRefGlobal.value; + } else { + playerStore.handlePlaySoundNeededData(+props.id); + } + }; + return () => { + const playingValue = playing.value; + const playingIconClassValue = playIconClass.value; + return ( +
        + + +
        + ); + }; + }, +}); export const isCtrlAndArrowUp = ({ key, ctrlKey }: KeyboardEvent) => { - return ctrlKey && key === "ArrowUp"; -} + return ctrlKey && key === 'ArrowUp'; +}; export const isCtrlAndArrowDown = ({ key, ctrlKey }: KeyboardEvent) => { - return ctrlKey && key === 'ArrowDown'; -} + return ctrlKey && key === 'ArrowDown'; +}; export const Volume = defineComponent({ - name: "Volume", - setup(props, { slots, emit }) { - const isShow = ref(false); - const volumeRef = ref(); - const switchShow = () => (isShow.value = !isShow.value); - const volumeData = computed(() => { - const volumeValue = volumeRefGlobal.value; - return { - decimal: volumeValue, - ratio: decimalToPercent(volumeValue), - }; - }); - const volumeChange = ({ decimal }: ProgressInfo) => { - volumeRefGlobal.value = decimal; - }; - const switchMuted = () => { - //如果音量为0,就return - if (volumeRefGlobal.value === 0) return; - //切换静音状态 - muteRefGlobal.value = !muteRefGlobal.value; - }; - onClickOutside(volumeRef, () => (isShow.value = false), { - event: "pointerup", - }); + name: 'Volume', + setup(props, { slots, emit }) { + const isShow = ref(false); + const volumeRef = ref(); + const switchShow = () => (isShow.value = !isShow.value); + const volumeData = computed(() => { + const volumeValue = volumeRefGlobal.value; + return { + decimal: volumeValue, + ratio: decimalToPercent(volumeValue), + }; + }); + const volumeChange = ({ decimal }: ProgressInfo) => { + volumeRefGlobal.value = decimal; + }; + const switchMuted = () => { + //如果音量为0,就return + if (volumeRefGlobal.value === 0) return; + //切换静音状态 + muteRefGlobal.value = !muteRefGlobal.value; + }; + onClickOutside(volumeRef, () => (isShow.value = false), { + event: 'pointerup', + }); - onKeyStroke(isCtrlAndArrowUp, () => { - let tarVolume = volumeRefGlobal.value + .05; - tarVolume > 1 && (tarVolume = 1); - volumeRefGlobal.value = tarVolume; - }, { - eventName: 'keyup', - }) + onKeyStroke( + isCtrlAndArrowUp, + () => { + let tarVolume = volumeRefGlobal.value + 0.05; + tarVolume > 1 && (tarVolume = 1); + volumeRefGlobal.value = tarVolume; + }, + { + eventName: 'keyup', + }, + ); - onKeyStroke(isCtrlAndArrowDown, () => { - let tarVolume = volumeRefGlobal.value - .05; - tarVolume < 0 && (tarVolume = 0); - volumeRefGlobal.value = tarVolume; - }, { - eventName: 'keyup', - }) + onKeyStroke( + isCtrlAndArrowDown, + () => { + let tarVolume = volumeRefGlobal.value - 0.05; + tarVolume < 0 && (tarVolume = 0); + volumeRefGlobal.value = tarVolume; + }, + { + eventName: 'keyup', + }, + ); - return () => { - const mute = muteRefGlobal.value; - const { ratio, decimal } = volumeData.value; - const title = `音量:${ratio};增大音量:Ctrl+Up;减小音量:Ctrl+Down` - return ( -
        -
        - - -
        -
        -
        - -
        -
        {ratio}
        -
        - - -
        -
        -
        - ); - }; - }, + return () => { + const mute = muteRefGlobal.value; + const { ratio, decimal } = volumeData.value; + const title = `音量:${ratio};增大音量:Ctrl+Up;减小音量:Ctrl+Down`; + return ( +
        +
        + + +
        +
        +
        + +
        +
        {ratio}
        +
        + + +
        +
        +
        + ); + }; + }, }); export enum MusicLoveTitle { - "本宝宝喜欢你~~" = 1, - "本宝宝讨厌你~~" = 0, + '本宝宝喜欢你~~' = 1, + '本宝宝讨厌你~~' = 0, } export const MusicLoveIcon = defineComponent({ - name: "MusicLoveIcon", - props: { - songInfo: { - type: Object as PropType, - required: true, - } - }, - setup(props, { slots, emit }) { - const userStore = useUserStore(); + name: 'MusicLoveIcon', + props: { + songInfo: { + type: Object as PropType, + required: true, + }, + }, + setup(props, { slots, emit }) { + const userStore = useUserStore(); - const isLoved = customRef((track, trigger) => ({ - get() { - track(); - return userStore.myLoveListIds.includes(props.songInfo.id); - }, - set(isLove) { - const curId = props.songInfo.id; - if (isLove) { - userStore.myLoveListIds.push(curId); - } else { - userStore.myLoveListIds.some((tarId, i) => { - if (tarId === curId) { - userStore.myLoveListIds.splice(i, 1); - return true; - } - }) - } - trigger(); - } - })) + const isLoved = customRef((track, trigger) => ({ + get() { + track(); + return userStore.myLoveListIds.includes(props.songInfo.id); + }, + set(isLove) { + const curId = props.songInfo.id; + if (isLove) { + userStore.myLoveListIds.push(curId); + } else { + userStore.myLoveListIds.some((tarId, i) => { + if (tarId === curId) { + userStore.myLoveListIds.splice(i, 1); + return true; + } + }); + } + trigger(); + }, + })); - const loveSwitch = async () => { - const { id } = props.songInfo; - if (!id) { - messageBus.dispatch('warnMessage', `还没有播放的歌曲哦~${UNICODE_CHAR.hugface}`); - } else if (!userStore.isLogin) { - messageBus.dispatch('warnMessage', `赶紧登录⑧~${UNICODE_CHAR.hugface}`); - } else { - const willIsLovedValue = !isLoved.value; - const { code } = await userLikeMusic({ - id, - like: willIsLovedValue - }); - if (code == 200) { - isLoved.value = willIsLovedValue; - messageBus.dispatch( - 'successMessage', - `${willIsLovedValue ? `喜欢成功${UNICODE_CHAR.hugface}` : `已移除${UNICODE_CHAR.pensive}`}` - ); - } - } - } + const loveSwitch = async () => { + const { id } = props.songInfo; + if (!id) { + messageBus.dispatch('warnMessage', `还没有播放的歌曲哦~${UNICODE_CHAR.hugface}`); + } else if (!userStore.isLogin) { + messageBus.dispatch('warnMessage', `赶紧登录⑧~${UNICODE_CHAR.hugface}`); + } else { + const willIsLovedValue = !isLoved.value; + const { code } = await userLikeMusic({ + id, + like: willIsLovedValue, + }); + if (code == 200) { + isLoved.value = willIsLovedValue; + messageBus.dispatch( + 'successMessage', + `${ + willIsLovedValue + ? `喜欢成功${UNICODE_CHAR.hugface}` + : `已移除${UNICODE_CHAR.pensive}` + }`, + ); + } + } + }; - return () => { - const isLovedValue = isLoved.value; - const loveTitle = MusicLoveTitle[isLovedValue ? 0 : 1]; - return ( -
        - - -
        - ); - }; - }, + return () => { + const isLovedValue = isLoved.value; + const loveTitle = MusicLoveTitle[isLovedValue ? 0 : 1]; + return ( +
        + + +
        + ); + }; + }, }); - + export const MusicSinger = defineComponent({ - name: "MusicSinger", - props: { - singers: { - type: Array as PropType, - required: true, - }, - class: { - type: String as PropType, - required: false, - default: () => "", - }, - }, - setup(props, { slots, emit }) { - const router = useRouter(); - const toSingerDetailPage = (id: number) => { - router.push({ - path: "/artist", - query: { id }, - }); - }; - return () => { - const { singers } = props; - const classNameObj = { - 'music-singer': true, - [props.class]: true - } - return ( -

        - {singers.map(({ id, name }, i) => { - return ( - <> - toSingerDetailPage(id)} title={name}>{name} - {i !== singers.length - 1 && / } - - ); - })} -

        - ); - }; - }, + name: 'MusicSinger', + props: { + singers: { + type: Array as PropType, + required: true, + }, + class: { + type: String as PropType, + required: false, + default: () => '', + }, + }, + setup(props, { slots, emit }) { + const router = useRouter(); + const toSingerDetailPage = (id: number) => { + router.push({ + path: '/artist', + query: { id }, + }); + }; + return () => { + const { singers } = props; + const classNameObj = { + 'music-singer': true, + [props.class]: true, + }; + return ( +

        + {singers.map(({ id, name }, i) => { + return ( + <> + toSingerDetailPage(id)} title={name}> + {name} + + {i !== singers.length - 1 && / } + + ); + })} +

        + ); + }; + }, }); - diff --git a/src/widgets/mv-list/index.scss b/src/widgets/mv-list/index.scss index 4b0d9a8..e2dde65 100644 --- a/src/widgets/mv-list/index.scss +++ b/src/widgets/mv-list/index.scss @@ -1,59 +1,59 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .mv-list { - min-height: 400px; - padding-top: 1em; - - .mv-item { - position: relative; - transition: transform 0.2s, filter 0.14s; - cursor: pointer; - - &:hover { - filter: drop-shadow(0 0 30px rgba(100, 100, 100, 0.3)); - transform: translateY(-6px); - img { - filter: brightness(75%); - } - } - - img { - border-radius: 8px; - } - - em.playCount { - position: absolute; - right: 1em; - top: 1em; - padding: 0.5em; - background-color: rgba(170, 180, 180, 0.36); - color: rgba(240, 240, 240); - font-size: 13px; - border-radius: 0.5em; - } - - h6 { - padding: .5em 0; - color: #555; - font-size: 14px; - text-align: justify; - line-height: 1.6; - } - - - .desc { - .publishtime, - .duration { - color: #777; - line-height: 1.6; - span { - @include flexInline(); - } - em { - opacity: .7; - } - } - } - - } + min-height: 400px; + padding-top: 1em; + + .mv-item { + position: relative; + transition: transform 0.2s, filter 0.14s; + cursor: pointer; + + &:hover { + filter: drop-shadow(0 0 30px rgba(100, 100, 100, 0.3)); + transform: translateY(-6px); + img { + filter: brightness(75%); + } + } + + img { + border-radius: 8px; + } + + em.playCount { + position: absolute; + right: 1em; + top: 1em; + padding: 0.5em; + background-color: rgba(170, 180, 180, 0.36); + color: rgba(240, 240, 240); + font-size: 13px; + border-radius: 0.5em; + } + + h6 { + padding: 0.5em 0; + color: #555; + font-size: 14px; + text-align: justify; + line-height: 1.6; + } + + .desc { + .publishtime, + .duration { + color: #777; + line-height: 1.6; + span { + @include flexInline(); + } + em { + opacity: 0.7; + } + } + } + } } diff --git a/src/widgets/mv-list/index.tsx b/src/widgets/mv-list/index.tsx index 181c371..35c169d 100644 --- a/src/widgets/mv-list/index.tsx +++ b/src/widgets/mv-list/index.tsx @@ -1,165 +1,166 @@ -import { shallowReactive, defineComponent, PropType } from "vue"; -import { - useRouter, - useRoute, - onBeforeRouteUpdate, -} from "vue-router"; +/** @format */ + +import { shallowReactive, defineComponent, PropType } from 'vue'; +import { useRouter, useRoute, onBeforeRouteUpdate } from 'vue-router'; -import { getLocaleCount, getLocaleDate, msSecondToTimeStr, padPicCrop, second2TimeStr } from "@utils/index"; -import { Mv, SearchMv } from "@/types/mv"; -import RoutePagination from "@widgets/route-pagination"; -import { PAGE_SIZE } from "@/utils/preference"; -import { NGrid, NGridItem } from "naive-ui"; -import "./index.scss"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +import { + getLocaleCount, + getLocaleDate, + msSecondToTimeStr, + padPicCrop, + second2TimeStr, +} from '@utils/index'; +import { Mv, SearchMv } from '@/types/mv'; +import RoutePagination from '@widgets/route-pagination'; +import { PAGE_SIZE } from '@/utils/preference'; +import { NGrid, NGridItem } from 'naive-ui'; +import './index.scss'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export type MvListType = Mv & SearchMv; export default defineComponent({ - name: "MvList", - props: { - mvlists: { - type: Array as PropType, - required: false, - default: () => [], - }, - defaultLimit: { - type: Number as PropType, - required: false, - default: PAGE_SIZE.DEFAULT, - }, - total: { - type: Number as PropType, - required: false, - default: 0, - }, - gaps: { - type: Object as PropType>>, - required: false, - default: () => ({ x: 30, y: 30 }), - }, - cols: { - type: Number as PropType, - required: false, - default: 5, - }, - showPagination: { - type: Boolean as PropType, - required: false, - default: false, - }, - hasMore: { - type: Boolean as PropType, - required: false, - default: false, - } - }, - setup(props, context) { - const route = useRoute() as PlainObject; - const router = useRouter(); - const mvListPagiInfo = shallowReactive({ - limit: 0, - offset: 0, - total: 0, - sizeArr: [] as number[], - }); + name: 'MvList', + props: { + mvlists: { + type: Array as PropType, + required: false, + default: () => [], + }, + defaultLimit: { + type: Number as PropType, + required: false, + default: PAGE_SIZE.DEFAULT, + }, + total: { + type: Number as PropType, + required: false, + default: 0, + }, + gaps: { + type: Object as PropType>>, + required: false, + default: () => ({ x: 30, y: 30 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 5, + }, + showPagination: { + type: Boolean as PropType, + required: false, + default: false, + }, + hasMore: { + type: Boolean as PropType, + required: false, + default: false, + }, + }, + setup(props, context) { + const route = useRoute() as PlainObject; + const router = useRouter(); + const mvListPagiInfo = shallowReactive({ + limit: 0, + offset: 0, + total: 0, + sizeArr: [] as number[], + }); - const updateTolListInfo = (query: PlainObject) => { - const { defaultLimit, total } = props; - const { limit = defaultLimit, offset = 0 } = query; - mvListPagiInfo.limit = limit; - mvListPagiInfo.offset = offset; - mvListPagiInfo.total = total; - mvListPagiInfo.sizeArr = Array(2) - .fill("") - .map((v, i) => defaultLimit * (i + 1)); - }; - updateTolListInfo(route.query); + const updateTolListInfo = (query: PlainObject) => { + const { defaultLimit, total } = props; + const { limit = defaultLimit, offset = 0 } = query; + mvListPagiInfo.limit = limit; + mvListPagiInfo.offset = offset; + mvListPagiInfo.total = total; + mvListPagiInfo.sizeArr = Array(2) + .fill('') + .map((v, i) => defaultLimit * (i + 1)); + }; + updateTolListInfo(route.query); - onFilteredBeforeRouteUpdate((to) => { - updateTolListInfo(to.query); - }); + onFilteredBeforeRouteUpdate((to) => { + updateTolListInfo(to.query); + }); - const toMvDetailPage = (id: string | number) => { - router.push({ - path: '/mv', - query: { - id, - } - }) - } + const toMvDetailPage = (id: string | number) => { + router.push({ + path: '/mv', + query: { + id, + }, + }); + }; - const renderMvLists = () => { - const { gaps: { x, y }, cols, mvlists } = props; - mvlists.forEach((item) => { - item.playCountStr = getLocaleCount(item.playCount); - }); - return ( - - { - mvlists.map(({ name, imgurl16v9, cover, duration, id, publishTime, playCountStr }) => - -
        toMvDetailPage(id)}> -
        - {name} -
        -
        {name}
        -
        -

        - 时长: - - {msSecondToTimeStr(duration)} - -

        - { - publishTime &&

        - 发布: - - {publishTime} - -

        - } -
        - {playCountStr} -
        -
        - ) - } -
        - ); - }; + const renderMvLists = () => { + const { + gaps: { x, y }, + cols, + mvlists, + } = props; + mvlists.forEach((item) => { + item.playCountStr = getLocaleCount(item.playCount); + }); + return ( + + {mvlists.map( + ({ name, imgurl16v9, cover, duration, id, publishTime, playCountStr }) => ( + +
        toMvDetailPage(id)}> +
        + {name} +
        +
        {name}
        +
        +

        + 时长: + {msSecondToTimeStr(duration)} +

        + {publishTime && ( +

        + 发布: + {publishTime} +

        + )} +
        + {playCountStr} +
        +
        + ), + )} +
        + ); + }; - return () => { - const { mvlists } = props - return ( - <> - { - mvlists.length ? - ( -
        - {renderMvLists()} - { - props.showPagination && ( -
        - -
        - ) - } -
        - ) - : ( -
        - 这哩啥也没有喔~ -
        - ) - } - - ); - }; - }, + return () => { + const { mvlists } = props; + return ( + <> + {mvlists.length ? ( +
        + {renderMvLists()} + {props.showPagination && ( +
        + +
        + )} +
        + ) : ( +
        + 这哩啥也没有喔~ +
        + )} + + ); + }; + }, }); diff --git a/src/widgets/progress-bar/index.scss b/src/widgets/progress-bar/index.scss index 1910a99..c2f05f1 100644 --- a/src/widgets/progress-bar/index.scss +++ b/src/widgets/progress-bar/index.scss @@ -1,63 +1,65 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; //进度条 -.progressbar { - cursor: pointer; - - &.progressbar-horizontal { - @include flexVc(); - width: 100%; - height: $progressBarWidthOrHeight; - .progressbar-buffer { - width: 100%; - height: 2px; - } - } - - &.progressbar-vertical { - height: 100%; - width: $progressBarWidthOrHeight; - .progressbar-buffer { - width: 4px; - height: 100%; - margin: 0 auto; - transform: scale(-1); - } - } - - .progressbar-buffer { - position: relative; - - .progressbar-current { - position: absolute; - right: 0; - top: 0; - height: 100%; - width: 100%; - } - - .progressbar-dot { - position: absolute; - width: 10px; - height: 10px; - z-index: 10; - margin-left: -5px; - margin-top: -5px; - background-color: var(--theme); - border-radius: 100%; - } - } - - &[defaultdotshow="false"]:hover .progressbar-buffer, - &[defaultdotshow="false"][showdot="true"] { - .progressbar-dot { - opacity: 1; - } - } - - &[defaultdotshow="false"][showdot="false"] { - .progressbar-dot { - opacity: 0; - } - } +.progressbar { + cursor: pointer; + + &.progressbar-horizontal { + @include flexVc(); + width: 100%; + height: $progressBarWidthOrHeight; + .progressbar-buffer { + width: 100%; + height: 2px; + } + } + + &.progressbar-vertical { + height: 100%; + width: $progressBarWidthOrHeight; + .progressbar-buffer { + width: 4px; + height: 100%; + margin: 0 auto; + transform: scale(-1); + } + } + + .progressbar-buffer { + position: relative; + + .progressbar-current { + position: absolute; + right: 0; + top: 0; + height: 100%; + width: 100%; + } + + .progressbar-dot { + position: absolute; + width: 10px; + height: 10px; + z-index: 10; + margin-left: -5px; + margin-top: -5px; + background-color: var(--theme); + border-radius: 100%; + } + } + + &[defaultdotshow='false']:hover .progressbar-buffer, + &[defaultdotshow='false'][showdot='true'] { + .progressbar-dot { + opacity: 1; + } + } + + &[defaultdotshow='false'][showdot='false'] { + .progressbar-dot { + opacity: 0; + } + } } diff --git a/src/widgets/progress-bar/index.tsx b/src/widgets/progress-bar/index.tsx index 8fa666f..3b5beba 100644 --- a/src/widgets/progress-bar/index.tsx +++ b/src/widgets/progress-bar/index.tsx @@ -1,242 +1,260 @@ +/** @format */ + import { - defineComponent, - getCurrentInstance, - onBeforeUnmount, - onMounted, - PropType, - toRefs, - watch, - useCssVars, - ref, - computed, - reactive, - shallowReactive, - DefineComponent, -} from "vue"; + defineComponent, + getCurrentInstance, + onBeforeUnmount, + onMounted, + PropType, + toRefs, + watch, + useCssVars, + ref, + computed, + reactive, + shallowReactive, + DefineComponent, +} from 'vue'; import { - computedStyle, - decimalToPercent, - getElmRectInfo, - percentToDecimal, + computedStyle, + decimalToPercent, + getElmRectInfo, + percentToDecimal, } from '@utils/index'; import './index.scss'; -import { useEventListener, useIntersectionObserver, useMutationObserver, useResizeObserver } from '@vueuse/core'; +import { + useEventListener, + useIntersectionObserver, + useMutationObserver, + useResizeObserver, +} from '@vueuse/core'; export interface StripPrjt { - long: number; - offset: number; - attrs: Partial>; -}; + long: number; + offset: number; + attrs: Partial>; +} export type ProgressInfo = { - ratio: string; - decimal: number; -} + ratio: string; + decimal: number; +}; -export declare type ProgressBarComp = DefineComponent<{ - dir: { - type: PropType; - default: string; - }, - bgc: { - type: PropType; - default: string; - }, - dotFixed: { - type: BooleanConstructor; - default: boolean; - }, - currentRatio: { - type: NumberConstructor; - default: number; - }, -}, any, any, any, any, any, any, any, any>; +export declare type ProgressBarComp = DefineComponent< + { + dir: { + type: PropType; + default: string; + }; + bgc: { + type: PropType; + default: string; + }; + dotFixed: { + type: BooleanConstructor; + default: boolean; + }; + currentRatio: { + type: NumberConstructor; + default: number; + }; + }, + any, + any, + any, + any, + any, + any, + any, + any +>; const ProgressBar: ProgressBarComp = defineComponent({ - name: "ProgressBar", - props: { - dir: { - type: String, - required: false, - default: "horizontal", - }, - bgc: { - type: String, - required: false, - default: "rgba(160, 160, 160, 0.15)", - }, - dotFixed: { - type: Boolean, - required: false, - default: false, - }, - currentRatio: { - type: Number, - required: false, - default: 0, - }, - }, - emits: ["change", "down", "move", "up"], - setup(props, { slots, emit }) { - const vm = getCurrentInstance()!; //即this组件实例 - - //是否可以移动 - const canMove = ref(false); - //当前进度条状态 - const currentProgress = shallowReactive({ - decimal: 0, //零点几 - ratio: "0%", //百分比 - }); - //进度条信息 - const progressbarInfo = reactive({ - long: 0, - offset: 0, - attrs: { - dir: "", offset: "", long: "", mouseOffset: "", - }, - }); - //是否显示控制点 - const isShowDot = ref(props.dotFixed); - //进度条元素引用 - const stripElmRef = ref() - - //进度条class - const progressbarClass = computed(() => { - return ["progressbar", `progressbar-${props.dir}`].join(" "); - }); - - //进入背景 - const progressbarBufferStyle = computed(() => { - return { - backgroundColor: props.bgc - } - }) - - //当前进度样式 - const progressbarCurrentStyle = computed(() => { - const dir = props.dir; - const ratio = currentProgress.ratio; - const toDir = dir === 'horizontal' ? 'right' : 'bottom'; - return { - backgroundImage: `linear-gradient(to ${toDir},var(--theme) ${ratio},transparent ${ratio})`, - } - }) - - //控制小圆点样式 - const progressbarDotStyle = computed(() => { - const dir = props.dir; - const ratio = currentProgress.ratio; - const styleObj = { - left: '50%', - top: '50%' - } - const willChangeAttr = dir === 'horizontal' ? 'left' : 'top'; - styleObj[willChangeAttr] = ratio; - return styleObj; - }) - - //获取偏移值 - const getTranslate = (ev: PlainObject) => { - const { offset, long, attrs } = progressbarInfo; - let tarTranslate = ev[attrs.mouseOffset!] - offset; - if (tarTranslate > long) { - tarTranslate = long; - } else if (tarTranslate < 0) { - tarTranslate = 0; - } - return props.dir === "vertical" - ? long - tarTranslate - : tarTranslate; - }; - - //更新进度条状态 - const updateCurrentProgress = (ev: PlainObject) => { - updateProgressbarInfo(); - const translate = getTranslate(ev); - const tarDemical = translate / progressbarInfo.long; - currentProgress.decimal = tarDemical; - currentProgress.ratio = decimalToPercent(tarDemical, 2); - }; - - //按下进度条时 - const down = (ev: MouseEvent) => { - canMove.value = true; - !props.dotFixed && (isShowDot.value = true); - updateCurrentProgress(ev); - emit("down", currentProgress); - emit("change", currentProgress); - }; - - //移动进度条时 - const move = (ev: MouseEvent) => { - if (!canMove.value) return; - updateCurrentProgress(ev); - emit("move", currentProgress); - emit("change", currentProgress); - }; - - //松开进度条时 - const up = (ev: MouseEvent) => { - if (canMove.value) { - emit("up", currentProgress); - } - canMove.value = false; - !props.dotFixed && (isShowDot.value = false); - }; - - useEventListener(document, "mousemove", move) - useEventListener(document, "mouseup", up) - - watch( - () => props.currentRatio, - (val, oldVal) => { - //如果在移动操作中,就不允许外界改变 - if (canMove.value) { - return; - } - currentProgress.decimal = percentToDecimal(val); - currentProgress.ratio = `${val}%`; - }, - { - immediate: true - } - ); - - const stripAttrs: PlainObject[] = [ - { dir: "vertical", offset: "top", long: "height", mouseOffset: "pageY", }, - { dir: "horizontal", offset: "left", long: "width", mouseOffset: "pageX", }, - ]; - - /** - * 更新progressbar信息 - */ - const updateProgressbarInfo = () => { - const { dir } = props; - const stripElmRect = getElmRectInfo(stripElmRef.value!); - const tarAttrObj = stripAttrs.find(({ dir: tarDir }) => tarDir === dir)!; - progressbarInfo.long = Math.round(stripElmRect[tarAttrObj.long]); - progressbarInfo.offset = Math.round(stripElmRect[tarAttrObj.offset]); - progressbarInfo.attrs = tarAttrObj; - }; - - return () => { - return ( -
        -
        -
        -
        -
        -
        - ) - } - } -}) - -export default ProgressBar \ No newline at end of file + name: 'ProgressBar', + props: { + dir: { + type: String, + required: false, + default: 'horizontal', + }, + bgc: { + type: String, + required: false, + default: 'rgba(160, 160, 160, 0.15)', + }, + dotFixed: { + type: Boolean, + required: false, + default: false, + }, + currentRatio: { + type: Number, + required: false, + default: 0, + }, + }, + emits: ['change', 'down', 'move', 'up'], + setup(props, { slots, emit }) { + const vm = getCurrentInstance()!; //即this组件实例 + + //是否可以移动 + const canMove = ref(false); + //当前进度条状态 + const currentProgress = shallowReactive({ + decimal: 0, //零点几 + ratio: '0%', //百分比 + }); + //进度条信息 + const progressbarInfo = reactive({ + long: 0, + offset: 0, + attrs: { + dir: '', + offset: '', + long: '', + mouseOffset: '', + }, + }); + //是否显示控制点 + const isShowDot = ref(props.dotFixed); + //进度条元素引用 + const stripElmRef = ref(); + + //进度条class + const progressbarClass = computed(() => { + return ['progressbar', `progressbar-${props.dir}`].join(' '); + }); + + //进入背景 + const progressbarBufferStyle = computed(() => { + return { + backgroundColor: props.bgc, + }; + }); + + //当前进度样式 + const progressbarCurrentStyle = computed(() => { + const dir = props.dir; + const ratio = currentProgress.ratio; + const toDir = dir === 'horizontal' ? 'right' : 'bottom'; + return { + backgroundImage: `linear-gradient(to ${toDir},var(--theme) ${ratio},transparent ${ratio})`, + }; + }); + + //控制小圆点样式 + const progressbarDotStyle = computed(() => { + const dir = props.dir; + const ratio = currentProgress.ratio; + const styleObj = { + left: '50%', + top: '50%', + }; + const willChangeAttr = dir === 'horizontal' ? 'left' : 'top'; + styleObj[willChangeAttr] = ratio; + return styleObj; + }); + + //获取偏移值 + const getTranslate = (ev: PlainObject) => { + const { offset, long, attrs } = progressbarInfo; + let tarTranslate = ev[attrs.mouseOffset!] - offset; + if (tarTranslate > long) { + tarTranslate = long; + } else if (tarTranslate < 0) { + tarTranslate = 0; + } + return props.dir === 'vertical' ? long - tarTranslate : tarTranslate; + }; + + //更新进度条状态 + const updateCurrentProgress = (ev: PlainObject) => { + updateProgressbarInfo(); + const translate = getTranslate(ev); + const tarDemical = translate / progressbarInfo.long; + currentProgress.decimal = tarDemical; + currentProgress.ratio = decimalToPercent(tarDemical, 2); + }; + + //按下进度条时 + const down = (ev: MouseEvent) => { + canMove.value = true; + !props.dotFixed && (isShowDot.value = true); + updateCurrentProgress(ev); + emit('down', currentProgress); + emit('change', currentProgress); + }; + + //移动进度条时 + const move = (ev: MouseEvent) => { + if (!canMove.value) return; + updateCurrentProgress(ev); + emit('move', currentProgress); + emit('change', currentProgress); + }; + + //松开进度条时 + const up = (ev: MouseEvent) => { + if (canMove.value) { + emit('up', currentProgress); + } + canMove.value = false; + !props.dotFixed && (isShowDot.value = false); + }; + + useEventListener(document, 'mousemove', move); + useEventListener(document, 'mouseup', up); + + watch( + () => props.currentRatio, + (val, oldVal) => { + //如果在移动操作中,就不允许外界改变 + if (canMove.value) { + return; + } + currentProgress.decimal = percentToDecimal(val); + currentProgress.ratio = `${val}%`; + }, + { + immediate: true, + }, + ); + + const stripAttrs: PlainObject[] = [ + { dir: 'vertical', offset: 'top', long: 'height', mouseOffset: 'pageY' }, + { dir: 'horizontal', offset: 'left', long: 'width', mouseOffset: 'pageX' }, + ]; + + /** + * 更新progressbar信息 + */ + const updateProgressbarInfo = () => { + const { dir } = props; + const stripElmRect = getElmRectInfo(stripElmRef.value!); + const tarAttrObj = stripAttrs.find(({ dir: tarDir }) => tarDir === dir)!; + progressbarInfo.long = Math.round(stripElmRect[tarAttrObj.long]); + progressbarInfo.offset = Math.round(stripElmRect[tarAttrObj.offset]); + progressbarInfo.attrs = tarAttrObj; + }; + + return () => { + return ( +
        +
        +
        +
        +
        +
        + ); + }; + }, +}); + +export default ProgressBar; diff --git a/src/widgets/reply-textarea/index.scss b/src/widgets/reply-textarea/index.scss index 341618f..60fe86b 100644 --- a/src/widgets/reply-textarea/index.scss +++ b/src/widgets/reply-textarea/index.scss @@ -1,13 +1,14 @@ -@import "@scss/variable"; +/** @format */ -.reply-container { - padding: 14px 20px 0 20px; - background-color: $replyTextareaBgc; +@import '@scss/variable'; - .reply-operation { - height: 38px; - background-color: inherit; - cursor: text; - } +.reply-container { + padding: 14px 20px 0 20px; + background-color: $replyTextareaBgc; -} \ No newline at end of file + .reply-operation { + height: 38px; + background-color: inherit; + cursor: text; + } +} diff --git a/src/widgets/reply-textarea/index.tsx b/src/widgets/reply-textarea/index.tsx index 38607c7..3720b0c 100644 --- a/src/widgets/reply-textarea/index.tsx +++ b/src/widgets/reply-textarea/index.tsx @@ -1,43 +1,46 @@ -import { UNICODE_CHAR } from "@/utils"; -import { computed, defineComponent, ref, shallowReactive } from "vue"; +/** @format */ + +import { UNICODE_CHAR } from '@/utils'; +import { computed, defineComponent, ref, shallowReactive } from 'vue'; import './index.scss'; -/** +/** * 评论等内容回复的公共组件 */ export default defineComponent({ - name: "ReplyTextarea", - setup() { - - const isFocus = ref(false); - const focusHandler = () => isFocus.value = true; - const blurHandler = () => isFocus.value = false; - - const excludeClasses: string[] = []; + name: 'ReplyTextarea', + setup() { + const isFocus = ref(false); + const focusHandler = () => (isFocus.value = true); + const blurHandler = () => (isFocus.value = false); - const pointerDownHandler = ({ target }: PointerEvent) => { - if (excludeClasses.some(className => (target as HTMLElement).closest(`.${className}`))) { - return; - } - focusHandler(); - } + const excludeClasses: string[] = []; - return () => { - return ( -
        -
        - -
        -
        + const pointerDownHandler = ({ target }: PointerEvent) => { + if ( + excludeClasses.some((className) => + (target as HTMLElement).closest(`.${className}`), + ) + ) { + return; + } + focusHandler(); + }; -
        -
        - ) - } - } -}) \ No newline at end of file + return () => { + return ( +
        +
        + +
        +
        +
        + ); + }; + }, +}); diff --git a/src/widgets/route-pagination/index.scss b/src/widgets/route-pagination/index.scss index 7a310f1..8de3da0 100644 --- a/src/widgets/route-pagination/index.scss +++ b/src/widgets/route-pagination/index.scss @@ -1,12 +1,12 @@ +/** @format */ + @import '@scss/variable'; .naive-pagination { - @include flexHc(); - padding: 30px 0; - - .no-total { - @include flexHcVc(); - - } + @include flexHc(); + padding: 30px 0; -} \ No newline at end of file + .no-total { + @include flexHcVc(); + } +} diff --git a/src/widgets/route-pagination/index.tsx b/src/widgets/route-pagination/index.tsx index 996cd5e..21658fb 100644 --- a/src/widgets/route-pagination/index.tsx +++ b/src/widgets/route-pagination/index.tsx @@ -1,115 +1,131 @@ -import { NDropdown, NGradientText, NIcon, NInputNumber, NPagination, NSpace, NxButton } from "naive-ui"; -import { defineComponent, Ref, PropType } from "vue"; -import { useRouter, useRoute } from "vue-router"; -import "./index.scss"; +/** @format */ + +import { + NDropdown, + NGradientText, + NIcon, + NInputNumber, + NPagination, + NSpace, + NxButton, +} from 'naive-ui'; +import { defineComponent, Ref, PropType } from 'vue'; +import { useRouter, useRoute } from 'vue-router'; +import './index.scss'; export type PagiInfo = { - offset: number | string; - sizeArr: number[]; - limit: number | string; - total?: number | string; + offset: number | string; + sizeArr: number[]; + limit: number | string; + total?: number | string; }; export default defineComponent({ - name: "RoutePagination", - props: { - pagiInfo: { - type: Object as PropType, - required: true, - }, - hasMore: { - type: Boolean as PropType, - required: false, - default: true, - } - }, - setup(props, { slots, emit }) { - - const router = useRouter(); - const route = useRoute(); - - const handleSizeChange = (curSize: number) => { - router.push({ - query: { - ...route.query, - limit: curSize, - }, - }); - } - - const handleCurrentChange = (curPage: number) => { - router.push({ - query: { - ...route.query, - offset: curPage - 1, - }, - }); - }; + name: 'RoutePagination', + props: { + pagiInfo: { + type: Object as PropType, + required: true, + }, + hasMore: { + type: Boolean as PropType, + required: false, + default: true, + }, + }, + setup(props, { slots, emit }) { + const router = useRouter(); + const route = useRoute(); - return () => { - const { hasMore, pagiInfo } = props - const { offset, total, limit, sizeArr } = pagiInfo; - return ( -
        - { - total ? ( - - { - { - prefix({ itemCount }: PlainObject) { - return 共{itemCount}项 - } - } - } - - ) - : ( -
        - - handleCurrentChange(+offset)}>上一页 - 第 {+offset + 1} 页 - handleCurrentChange(+offset + 2)}>下一页 - ({ key: s, label: `${s}条/页` }))}> - - {{ - default: () => limit, - icon: () => ( - - ), - }} - - - { - curOffset! > 0 && handleCurrentChange(curOffset!) - }} - > - + const handleSizeChange = (curSize: number) => { + router.push({ + query: { + ...route.query, + limit: curSize, + }, + }); + }; -
        - ) - } + const handleCurrentChange = (curPage: number) => { + router.push({ + query: { + ...route.query, + offset: curPage - 1, + }, + }); + }; -
        - ) - }; - }, + return () => { + const { hasMore, pagiInfo } = props; + const { offset, total, limit, sizeArr } = pagiInfo; + return ( +
        + {total ? ( + + {{ + prefix({ itemCount }: PlainObject) { + return 共{itemCount}项; + }, + }} + + ) : ( +
        + + handleCurrentChange(+offset)} + > + 上一页 + + 第 {+offset + 1} 页 + handleCurrentChange(+offset + 2)} + > + 下一页 + + ({ key: s, label: `${s}条/页` }))} + > + + {{ + default: () => limit, + icon: () => , + }} + + + { + curOffset! > 0 && handleCurrentChange(curOffset!); + }} + > + +
        + )} +
        + ); + }; + }, }); diff --git a/src/widgets/song-list/index.scss b/src/widgets/song-list/index.scss index 1718a08..ed1cf88 100644 --- a/src/widgets/song-list/index.scss +++ b/src/widgets/song-list/index.scss @@ -1,83 +1,79 @@ -@import "@/scss/variable"; +/** @format */ -.song-list { - .music-wrap { - min-height: 100px; - padding: 20px; +@import '@/scss/variable'; - .music-item { - .music-cover { - transition: transform 0.2s ease-in-out, filter 0.18s, - backdrop-filter 0.18s; - cursor: pointer; - &:hover { - transform: translateY(-8px); - filter: drop-shadow(0 0 28px rgba(38, 38, 38, 0.3)); - backdrop-filter: blur(10px); - } - img { - border-radius: 6px; - } - } +.song-list { + .music-wrap { + min-height: 100px; + padding: 20px; - .music-body { - h6 { - color: #555; - font-size: 15px; - font-weight: bold; - line-height: 3; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } + .music-item { + .music-cover { + transition: transform 0.2s ease-in-out, filter 0.18s, backdrop-filter 0.18s; + cursor: pointer; + &:hover { + transform: translateY(-8px); + filter: drop-shadow(0 0 28px rgba(38, 38, 38, 0.3)); + backdrop-filter: blur(10px); + } + img { + border-radius: 6px; + } + } - .music-creator { - @include flexVc(); - font-size: 14px; + .music-body { + h6 { + color: #555; + font-size: 15px; + font-weight: bold; + line-height: 3; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } - .creator-avatar-wrap { - width: 20%; - max-width: 38px; - border-radius: 100%; - overflow: hidden; + .music-creator { + @include flexVc(); + font-size: 14px; - img { - &:hover { - transform: scale(1.2); - } - transition: transform .2s; - cursor: pointer; - } - } + .creator-avatar-wrap { + width: 20%; + max-width: 38px; + border-radius: 100%; + overflow: hidden; - .creator-title { - color: #555; - } + img { + &:hover { + transform: scale(1.2); + } + transition: transform 0.2s; + cursor: pointer; + } + } - .creator-nickname { - flex: 1; - text-align: right; - span { - color: #777; - transition: color .14s; - cursor: pointer; - &:hover{ - color: var(--theme) - } - } - - } + .creator-title { + color: #555; + } - } + .creator-nickname { + flex: 1; + text-align: right; + span { + color: #777; + transition: color 0.14s; + cursor: pointer; + &:hover { + color: var(--theme); + } + } + } + } + } + } - } - - } - - .songlist-empty { - @include flexHcVc(); - min-height: inherit; - } - - } + .songlist-empty { + @include flexHcVc(); + min-height: inherit; + } + } } diff --git a/src/widgets/song-list/index.tsx b/src/widgets/song-list/index.tsx index dac744a..bb11879 100644 --- a/src/widgets/song-list/index.tsx +++ b/src/widgets/song-list/index.tsx @@ -1,220 +1,219 @@ +/** @format */ + import { - watch, - ref, - shallowReactive, - shallowRef, - computed, - defineComponent, - PropType, -} from "vue"; + watch, + ref, + shallowReactive, + shallowRef, + computed, + defineComponent, + PropType, +} from 'vue'; import { - useRouter, - useRoute, - onBeforeRouteLeave, - onBeforeRouteUpdate, - RouterLink, -} from "vue-router"; -import RoutePagination from "@/widgets/route-pagination"; -import "./index.scss"; -import { NEmpty, NGrid, NGridItem } from "naive-ui"; -import { UNICODE_CHAR, padPicCrop } from "@utils/index"; -import { getFullName, getFullNames } from "@/utils/apiSpecial"; -import { PAGE_SIZE } from "@/utils/preference"; -import { PlaylistCommon } from "@/types/songlist"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; + useRouter, + useRoute, + onBeforeRouteLeave, + onBeforeRouteUpdate, + RouterLink, +} from 'vue-router'; +import RoutePagination from '@/widgets/route-pagination'; +import './index.scss'; +import { NEmpty, NGrid, NGridItem } from 'naive-ui'; +import { UNICODE_CHAR, padPicCrop } from '@utils/index'; +import { getFullName, getFullNames } from '@/utils/apiSpecial'; +import { PAGE_SIZE } from '@/utils/preference'; +import { PlaylistCommon } from '@/types/songlist'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; import YuanInfinityScroll from '@widgets/infinity-scroll/infinity-scroll'; - -export default defineComponent({ - name: "Songlist", - props: { - playlists: { - type: Array as PropType, - required: false, - default: () => [], - }, - defaultLimit: { - type: Number as PropType, - required: false, - default: PAGE_SIZE.DEFAULT, - }, - total: { - type: Number as PropType, - required: false, - default: 0, - }, - gaps: { - type: Object as PropType>>, - required: false, - default: () => ({ x: 30, y: 30 }), - }, - cols: { - type: Number as PropType, - required: false, - default: 7 - }, - hasMore: { - type: Boolean as PropType, - required: false, - default: true - }, - showPagination: { - type: Boolean, - required: false, - default: true - }, - needInfinityScroll: { - type: Boolean, - required: false, - default: false, - } - }, - setup(props, context) { - const route = useRoute(); - const router = useRouter(); - const topListInfo = shallowReactive({ - limit: 0, - offset: 0, - total: 0, - sizeArr: [] as number[], - }); - - const updateTolListInfo = (query: PlainObject) => { - const { defaultLimit, total } = props; - const { limit = defaultLimit, offset = 0 } = query; - topListInfo.limit = limit; - topListInfo.offset = offset; - topListInfo.total = total; - topListInfo.sizeArr = Array(2) - .fill("") - .map((v, i) => defaultLimit * (i + 1)); - }; - updateTolListInfo(route.query as PlainObject); +export default defineComponent({ + name: 'Songlist', + props: { + playlists: { + type: Array as PropType, + required: false, + default: () => [], + }, + defaultLimit: { + type: Number as PropType, + required: false, + default: PAGE_SIZE.DEFAULT, + }, + total: { + type: Number as PropType, + required: false, + default: 0, + }, + gaps: { + type: Object as PropType>>, + required: false, + default: () => ({ x: 30, y: 30 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 7, + }, + hasMore: { + type: Boolean as PropType, + required: false, + default: true, + }, + showPagination: { + type: Boolean, + required: false, + default: true, + }, + needInfinityScroll: { + type: Boolean, + required: false, + default: false, + }, + }, + setup(props, context) { + const route = useRoute(); + const router = useRouter(); - onFilteredBeforeRouteUpdate((to, from) => { - updateTolListInfo(to.query as PlainObject); - }); + const topListInfo = shallowReactive({ + limit: 0, + offset: 0, + total: 0, + sizeArr: [] as number[], + }); - const toSonglistDetailPage = (id: string | number) => - router.push({ - path: "/songlist/:id", - name: 'songlist', - params: { id } - }); + const updateTolListInfo = (query: PlainObject) => { + const { defaultLimit, total } = props; + const { limit = defaultLimit, offset = 0 } = query; + topListInfo.limit = limit; + topListInfo.offset = offset; + topListInfo.total = total; + topListInfo.sizeArr = Array(2) + .fill('') + .map((v, i) => defaultLimit * (i + 1)); + }; + updateTolListInfo(route.query as PlainObject); - const toUserDetailPage = (userId: string | number) => - router.push({ - path: '/user', - query: { - id: userId - } - }) + onFilteredBeforeRouteUpdate((to, from) => { + updateTolListInfo(to.query as PlainObject); + }); - const listRender = (currentCount: number = props.playlists.length) => { - const { playlists, gaps, cols } = props; - return ( - - { - playlists - .slice(0, currentCount) - .map(({ name, id, coverImgUrl, picUrl, description, creator }) => { - const { userId, nickname, avatarUrl } = creator; - return ( - -
        -
        toSonglistDetailPage(id)}> - {`${name}-${description}`} -
        -
        -
        {name}
        -
        - <> - { - avatarUrl ? ( -
        -
        toUserDetailPage(userId)}> - -
        -
        - ) : ( - 创建者: - ) - } - - toUserDetailPage(userId)}> - {nickname} - - - -
        -
        -
        -
        - ) - }) - } -
        - ) - } + const toSonglistDetailPage = (id: string | number) => + router.push({ + path: '/songlist/:id', + name: 'songlist', + params: { id }, + }); - const renderList = () => { - if(props.needInfinityScroll) { - return ( - - { - { - default: listRender - } - } - - ) - } - return listRender() - } + const toUserDetailPage = (userId: string | number) => + router.push({ + path: '/user', + query: { + id: userId, + }, + }); - const renderMainList = () => { - const { playlists } = props; - return ( -
        - { - playlists.length - ? renderList() - : ( - - ) - } -
        - ); - }; + const listRender = (currentCount: number = props.playlists.length) => { + const { playlists, gaps, cols } = props; + return ( + + {playlists + .slice(0, currentCount) + .map(({ name, id, coverImgUrl, picUrl, description, creator }) => { + const { userId, nickname, avatarUrl } = creator; + return ( + +
        +
        toSonglistDetailPage(id)} + > + {`${name}-${description}`} +
        +
        +
        {name}
        +
        + <> + {avatarUrl ? ( +
        +
        toUserDetailPage(userId)} + > + +
        +
        + ) : ( + 创建者: + )} + + toUserDetailPage(userId)}> + {nickname} + + + +
        +
        +
        +
        + ); + })} +
        + ); + }; - return () => { + const renderList = () => { + if (props.needInfinityScroll) { + return ( + + {{ + default: listRender, + }} + + ); + } + return listRender(); + }; - return ( -
        - {renderMainList()} + const renderMainList = () => { + const { playlists } = props; + return ( +
        + {playlists.length ? ( + renderList() + ) : ( + + )} +
        + ); + }; - { - props.showPagination && ( -
        - -
        - ) - } + return () => { + return ( +
        + {renderMainList()} -
        - ); - }; - }, + {props.showPagination && ( +
        + +
        + )} +
        + ); + }; + }, }); diff --git a/src/widgets/song-table/index.scss b/src/widgets/song-table/index.scss index 7677efe..46b2e1a 100644 --- a/src/widgets/song-table/index.scss +++ b/src/widgets/song-table/index.scss @@ -1,75 +1,74 @@ +/** @format */ -@import "@/scss/variable"; +@import '@/scss/variable'; $baseSongTableHeight: 40em; .song-table { - position: relative; - min-height: $baseSongTableHeight; - &-loadingIcon { - position: absolute; - top: 0; - left: 0; - right: 0; - height: $baseSongTableHeight; - } + position: relative; + min-height: $baseSongTableHeight; + &-loadingIcon { + position: absolute; + top: 0; + left: 0; + right: 0; + height: $baseSongTableHeight; + } } .song-table { - .song-list-item { - transition: background-color 0.14s; - &:hover { - background-color: rgba(165,180,150, .12); - .song-list-cell { - background-color: transparent; - } + .song-list-item { + transition: background-color 0.14s; + &:hover { + background-color: rgba(165, 180, 150, 0.12); + .song-list-cell { + background-color: transparent; + } - .tools { - opacity: 1; - } - } - } + .tools { + opacity: 1; + } + } + } - .tools { - @include flexVc(); - opacity: 0; - color: #888; - .tool-item { - margin: 0 6px; - font-size: 28px; - cursor: pointer; + .tools { + @include flexVc(); + opacity: 0; + color: #888; + .tool-item { + margin: 0 6px; + font-size: 28px; + cursor: pointer; - &:hover { - color: var(--theme); - } + &:hover { + color: var(--theme); + } + } + } - } - } + .song-publish-time, + .song-name, + .song-duration, + .song-album { + color: #555; + font-size: 14px; + } - .song-publish-time, - .song-name, - .song-duration, - .song-album { - color: #555; - font-size: 14px; - } + .song-body-left { + @include flexVc(); + } - .song-body-left { - @include flexVc(); - } + .song-name { + padding-left: 1em; + padding-right: 4em; + } - .song-name { - padding-left: 1em; - padding-right: 4em; - } - - .song-love { - @include flexVc(); - margin-right: 8px; - font-size: 20px; - font-weight: 300; - color: #777; - cursor: pointer; - } - -} \ No newline at end of file + .song-love { + @include flexVc(); + margin-right: 8px; + font-size: 20px; + font-weight: 300; + color: #777; + cursor: pointer; + } +} diff --git a/src/widgets/song-table/index.tsx b/src/widgets/song-table/index.tsx index 82ce996..b177d9a 100644 --- a/src/widgets/song-table/index.tsx +++ b/src/widgets/song-table/index.tsx @@ -1,176 +1,161 @@ -import { ref, computed, defineComponent, PropType } from "vue"; -import { SongInfo } from "@/types/song"; -import { - freeze, -} from "@utils/index"; -import "./index.scss"; -import { - CurrentSongInfo, - getModifiedSongInfo, -} from "@/utils/apiSpecial"; -import usePlayerStore, { currentSongRefGlobal } from "@/stores/player"; -import YuanTable, { YuanTableColumn } from "./yuan-table"; -import { NGrid, NGridItem } from "naive-ui"; -import { MusicLoveIcon, PlayStatusSwitch } from "../music-tiny-comp"; -import { playingRefGlobal } from "@/stores/audio"; -import { useRoute } from "vue-router"; +/** @format */ + +import { ref, computed, defineComponent, PropType } from 'vue'; +import { SongInfo } from '@/types/song'; +import { freeze } from '@utils/index'; +import './index.scss'; +import { CurrentSongInfo, getModifiedSongInfo } from '@/utils/apiSpecial'; +import usePlayerStore, { currentSongRefGlobal } from '@/stores/player'; +import YuanTable, { YuanTableColumn } from './yuan-table'; +import { NGrid, NGridItem } from 'naive-ui'; +import { MusicLoveIcon, PlayStatusSwitch } from '../music-tiny-comp'; +import { playingRefGlobal } from '@/stores/audio'; +import { useRoute } from 'vue-router'; export const getRealTabelSerial = (index: number) => { - const { offset = 0, limit = 30 } = useRoute().query; - const accumulate = Number(offset) * Number(limit); - return ++index + accumulate; -} + const { offset = 0, limit = 30 } = useRoute().query; + const accumulate = Number(offset) * Number(limit); + return ++index + accumulate; +}; export default defineComponent({ - name: "SongTable", - props: { - showIndex: { - type: Boolean, - required: false, - }, - dataList: { - type: Array as PropType, - required: true, - }, - }, - setup(props, ctx) { - const route = useRoute(); - const songData = computed(() => { - const dataList = freeze([...props.dataList] || []); - return dataList.map((songInfo) => { - const realSongInfo = getModifiedSongInfo(songInfo); - return realSongInfo; - }); - }); - const playerStore = usePlayerStore(); - - const isRenderPublishTime = computed(() => { - return props.dataList.some(({ publishTime }) => publishTime != null); - }); - - const handleDownload = (songItem: CurrentSongInfo) => { - console.info(songItem); - }; + name: 'SongTable', + props: { + showIndex: { + type: Boolean, + required: false, + }, + dataList: { + type: Array as PropType, + required: true, + }, + }, + setup(props, ctx) { + const route = useRoute(); + const songData = computed(() => { + const dataList = freeze([...props.dataList] || []); + return dataList.map((songInfo) => { + const realSongInfo = getModifiedSongInfo(songInfo); + return realSongInfo; + }); + }); + const playerStore = usePlayerStore(); - return () => { - if (!songData.value.length) return; - const playing = playingRefGlobal.value; - - return ( -
        + const isRenderPublishTime = computed(() => { + return props.dataList.some(({ publishTime }) => publishTime != null); + }); - + const handleDownload = (songItem: CurrentSongInfo) => { + console.info(songItem); + }; - - - -
        - {musicName} -
        -
        + return () => { + if (!songData.value.length) return; + const playing = playingRefGlobal.value; - -
        -
        - -
        -
        - -
        -
        handleDownload(curSongInfo)} - > - -
        -
        -
        - - ) - } - }} - >
        + return ( +
        + + + + +
        + {musicName} +
        +
        - - {name} - - ); - }, - }} - > + +
        +
        + +
        +
        + +
        +
        handleDownload(curSongInfo)} + > + +
        +
        +
        + + ); + }, + }} + >
        - - {localedDuration} - - ); - }, - }} - > + + {name} + + ); + }, + }} + > - { - isRenderPublishTime.value && ( - - {localedPublishTime} - - ); - }, - } - } - > - ) - } -
        + + {localedDuration} + + ); + }, + }} + > -
        - ); - }; - }, + {isRenderPublishTime.value && ( + + {localedPublishTime} + + ); + }, + }} + > + )} +
        +
        + ); + }; + }, }); diff --git a/src/widgets/song-table/yuan-table/index.scss b/src/widgets/song-table/yuan-table/index.scss index 56a17b8..b1e6b93 100644 --- a/src/widgets/song-table/yuan-table/index.scss +++ b/src/widgets/song-table/yuan-table/index.scss @@ -1,33 +1,29 @@ +/** @format */ + @import '@scss/variable'; .yuan-table { - - &-header { - - } - - &-layer { - @include flex(); - transition: background-color .1s; - - .grid-item { - @include flexVc(); - $gridMinWidth: calc(2em + 14 * 2px); - min-width: $gridMinWidth; - padding: 14px 10px; - white-space: nowrap; - } - - &.header-layer { - color: #555; - } - - &.body-layer { - color: #677; - - } - } - - - -} \ No newline at end of file + &-header { + } + + &-layer { + @include flex(); + transition: background-color 0.1s; + + .grid-item { + @include flexVc(); + $gridMinWidth: calc(2em + 14 * 2px); + min-width: $gridMinWidth; + padding: 14px 10px; + white-space: nowrap; + } + + &.header-layer { + color: #555; + } + + &.body-layer { + color: #677; + } + } +} diff --git a/src/widgets/song-table/yuan-table/index.tsx b/src/widgets/song-table/yuan-table/index.tsx index b5e31e6..56645f8 100644 --- a/src/widgets/song-table/yuan-table/index.tsx +++ b/src/widgets/song-table/yuan-table/index.tsx @@ -1,153 +1,140 @@ -import { ref, computed, defineComponent, PropType, isVNode, getCurrentInstance, provide, inject, ComponentPublicInstance } from "vue"; -import { - EMPTY_ARR, - is, -} from "@utils/index"; -import { NGrid, NGridItem } from "naive-ui"; -import "./index.scss"; -import { useParent } from "@/hooks/useParent"; -import { useChildren } from "@/hooks/useChildren"; +/** @format */ -export const YUAN_TABLE_KEY = Symbol('YuanTable'); - - -export default defineComponent({ - name: "YuanTable", - props: { - class: { - type: String as PropType, - require: false, - default: '', - }, - data: { - type: Array as PropType>, - requried: false, - default: () => EMPTY_ARR, - }, - showSerial: { - type: Boolean as PropType, - required: false, - default: false, - }, - serialDefiner: { - type: Function as PropType<(serial: number) => number>, - required: false, - default: (serial: number) => serial, - }, - rowClass: { - type: String as PropType, - required: false, - default: '', - } - }, - setup(props, { slots, emit }) { +import { defineComponent, PropType, ComponentPublicInstance } from 'vue'; +import { EMPTY_ARR, is } from '@utils/index'; +import { NGrid, NGridItem } from 'naive-ui'; +import './index.scss'; +import { useParent } from '@/hooks/useParent'; +import { useChildren } from '@/hooks/useChildren'; - const { linkChildren } = useChildren(YUAN_TABLE_KEY) +export const YUAN_TABLE_KEY = Symbol('YuanTable'); - linkChildren({ props }) +type ParentExtraData = { + props: Parameters>[0]; +}; - return () => { - const { data } = props; - if (is.emptyArray(data)) { - return - } - const { class: className, showSerial, rowClass, serialDefiner } = props; - const slotArr = slots.default!().filter(({ type }) => is.object(type) && Reflect.get(type, 'name')); - const totalSpan = slotArr.map(slot => slot!.props?.span || 1).reduce((a, b) => a + b); +const defaultComp = defineComponent({ + name: 'YuanTable', + props: { + class: { + type: String as PropType, + require: false, + default: '', + }, + data: { + type: Array as PropType>, + requried: false, + default: () => EMPTY_ARR, + }, + showSerial: { + type: Boolean as PropType, + required: false, + default: false, + }, + serialDefiner: { + type: Function as PropType<(serial: number) => number>, + required: false, + default: (serial: number) => serial, + }, + rowClass: { + type: String as PropType, + required: false, + default: '', + }, + }, + setup(props, { slots, emit }) { + const { linkChildren } = useChildren( + YUAN_TABLE_KEY, + ); - return ( -
        -
        -
        - { - showSerial &&
        序号
        - } - - { - slotArr.map((slot, i) => { - const props = slot!.props!; - let { span } = props; - const { label } = props; - !Number.isFinite(+span) && (span = 1); - return ( - - {is.function(label) ? label() : label} - - ) - }) - } - -
        -
        -
        - { - data.map((item, index) => { - return ( -
        - { - showSerial &&
        {serialDefiner(index)}
        - } - - { - slotArr.map(slot => {slot}) - } - -
        - ) - }) - } -
        + linkChildren({ props }); -
        + return () => { + const { data } = props; + if (is.emptyArray(data)) { + return; + } + const { class: className, showSerial, rowClass, serialDefiner } = props; + const slotArr = slots.default!().filter( + ({ type }) => is.object(type) && Reflect.get(type, 'name'), + ); + const totalSpan = slotArr + .map((slot) => slot!.props?.span || 1) + .reduce((a, b) => a + b); - ) - } - } + return ( +
        +
        +
        + {showSerial &&
        序号
        } + + {slotArr.map((slot, i) => { + const props = slot!.props!; + let { span } = props; + const { label } = props; + !Number.isFinite(+span) && (span = 1); + return ( + + {is.function(label) ? label() : label} + + ); + })} + +
        +
        +
        + {data.map((item, index) => { + return ( +
        + {showSerial &&
        {serialDefiner(index)}
        } + + {slotArr.map((slot) => ( + + {slot} + + ))} + +
        + ); + })} +
        +
        + ); + }; + }, }); +export default defaultComp; export const YuanTableColumn = defineComponent({ - name: "YuanTableColumn", - props: { - label: { - type: [String, Function] as PropType any)>, - requried: false, - default: '', - }, - span: { - type: Number as PropType, - required: false, - default: 1 - } - }, - setup(props, { slots, emit }) { - const { parent, index } = useParent(YUAN_TABLE_KEY); - - if (!parent) { - if (process.env.NODE_ENV !== 'production') { - console.error(` must be a child component of `) - } - return; - } + name: 'YuanTableColumn', + props: { + label: { + type: [String, Function] as PropType any)>, + requried: false, + default: '', + }, + span: { + type: Number as PropType, + required: false, + default: 1, + }, + }, + setup(props, { slots, emit }) { + const { parent, index } = useParent(YUAN_TABLE_KEY); - return () => { - const data = parent.props.data; - const dataIndex = data.length && Math.floor(index.value % ((data.length - 1) || 1)); + if (!parent) { + if (process.env.NODE_ENV !== 'production') { + console.error(` must be a child component of `); + } + return; + } - return ( -
        - { - slots.default!(data[dataIndex]) - } -
        - ) + return () => { + const data = parent.props.data; + const dataIndex = data.length && Math.floor(index.value % (data.length - 1 || 1)); - } - } -}) \ No newline at end of file + return
        {slots.default!(data[dataIndex])}
        ; + }; + }, +}); diff --git a/src/widgets/subscriber-list/index.scss b/src/widgets/subscriber-list/index.scss index 1747fd7..b900ebd 100644 --- a/src/widgets/subscriber-list/index.scss +++ b/src/widgets/subscriber-list/index.scss @@ -1,18 +1,20 @@ -@import "@/scss/variable"; +/** @format */ + +@import '@/scss/variable'; .subscriber-list { - .user-layer { - .user-item { - cursor: pointer; + .user-layer { + .user-item { + cursor: pointer; - img { - border-radius: 100%; - } + img { + border-radius: 100%; + } - .user-name { - text-align: center; - line-height: 2.8; - } - } - } + .user-name { + text-align: center; + line-height: 2.8; + } + } + } } diff --git a/src/widgets/subscriber-list/index.tsx b/src/widgets/subscriber-list/index.tsx index 9205d60..b8563d9 100644 --- a/src/widgets/subscriber-list/index.tsx +++ b/src/widgets/subscriber-list/index.tsx @@ -1,114 +1,118 @@ -import { watch, shallowReactive, toRefs, computed, PropType, defineComponent } from "vue"; -import { useRouter, useRoute, RouteLocationNormalized } from "vue-router"; -import { Subscriber } from "@/types/songlist"; -import "./index.scss"; -import { is, padPicCrop } from "@/utils"; -import { NEmpty, NGrid, NGridItem } from "naive-ui"; -import RoutePagination, { PagiInfo } from "../route-pagination"; -import { PAGE_SIZE } from "@/utils/preference"; -import { onFilteredBeforeRouteUpdate } from "@/hooks/onRouteHook"; +/** @format */ + +import { watch, shallowReactive, toRefs, computed, PropType, defineComponent } from 'vue'; +import { useRouter, useRoute, RouteLocationNormalized } from 'vue-router'; +import { Subscriber } from '@/types/songlist'; +import './index.scss'; +import { is, padPicCrop } from '@/utils'; +import { NEmpty, NGrid, NGridItem } from 'naive-ui'; +import RoutePagination, { PagiInfo } from '../route-pagination'; +import { PAGE_SIZE } from '@/utils/preference'; +import { onFilteredBeforeRouteUpdate } from '@/hooks/onRouteHook'; export default defineComponent({ - name: "subscriberList", - props: { - userLists: { - type: Array, - required: true, - }, - total: { - type: Number as PropType, - required: false, - default: 0, - }, - defaultLimit: { - type: Number as PropType, - required: false, - default: PAGE_SIZE.DEFAULT, - }, - gaps: { - type: Object as PropType>, - required: false, - default: () => ({ x: 40, y: 40 }) - }, - cols: { - type: Number as PropType, - required: false, - default: 10, - }, - hasMore: { - type: Boolean as PropType, - required: false, - default: true, - }, - }, - setup(props, { emit, slots }) { - const route = useRoute(); - const router = useRouter(); - const userItemClick = (item: Subscriber) => { - router.push({ path: "/user", query: { id: item.userId } }); - }; - const { defaultLimit } = props; - const subscriberPagiInfo = shallowReactive({ - limit: defaultLimit, - sizeArr: Array(2) - .fill(0) - .map((v, i) => defaultLimit * (i + 1)), - offset: 0, - total: 0, - }); + name: 'subscriberList', + props: { + userLists: { + type: Array, + required: true, + }, + total: { + type: Number as PropType, + required: false, + default: 0, + }, + defaultLimit: { + type: Number as PropType, + required: false, + default: PAGE_SIZE.DEFAULT, + }, + gaps: { + type: Object as PropType>, + required: false, + default: () => ({ x: 40, y: 40 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 10, + }, + hasMore: { + type: Boolean as PropType, + required: false, + default: true, + }, + }, + setup(props, { emit, slots }) { + const route = useRoute(); + const router = useRouter(); + const userItemClick = (item: Subscriber) => { + router.push({ path: '/user', query: { id: item.userId } }); + }; + const { defaultLimit } = props; + const subscriberPagiInfo = shallowReactive({ + limit: defaultLimit, + sizeArr: Array(2) + .fill(0) + .map((v, i) => defaultLimit * (i + 1)), + offset: 0, + total: 0, + }); + + const routeUpdateHandler = async ({ + params: { id }, + query: { limit, offset }, + }: RouteLocationNormalized) => { + const realLimit = Number(limit) || defaultLimit; + const realOffset = Number(offset) || 0; + subscriberPagiInfo.limit = realLimit; + subscriberPagiInfo.offset = realOffset; + }; + routeUpdateHandler(route); - const routeUpdateHandler = async ({ params: { id }, query: { limit, offset } } :RouteLocationNormalized) => { - const realLimit = Number(limit) || defaultLimit; - const realOffset = Number(offset) || 0; - subscriberPagiInfo.limit = realLimit; - subscriberPagiInfo.offset = realOffset; - }; - routeUpdateHandler(route); + onFilteredBeforeRouteUpdate((to) => { + routeUpdateHandler(to); + }); - onFilteredBeforeRouteUpdate((to) => { - routeUpdateHandler(to); - }); - - return () => { - const { userLists, gaps: { x, y }, cols, hasMore } = props; - return ( -
        -
        - - { - userLists.map((item: any) => ( - -
        userItemClick(item)} - > -
        - -
        -
        - {item.nickname} -
        -
        -
        - )) - } -
        -
        - { - !is.emptyArray(userLists) ? ( - - ) : ( - - ) - } -
        - ); - }; - }, + return () => { + const { + userLists, + gaps: { x, y }, + cols, + hasMore, + } = props; + return ( +
        +
        + + {userLists.map((item: any) => ( + +
        userItemClick(item)}> +
        + +
        +
        + {item.nickname} +
        +
        +
        + ))} +
        +
        + {!is.emptyArray(userLists) ? ( + + ) : ( + + )} +
        + ); + }; + }, }); diff --git a/src/widgets/user-login/index.scss b/src/widgets/user-login/index.scss index f228383..b6785c6 100644 --- a/src/widgets/user-login/index.scss +++ b/src/widgets/user-login/index.scss @@ -1,158 +1,155 @@ +/** @format */ + @import '@scss/variable'; .user-login { - @include flexVc(); - max-width: 158px; - margin-right: 50px; - color: #555; - transition: color .09s; - cursor: pointer; - - &:hover { - color: var(--theme); - } - .user-avatar { - height: 34px; - width: 34px; - border-radius: 50%; - background-image: url("@assets/img/user.svg"); - background-size: 40px; - background-repeat: no-repeat; - background-position: 0 center; - } - - .user-text { - flex: 1; - padding-left: 8px; - font-size: 13px; - } - - - + @include flexVc(); + max-width: 158px; + margin-right: 50px; + color: #555; + transition: color 0.09s; + cursor: pointer; + + &:hover { + color: var(--theme); + } + .user-avatar { + height: 34px; + width: 34px; + border-radius: 50%; + background-image: url('@assets/img/user.svg'); + background-size: 40px; + background-repeat: no-repeat; + background-position: 0 center; + } + + .user-text { + flex: 1; + padding-left: 8px; + font-size: 13px; + } } .login-dialog { - position: fixed; - left: 50%; - top: 50%; - width: 540px; - background-color: $colorbase; - transform: translate(-50%, -50%); - border-radius: 8px; - filter: drop-shadow(0 0 20px rgba(120,140,140, .3)); - z-index: 100; - - &-head { - @include flexHeVc(); - padding: 18px 24px; - - i { - color: #777; - font-size: 14px; - cursor: pointer; - &:hover { - color: var(--theme); - } - } - } - - &-body { - padding: 20px 40px; - - .login-button { - @include flexHcVc(); - } - - .login-by-qrCode { - .qrCode-img { - position: relative; - width: 180px; - height: 180px; - margin: 0 auto; - > img { - width: 100%; - height: 100%; - object-fit: contain; - } - .qrCode-user-mask { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - @include flexHcVc(); - background-color: rgba(160,160,160,.3); - - .qrCode-user-avatar { - position: relative; - filter: brightness(130%); - - > img { - width: 60px; - height: 60px; - object-fit: cover; - border-radius: 50%; - } - i { - position: absolute; - right: 0; - bottom: 0; - font-size: 28px; - font-weight: 500; - color: #d03050; - } - } - } - } - .qrCode-scan-status { - padding-top: 1rem; - text-align: center; - font-weight: 500; - color: #d03050; - } - .qrCode-refresh { - padding: 1rem 0 0 0; - text-align: center; - cursor: pointer; - em{ - color: #848484; - i{ - font-size: 20px; - vertical-align: middle; - } - span { - margin-left: .5rem; - font-size: 14px; - vertical-align: middle; - } - &:hover{ - color: dodgerblue; - } - } - } - } - - } - - &-foot { - @include flexHcVc(); - padding: 30px 0; - font-size: 0; - em { - display: inline-block; - padding: 0 20px; - font-size: 14px; - transition: .05s; - cursor: pointer; - color: #454545; - &:hover { - color: var(--theme); - text-decoration: underline; - } - &:not(:last-of-type) { - border-right: 1px solid #757575; - } - } - } - -} \ No newline at end of file + position: fixed; + left: 50%; + top: 50%; + width: 540px; + background-color: $colorbase; + transform: translate(-50%, -50%); + border-radius: 8px; + filter: drop-shadow(0 0 20px rgba(120, 140, 140, 0.3)); + z-index: 100; + + &-head { + @include flexHeVc(); + padding: 18px 24px; + + i { + color: #777; + font-size: 14px; + cursor: pointer; + &:hover { + color: var(--theme); + } + } + } + + &-body { + padding: 20px 40px; + + .login-button { + @include flexHcVc(); + } + + .login-by-qrCode { + .qrCode-img { + position: relative; + width: 180px; + height: 180px; + margin: 0 auto; + > img { + width: 100%; + height: 100%; + object-fit: contain; + } + .qrCode-user-mask { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + @include flexHcVc(); + background-color: rgba(160, 160, 160, 0.3); + + .qrCode-user-avatar { + position: relative; + filter: brightness(130%); + + > img { + width: 60px; + height: 60px; + object-fit: cover; + border-radius: 50%; + } + i { + position: absolute; + right: 0; + bottom: 0; + font-size: 28px; + font-weight: 500; + color: #d03050; + } + } + } + } + .qrCode-scan-status { + padding-top: 1rem; + text-align: center; + font-weight: 500; + color: #d03050; + } + .qrCode-refresh { + padding: 1rem 0 0 0; + text-align: center; + cursor: pointer; + em { + color: #848484; + i { + font-size: 20px; + vertical-align: middle; + } + span { + margin-left: 0.5rem; + font-size: 14px; + vertical-align: middle; + } + &:hover { + color: dodgerblue; + } + } + } + } + } + + &-foot { + @include flexHcVc(); + padding: 30px 0; + font-size: 0; + em { + display: inline-block; + padding: 0 20px; + font-size: 14px; + transition: 0.05s; + cursor: pointer; + color: #454545; + &:hover { + color: var(--theme); + text-decoration: underline; + } + &:not(:last-of-type) { + border-right: 1px solid #757575; + } + } + } +} diff --git a/src/widgets/user-login/index.tsx b/src/widgets/user-login/index.tsx index 179e41a..1db2d36 100644 --- a/src/widgets/user-login/index.tsx +++ b/src/widgets/user-login/index.tsx @@ -1,446 +1,498 @@ -import { getQrCodeImgInfo, getQrCodeKey, getQrCodeScanStatus, loginStatus, loginWithEmail, loginWithPhone } from "@/api/auth"; -import { userAccount, userCollectedMv, userDetail, userLikeList, userPlaylist, userSubcount } from "@/api/user"; -import { getPraisedVideos } from "@/api/video"; -import { NaiveFormValidateError } from "@/shim"; -import usePlayerStore from "@/stores/player"; -import useUserStore from "@/stores/user"; -import { emailVerifyPatt, is, phoneVerifyPatt } from "@/utils"; -import { loginCookie } from "@/utils/auth"; -import { messageBus } from "@/utils/event/register"; -import md5 from 'crypto-js/md5' -import { FormItemRule, NButton, NCol, NForm, NFormItem, NGrid, NGridItem, NInput, NRow, NxButton } from "naive-ui"; +/** @format */ + +import { + getQrCodeImgInfo, + getQrCodeKey, + getQrCodeScanStatus, + loginStatus, + loginWithEmail, + loginWithPhone, +} from '@/api/auth'; +import { + userAccount, + userCollectedMv, + userDetail, + userLikeList, + userPlaylist, + userSubcount, +} from '@/api/user'; +import { getPraisedVideos } from '@/api/video'; +import { NaiveFormValidateError } from '@/shim'; +import usePlayerStore from '@/stores/player'; +import useUserStore from '@/stores/user'; +import { emailVerifyPatt, is, phoneVerifyPatt } from '@/utils'; +import { loginCookie } from '@/utils/auth'; +import { messageBus } from '@/utils/event/register'; +import md5 from 'crypto-js/md5'; import { - getCurrentInstance, - markRaw, - defineComponent, - shallowRef, - ref, - Teleport, - shallowReactive, - PropType, - ComponentInternalInstance, - computed, - watch, - watchEffect, - reactive, - onUnmounted, - onBeforeUnmount, -} from "vue"; -import { routerViewLocationKey, useRouter } from "vue-router"; + FormItemRule, + NButton, + NCol, + NForm, + NFormItem, + NGrid, + NGridItem, + NInput, + NRow, + NxButton, +} from 'naive-ui'; +import { + getCurrentInstance, + markRaw, + defineComponent, + shallowRef, + ref, + Teleport, + shallowReactive, + PropType, + ComponentInternalInstance, + computed, + watch, + watchEffect, + reactive, + onUnmounted, + onBeforeUnmount, +} from 'vue'; +import { routerViewLocationKey, useRouter } from 'vue-router'; import './index.scss'; const loginTypes = [ - { key: 'phone', text: '手机号登录' }, - { key: 'qrCode', text: '二维码登录' }, - { key: 'email', text: '邮箱登录' }, -] + { key: 'phone', text: '手机号登录' }, + { key: 'qrCode', text: '二维码登录' }, + { key: 'email', text: '邮箱登录' }, +]; export enum QrCodeStatus { - '二维码已失效,请重新刷新扫码登录喔~~' = 800, - '请打开网易云音乐APP进行扫码唷~~' = 801, - '正在登录啦,赶快确认吧!' = 802, - '授权登录成功啦~~~' = 803, + '二维码已失效,请重新刷新扫码登录喔~~' = 800, + '请打开网易云音乐APP进行扫码唷~~' = 801, + '正在登录啦,赶快确认吧!' = 802, + '授权登录成功啦~~~' = 803, } - /** -* 电话和密码登录规则 -*/ + * 电话和密码登录规则 + */ const phoneRules: Record<'number' | 'password', FormItemRule | FormItemRule[]> = { - number: [ - { - required: true, - message: "阿娜哒~要输入正确的手机号噢~~", - validator(rule, value) { - return !!value.match(phoneVerifyPatt) - }, - trigger: ['blur'], - } - ], - password: { - required: true, - message: '请输入密码', - } -} + number: [ + { + required: true, + message: '阿娜哒~要输入正确的手机号噢~~', + validator(rule, value) { + return !!value.match(phoneVerifyPatt); + }, + trigger: ['blur'], + }, + ], + password: { + required: true, + message: '请输入密码', + }, +}; /** -* 网易云邮箱和密码登录规则 -*/ + * 网易云邮箱和密码登录规则 + */ const emailRules: Record<'email' | 'password', FormItemRule | FormItemRule[]> = { - email: [ - { - required: true, - message: "阿娜哒~要输入正确的邮箱号噢~~", - validator(rule, value) { - return !!value.match(emailVerifyPatt) - }, - trigger: ['blur'], - } - ], - password: { - required: true, - message: '请输入密码', - } -} + email: [ + { + required: true, + message: '阿娜哒~要输入正确的邮箱号噢~~', + validator(rule, value) { + return !!value.match(emailVerifyPatt); + }, + trigger: ['blur'], + }, + ], + password: { + required: true, + message: '请输入密码', + }, +}; export default defineComponent({ - name: "UserLogin", - setup(props, context) { - const userStore = useUserStore(); - const router = useRouter(); - const playerStore = usePlayerStore(); - /** - * 电话登录表单的Ref - */ - const loginWithPhoneFormRef = ref(); - /** - * 网易云邮箱表单的Ref - */ - const loginWithEmailFormRef = ref(); - - /** - * 当前登录的方式:电话、二维码、邮箱,默认:电话 - */ - const currentLoginType = ref(loginTypes[0].key); - /** - * 电话登录的表单数据 - */ - const phoneInfo = shallowReactive({ - number: '', - password: '', - }); - /** - * 二维码登录的表单数据 - */ - const emailInfo = shallowReactive({ - email: '', - password: '', - }); - /** - * 二维码登录的相关数据 - */ - const qrCodeInfo = reactive({ - img: '', - key: '', - scanStatusMsg: QrCodeStatus[801], - user: { - nickname: '', - avatarUrl: '', - } - }); - - /** - * 登录区域内容信息 - */ - const userLoginInfo = computed(() => { - const isLogin = userStore.isLogin; - const style = { - backgroundImage: isLogin ? `url(${userStore.detail.profile.avatarUrl})` : '' - } - const name = isLogin ? userStore.detail.profile.nickname : '点击登录' - return { - style, name - } - }); - - userStore.judgeAndSetAccountInfo({ - isFirstRefresh: true, - }); - - watch(() => userStore.userId, (id) => { - //我的详细信息 - userDetail(id).then(detail => { - userStore.detail = detail - }); - - //我喜欢的音乐ids - userLikeList({ uid: id }).then(({ ids }) => userStore.myLoveListIds = ids); - - //点赞过的视频 - getPraisedVideos().then(({ data: { feeds } }) => playerStore.video.beLiked = feeds); - - //收藏的mv - userCollectedMv().then(({ data }) => { - playerStore.mv.beCollected = data; - }); - - //我的歌单 - userPlaylist({ uid: id }).then(({ playlist }) => { - userStore.playlist = playlist.reduce( - (categoryItem: any, list: any) => { - categoryItem[list.userId === +id ? 'myCreated' : 'myCollection'].push(list); - return categoryItem; - }, - { - myCreated: [], - myCollection: [] - } - ); - }); - - //订阅数量等相关信息 - userSubcount().then((subCount) => { - userStore.subCount = subCount; - }); - }); - - /** - * header处的登录按钮点击 - * @returns - */ - const loginBtnClick = () => { - if (userStore.isLogin) { - router.push({ - path: '/myPage', - query: { id: userStore.detail.profile.userId } - }) - return - } - userStore.showLoginDialog = true; - } - - /** - * 关闭登录框 - */ - const closeLoginDialog = () => { - userStore.showLoginDialog = false; - } - - /** - * 切换登录类型 - * @param key - */ - const switchLoginType = (key: string) => { - currentLoginType.value = key; - } - - /** - * 处理登录后的响应数据 - * @param cookie - */ - const handleLoginResponse = (cookie: string) => { - loginCookie.value = cookie; - userStore.judgeAndSetAccountInfo(); - } - - /** - * 电话表单登录处理 - */ - const loginByPhoneClick = () => { - loginWithPhoneFormRef.value!.validate(async (error: NaiveFormValidateError) => { - if (error) { - return; - } - const { cookie } = await loginWithPhone({ - phone: phoneInfo.number, - password: 'invalidPassword', - md5_password: String(md5(phoneInfo.password)), - }); - handleLoginResponse(cookie); - }); - } - - /** - * 网易云邮箱登录处理 - */ - const loginByEmailClick = () => { - loginWithEmailFormRef.value!.validate(async (error: NaiveFormValidateError) => { - if (error) { - return; - } - const { cookie } = await loginWithEmail({ - email: emailInfo.email, - password: 'invalidPassword', - md5_password: String(md5(emailInfo.password)), - }); - handleLoginResponse(cookie); - }); - } - - /** - * 刷新二维码、开始二维码登录的请求通道 - */ - let rotationQrCodeTimer: ReturnType; - const refrechOrBeginQrCode = (isRefresh = false) => { - clearInterval(rotationQrCodeTimer); - //重置让二维码登录提示 - qrCodeInfo.scanStatusMsg = QrCodeStatus[801]; - //重置用户待确认状态下的信息 - qrCodeInfo.user.avatarUrl = ''; - qrCodeInfo.user.nickname = ''; - getQrCodeKey().then(async ({ data: { unikey } }) => { - qrCodeInfo.key = unikey; - const { data: { qrimg, qrurl } } = await getQrCodeImgInfo({ - key: unikey - }) - qrCodeInfo.img = qrimg; - isRefresh && ( - messageBus.dispatch('warnMessage', '二维码刷新成功啦!赶紧登录吧', { - duration: 3000, - }) - ) - rotationQrCodeTimer = setInterval(async () => { - const { code, message, cookie, avatarUrl, nickname, account } = await getQrCodeScanStatus({ key: unikey }); - qrCodeInfo.scanStatusMsg = QrCodeStatus[code]; - //已过期 - if (code === 800) { - clearInterval(rotationQrCodeTimer); - } - //待确认中 - else if (code === 802 && !cookie) { - qrCodeInfo.user.avatarUrl = avatarUrl; - qrCodeInfo.user.nickname = nickname; - } - else if (code === 803 || cookie) { - clearInterval(rotationQrCodeTimer); - handleLoginResponse(cookie); - } - }, 3000); - }); - } - - - /** - * 组件将要卸载时也要清除下轮询定时器,以防未知情况 - */ - onBeforeUnmount(() => { - clearInterval(rotationQrCodeTimer); - }); - - watchEffect(() => { - if (currentLoginType.value === 'qrCode') { - refrechOrBeginQrCode(); - return; - } - //切换到其它登录方式下也要清除轮询定时器 - clearInterval(rotationQrCodeTimer); - }); - - watchEffect(() => { - //关闭登录框按钮后,重置登录模式为手机号登录 - if (!userStore.showLoginDialog) { - switchLoginType(loginTypes[0].key); - } - }); - - const renderLoginBody = () => { - const currentLoginTypeValue = currentLoginType.value; - //电话登录 - if (currentLoginTypeValue === 'phone') { - return ( -
        - - - phoneInfo.number = value}> - - - phoneInfo.password = value}> - - - -
        - ) - } - //网易云邮箱登录 - else if (currentLoginTypeValue === 'email') { - return ( -
        - - - emailInfo.email = value}> - - - emailInfo.password = value}> - - - -
        - ) - } - //二维码登录 - else if (currentLoginTypeValue === 'qrCode') { - return ( -
        -
        - - { - qrCodeInfo.user.avatarUrl && ( -
        -
        - - -
        -
        - ) - } -
        -

        - { - qrCodeInfo.scanStatusMsg - } -

        -

        - refrechOrBeginQrCode(true)} title="刷新二维码登录"> - - 刷新二维码 - -

        -
        - ) - } - } - - return () => { - - const loginBaseInfo = userLoginInfo.value; - const currentLoginTypeValue = currentLoginType.value; - const otherLoginTypes = loginTypes.filter(({ key }) => key !== currentLoginTypeValue); - console.log(userStore.showLoginDialog, userStore.isLogin); - - return ( - <> - - - - - - - - - ) - } - }, + name: 'UserLogin', + setup(props, context) { + const userStore = useUserStore(); + const router = useRouter(); + const playerStore = usePlayerStore(); + /** + * 电话登录表单的Ref + */ + const loginWithPhoneFormRef = ref(); + /** + * 网易云邮箱表单的Ref + */ + const loginWithEmailFormRef = ref(); + + /** + * 当前登录的方式:电话、二维码、邮箱,默认:电话 + */ + const currentLoginType = ref(loginTypes[0].key); + /** + * 电话登录的表单数据 + */ + const phoneInfo = shallowReactive({ + number: '', + password: '', + }); + /** + * 二维码登录的表单数据 + */ + const emailInfo = shallowReactive({ + email: '', + password: '', + }); + /** + * 二维码登录的相关数据 + */ + const qrCodeInfo = reactive({ + img: '', + key: '', + scanStatusMsg: QrCodeStatus[801], + user: { + nickname: '', + avatarUrl: '', + }, + }); + + /** + * 登录区域内容信息 + */ + const userLoginInfo = computed(() => { + const isLogin = userStore.isLogin; + const style = { + backgroundImage: isLogin ? `url(${userStore.detail.profile.avatarUrl})` : '', + }; + const name = isLogin ? userStore.detail.profile.nickname : '点击登录'; + return { + style, + name, + }; + }); + + userStore.judgeAndSetAccountInfo({ + isFirstRefresh: true, + }); + + watch( + () => userStore.userId, + (id) => { + //我的详细信息 + userDetail(id).then((detail) => { + userStore.detail = detail; + }); + + //我喜欢的音乐ids + userLikeList({ uid: id }).then(({ ids }) => (userStore.myLoveListIds = ids)); + + //点赞过的视频 + getPraisedVideos().then( + ({ data: { feeds } }) => (playerStore.video.beLiked = feeds), + ); + + //收藏的mv + userCollectedMv().then(({ data }) => { + playerStore.mv.beCollected = data; + }); + + //我的歌单 + userPlaylist({ uid: id }).then(({ playlist }) => { + userStore.playlist = playlist.reduce( + (categoryItem: any, list: any) => { + categoryItem[list.userId === +id ? 'myCreated' : 'myCollection'].push(list); + return categoryItem; + }, + { + myCreated: [], + myCollection: [], + }, + ); + }); + + //订阅数量等相关信息 + userSubcount().then((subCount) => { + userStore.subCount = subCount; + }); + }, + ); + + /** + * header处的登录按钮点击 + * @returns + */ + const loginBtnClick = () => { + if (userStore.isLogin) { + router.push({ + path: '/myPage', + query: { id: userStore.detail.profile.userId }, + }); + return; + } + userStore.showLoginDialog = true; + }; + + /** + * 关闭登录框 + */ + const closeLoginDialog = () => { + userStore.showLoginDialog = false; + }; + + /** + * 切换登录类型 + * @param key + */ + const switchLoginType = (key: string) => { + currentLoginType.value = key; + }; + + /** + * 处理登录后的响应数据 + * @param cookie + */ + const handleLoginResponse = (cookie: string) => { + loginCookie.value = cookie; + userStore.judgeAndSetAccountInfo(); + }; + + /** + * 电话表单登录处理 + */ + const loginByPhoneClick = () => { + loginWithPhoneFormRef.value!.validate(async (error: NaiveFormValidateError) => { + if (error) { + return; + } + const { cookie } = await loginWithPhone({ + phone: phoneInfo.number, + password: 'invalidPassword', + md5_password: String(md5(phoneInfo.password)), + }); + handleLoginResponse(cookie); + }); + }; + + /** + * 网易云邮箱登录处理 + */ + const loginByEmailClick = () => { + loginWithEmailFormRef.value!.validate(async (error: NaiveFormValidateError) => { + if (error) { + return; + } + const { cookie } = await loginWithEmail({ + email: emailInfo.email, + password: 'invalidPassword', + md5_password: String(md5(emailInfo.password)), + }); + handleLoginResponse(cookie); + }); + }; + + /** + * 刷新二维码、开始二维码登录的请求通道 + */ + let rotationQrCodeTimer: ReturnType; + const refrechOrBeginQrCode = (isRefresh = false) => { + clearInterval(rotationQrCodeTimer); + //重置让二维码登录提示 + qrCodeInfo.scanStatusMsg = QrCodeStatus[801]; + //重置用户待确认状态下的信息 + qrCodeInfo.user.avatarUrl = ''; + qrCodeInfo.user.nickname = ''; + getQrCodeKey().then(async ({ data: { unikey } }) => { + qrCodeInfo.key = unikey; + const { + data: { qrimg, qrurl }, + } = await getQrCodeImgInfo({ + key: unikey, + }); + qrCodeInfo.img = qrimg; + isRefresh && + messageBus.dispatch('warnMessage', '二维码刷新成功啦!赶紧登录吧', { + duration: 3000, + }); + rotationQrCodeTimer = setInterval(async () => { + const { code, message, cookie, avatarUrl, nickname, account } = + await getQrCodeScanStatus({ key: unikey }); + qrCodeInfo.scanStatusMsg = QrCodeStatus[code]; + //已过期 + if (code === 800) { + clearInterval(rotationQrCodeTimer); + } + //待确认中 + else if (code === 802 && !cookie) { + qrCodeInfo.user.avatarUrl = avatarUrl; + qrCodeInfo.user.nickname = nickname; + } else if (code === 803 || cookie) { + clearInterval(rotationQrCodeTimer); + handleLoginResponse(cookie); + } + }, 3000); + }); + }; + + /** + * 组件将要卸载时也要清除下轮询定时器,以防未知情况 + */ + onBeforeUnmount(() => { + clearInterval(rotationQrCodeTimer); + }); + + watchEffect(() => { + if (currentLoginType.value === 'qrCode') { + refrechOrBeginQrCode(); + return; + } + //切换到其它登录方式下也要清除轮询定时器 + clearInterval(rotationQrCodeTimer); + }); + + watchEffect(() => { + //关闭登录框按钮后,重置登录模式为手机号登录 + if (!userStore.showLoginDialog) { + switchLoginType(loginTypes[0].key); + } + }); + + const renderLoginBody = () => { + const currentLoginTypeValue = currentLoginType.value; + //电话登录 + if (currentLoginTypeValue === 'phone') { + return ( +
        + + + (phoneInfo.number = value)} + > + + + (phoneInfo.password = value)} + > + + + +
        + ); + } + //网易云邮箱登录 + else if (currentLoginTypeValue === 'email') { + return ( +
        + + + (emailInfo.email = value)} + > + + + (emailInfo.password = value)} + > + + + +
        + ); + } + //二维码登录 + else if (currentLoginTypeValue === 'qrCode') { + return ( +
        +
        + + {qrCodeInfo.user.avatarUrl && ( +
        +
        + + +
        +
        + )} +
        +

        {qrCodeInfo.scanStatusMsg}

        +

        + refrechOrBeginQrCode(true)} title="刷新二维码登录"> + + 刷新二维码 + +

        +
        + ); + } + }; + + return () => { + const loginBaseInfo = userLoginInfo.value; + const currentLoginTypeValue = currentLoginType.value; + const otherLoginTypes = loginTypes.filter( + ({ key }) => key !== currentLoginTypeValue, + ); + console.log(userStore.showLoginDialog, userStore.isLogin); + + return ( + <> + + + + + + ); + }; + }, }); diff --git a/src/widgets/video-list/index.scss b/src/widgets/video-list/index.scss index 5d88311..53c1490 100644 --- a/src/widgets/video-list/index.scss +++ b/src/widgets/video-list/index.scss @@ -1,32 +1,34 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .video-container { - .video-card { - position: relative; - width: 100%; + .video-card { + position: relative; + width: 100%; - img { - width: 100%; - border-radius: 14px; - transition: 0.16s; - cursor: pointer; + img { + width: 100%; + border-radius: 14px; + transition: 0.16s; + cursor: pointer; - &:hover { - filter: drop-shadow(0 16px 20px rgba(40, 70, 100, 0.2)); - transform: translateY(-6px); - } - } - .video-title { - padding: 8px 0; - color: #555; - font-size: 14px; - line-height: 1.4; - } + &:hover { + filter: drop-shadow(0 16px 20px rgba(40, 70, 100, 0.2)); + transform: translateY(-6px); + } + } + .video-title { + padding: 8px 0; + color: #555; + font-size: 14px; + line-height: 1.4; + } - .video-info { - @include flexHbVc(); - color: #666; - font-size: 13px; - } - } + .video-info { + @include flexHbVc(); + color: #666; + font-size: 13px; + } + } } diff --git a/src/widgets/video-list/index.tsx b/src/widgets/video-list/index.tsx index 9e7b529..88a4e13 100644 --- a/src/widgets/video-list/index.tsx +++ b/src/widgets/video-list/index.tsx @@ -1,108 +1,100 @@ -import { - computed, - defineComponent, - PropType, - watchEffect, -} from "vue"; -import { - useRouter, -} from "vue-router"; -import { getLocaleCount, is, padPicCrop } from "@utils/index"; -import { NEmpty, NGrid, NGridItem } from "naive-ui"; -import "./index.scss"; -import usePlayerStore from "@/stores/player"; +/** @format */ -export const formatVideoList = (videoList:any) => { - videoList.forEach((v: any) => { - v.playTimeStr = getLocaleCount(v.playTime); - const { creator, artists, } = v; - v.userName = is.array(creator) - ? creator.map(({userName}) => userName).join("、") - : is.array(artists) - ? artists.map(({name}) => name).join('、') - : creator.nickname; - }); - return videoList; -} +import { computed, defineComponent, PropType, watchEffect } from 'vue'; +import { useRouter } from 'vue-router'; +import { getLocaleCount, is, padPicCrop } from '@utils/index'; +import { NEmpty, NGrid, NGridItem } from 'naive-ui'; +import './index.scss'; +import usePlayerStore from '@/stores/player'; + +export const formatVideoList = (videoList: any) => { + videoList.forEach((v: any) => { + v.playTimeStr = getLocaleCount(v.playTime); + const { creator, artists } = v; + v.userName = is.array(creator) + ? creator.map(({ userName }) => userName).join('、') + : is.array(artists) + ? artists.map(({ name }) => name).join('、') + : creator.nickname; + }); + return videoList; +}; export default defineComponent({ - name: "VideoList", - props: { - videoList: { - type: Array as PropType, - required: true - }, - gaps: { - type: Object as PropType>>, - required: false, - default: () => ({ x: 30, y: 30 }) - }, - cols: { - type: Number as PropType, - required: false, - default: 5 - } - }, - setup(props, context) { - const router = useRouter(); - const playerStore = usePlayerStore(); + name: 'VideoList', + props: { + videoList: { + type: Array as PropType, + required: true, + }, + gaps: { + type: Object as PropType>>, + required: false, + default: () => ({ x: 30, y: 30 }), + }, + cols: { + type: Number as PropType, + required: false, + default: 5, + }, + }, + setup(props, context) { + const router = useRouter(); + const playerStore = usePlayerStore(); - const toVideoDetailPage = (vid: number) => { - router.push({ - path: '/video', - query: { - vid - } - }); - playerStore.setVideoIsPlay(true); - } + const toVideoDetailPage = (vid: number) => { + router.push({ + path: '/video', + query: { + vid, + }, + }); + playerStore.setVideoIsPlay(true); + }; - const renderList = (videoList: any[]) => { - return ( - videoList.map((video) => - -
        -
        - toVideoDetailPage(video.vid)} - alt="" - /> -
        -
        -
        {video.title}
        -

        - {video.userName} - {video.playTimeStr} -

        -
        -
        -
        - ) - ) - } + const renderList = (videoList: any[]) => { + return videoList.map((video) => ( + +
        +
        + toVideoDetailPage(video.vid)} + alt="" + /> +
        +
        +
        {video.title}
        +

        + {video.userName} + {video.playTimeStr} +

        +
        +
        +
        + )); + }; - return () => { - const { gaps: { x, y }, cols } = props; - const videoList = formatVideoList(props.videoList); - return ( - <> -
        - { - is.emptyArray(videoList) - ? ( - - ) - : ( - - { renderList(videoList) } - - ) - } -
        - - ); - }; - }, + return () => { + const { + gaps: { x, y }, + cols, + } = props; + const videoList = formatVideoList(props.videoList); + return ( + <> +
        + {is.emptyArray(videoList) ? ( + + ) : ( + + {renderList(videoList)} + + )} +
        + + ); + }; + }, }); diff --git a/src/widgets/yuan-button/index.scss b/src/widgets/yuan-button/index.scss index 0fb1a76..1d5dbd4 100644 --- a/src/widgets/yuan-button/index.scss +++ b/src/widgets/yuan-button/index.scss @@ -1,30 +1,32 @@ -@import "@scss/variable"; +/** @format */ + +@import '@scss/variable'; .yuan-button { - @include flexVc(); - padding: 8px 12px; - border-radius: 8px; - color: #838383; - border-style: solid; - border-width: 1px; - border-color: rgba(222,222,222, .5); - background-color: $colorbase; - transition: .16s; - cursor: pointer; - - &.yuan-button-disabled { - cursor: no-drop; - } + @include flexVc(); + padding: 8px 12px; + border-radius: 8px; + color: #838383; + border-style: solid; + border-width: 1px; + border-color: rgba(222, 222, 222, 0.5); + background-color: $colorbase; + transition: 0.16s; + cursor: pointer; + + &.yuan-button-disabled { + cursor: no-drop; + } - &:not(.yuan-button-disabled):hover { - background-color: rgb(241,231,221,.6); - // color: var(--theme); - filter: drop-shadow(0 1rem 1rem rgba(160,170,190,.18)) brightness(104%); - } - // &:not(.yuan-button-disabled).yuan-button-isActive, - // &:not(.yuan-button-disabled):active { - // background-color: var(--theme); - // filter: drop-shadow(0 2rem 2rem rgba(160,170,190,.38)) brightness(104%); - // color: $colorbase; - // } -} \ No newline at end of file + &:not(.yuan-button-disabled):hover { + background-color: rgb(241, 231, 221, 0.6); + // color: var(--theme); + filter: drop-shadow(0 1rem 1rem rgba(160, 170, 190, 0.18)) brightness(104%); + } + // &:not(.yuan-button-disabled).yuan-button-isActive, + // &:not(.yuan-button-disabled):active { + // background-color: var(--theme); + // filter: drop-shadow(0 2rem 2rem rgba(160,170,190,.38)) brightness(104%); + // color: $colorbase; + // } +} diff --git a/src/widgets/yuan-button/index.tsx b/src/widgets/yuan-button/index.tsx index fccd0c8..2a16f59 100644 --- a/src/widgets/yuan-button/index.tsx +++ b/src/widgets/yuan-button/index.tsx @@ -1,77 +1,79 @@ -import { is } from "@/utils"; -import { computed, defineComponent, PropType, ref, watch } from "vue"; +/** @format */ + +import { is } from '@/utils'; +import { computed, defineComponent, PropType, ref, watch } from 'vue'; import './index.scss'; export type YuanButtonData = { - text: string; - value: any; -} + text: string; + value: any; +}; export default defineComponent({ - name: 'YuanButton', - props: { - value: { - type: null, - required: false, - default: void 0, - }, - text: { - type: String as PropType, - required: false, - default: '', - }, - onClick: { - type: Function as PropType<(data: YuanButtonData) => void>, - required: false, - default: () => (() => {}), - }, - disabled: { - type: Boolean as PropType, - required: false, - default: false - }, - isActive: { - type: Boolean as PropType, - required: false, - default: false - }, - onUpdateIsActive: { - type: [Function] as PropType<(isActive:boolean) => void>, - requred: false, - default: () => (() => {}), - } - }, - emits: ['updateIsActive'], - setup(props, { slots, emit }) { - const isActive = ref(false); - watch(() => props.isActive, (value) => { - isActive.value = value; - }, { - immediate: true - }); - const clickHandler = (data: { - value:string, - text: string - }) => { - isActive.value = !isActive.value; - emit('updateIsActive', isActive.value); - props.onClick(data); - } - const classNameRef = computed(() => { - return { - 'yuan-button': true, - 'yuan-button-disabled': props.disabled, - 'yuan-button-isActive': isActive.value, - } - }); - return () => { - const { text, value } = props; - return ( - - ) - } - - } -}) \ No newline at end of file + name: 'YuanButton', + props: { + value: { + type: null, + required: false, + default: void 0, + }, + text: { + type: String as PropType, + required: false, + default: '', + }, + onClick: { + type: Function as PropType<(data: YuanButtonData) => void>, + required: false, + default: () => () => {}, + }, + disabled: { + type: Boolean as PropType, + required: false, + default: false, + }, + isActive: { + type: Boolean as PropType, + required: false, + default: false, + }, + onUpdateIsActive: { + type: [Function] as PropType<(isActive: boolean) => void>, + requred: false, + default: () => () => {}, + }, + }, + emits: ['updateIsActive'], + setup(props, { slots, emit }) { + const isActive = ref(false); + watch( + () => props.isActive, + (value) => { + isActive.value = value; + }, + { + immediate: true, + }, + ); + const clickHandler = (data: { value: string; text: string }) => { + isActive.value = !isActive.value; + emit('updateIsActive', isActive.value); + props.onClick(data); + }; + const classNameRef = computed(() => { + return { + 'yuan-button': true, + 'yuan-button-disabled': props.disabled, + 'yuan-button-isActive': isActive.value, + }; + }); + return () => { + const { text, value } = props; + return ( + + ); + }; + }, +}); diff --git a/tsconfig.json b/tsconfig.json index bbd269d..48da71b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,57 +1,52 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "lib": ["DOM", "ESNext", "WebWorker"], - "jsx": "preserve", - "allowJs": true, - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "declaration": true, - "noImplicitAny": true, - "isolatedModules": true, - "moduleResolution": "node", - "resolveJsonModule": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - // "forceConsistentCasingInFileNames": true, - "rootDir": "./", - "outDir": "dist", - "baseUrl": "./", - "types": [ - "vite/client", - "node", - "vite-svg-loader", - "vite-plugin-pwa/client" - ], - "plugins": [{ "name": "@vuedx/typescript-plugin-vue" }], - "paths": { - "@/*": ["src/*"], - "@api/*": ["./src/api/*"], - "@router/*": ["./src/router/*"], - "@type/*": ["src/types/*"], - "@scss/*": ["./src/scss/*"], - "@components/*": ["./src/components/*"], - "@views/*": ["./src/views/*"], - "@assets/*": ["./src/assets/*"], - "@request/*": ["./src/request/*"], - "@utils/*": ["./src/utils/*"], - "@stores/*": ["./src/stores/*"], - "@use/*": ["src/hooks/*"], - "@database/*": ["./src/database/*"], - "@widgets/*": ["./src/widgets/*"] - }, - "sourceMap": true - }, - "include": [ - "scripts/**/*.ts", //必须指定,否则不能解析viteConfig下的ts文件 - "scripts/**/*.d.ts", - "src/**/*.d.ts", - "src/**/*.ts", - "src/**/*.tsx", - "src/**/*.vue", - "global.d.ts" - ], - "exclude": ["dist", "node_modules", "public"] + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["DOM", "ESNext", "WebWorker"], + "jsx": "preserve", + "allowJs": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "noImplicitAny": true, + "isolatedModules": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + // "forceConsistentCasingInFileNames": true, + "rootDir": "./", + "outDir": "dist", + "baseUrl": "./", + "types": ["vite/client", "node", "vite-svg-loader", "vite-plugin-pwa/client"], + "plugins": [{ "name": "@vuedx/typescript-plugin-vue" }], + "paths": { + "@/*": ["src/*"], + "@api/*": ["./src/api/*"], + "@router/*": ["./src/router/*"], + "@type/*": ["src/types/*"], + "@scss/*": ["./src/scss/*"], + "@components/*": ["./src/components/*"], + "@views/*": ["./src/views/*"], + "@assets/*": ["./src/assets/*"], + "@request/*": ["./src/request/*"], + "@utils/*": ["./src/utils/*"], + "@stores/*": ["./src/stores/*"], + "@use/*": ["src/hooks/*"], + "@database/*": ["./src/database/*"], + "@widgets/*": ["./src/widgets/*"] + }, + "sourceMap": true + }, + "include": [ + "scripts/**/*.ts", //必须指定,否则不能解析viteConfig下的ts文件 + "scripts/**/*.d.ts", + "src/**/*.d.ts", + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "global.d.ts" + ], + "exclude": ["dist", "node_modules", "public"] } diff --git a/vercel.json b/vercel.json index a725bc1..4ace14d 100644 --- a/vercel.json +++ b/vercel.json @@ -1,8 +1,8 @@ { - "rewrites": [ - { - "source": "/api/:slug*", - "destination": "https://refrain-music-api.vercel.app/:slug*" - } - ] + "rewrites": [ + { + "source": "/api/:slug*", + "destination": "https://refrain-music-api.vercel.app/:slug*" + } + ] }