🐞 fix: 同步上游接口以解决登录风控问题 #270

This commit is contained in:
imsyy
2024-10-05 11:13:25 +08:00
parent 2b48713565
commit dc480459eb
18 changed files with 702 additions and 214 deletions

View File

@@ -30,7 +30,7 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "off",
"vue/multi-word-component-names": "off",
},
global: {
globals: {
h: "readonly",
ref: "readonly",
computed: "readonly",

3
components.d.ts vendored
View File

@@ -124,11 +124,14 @@ declare module 'vue' {
SearchInpMenu: typeof import('./src/components/Menu/SearchInpMenu.vue')['default']
SearchSuggest: typeof import('./src/components/Search/SearchSuggest.vue')['default']
Sider: typeof import('./src/components/Layout/Sider.vue')['default']
SImage: typeof import('./src/components/UI/s-image.vue')['default']
SongCard: typeof import('./src/components/Card/SongCard.vue')['default']
SongDataCard: typeof import('./src/components/Card/SongDataCard.vue')['default']
SongInfoEditor: typeof import('./src/components/Modal/SongInfoEditor.vue')['default']
SongList: typeof import('./src/components/List/SongList.vue')['default']
SongListCard: typeof import('./src/components/Card/SongListCard.vue')['default']
SongListMenu: typeof import('./src/components/Menu/SongListMenu.vue')['default']
SongListNew: typeof import('./src/components/List/SongListNew.vue')['default']
SvgIcon: typeof import('./src/components/Global/SvgIcon.vue')['default']
TextContainer: typeof import('./src/components/Global/TextContainer.vue')['default']
UpdateApp: typeof import('./src/components/Modal/UpdateApp.vue')['default']

View File

@@ -1,7 +1,7 @@
{
"name": "splayer",
"productName": "SPlayer",
"version": "3.0.0-alpha.2",
"version": "3.0.0-alpha.3",
"description": "A minimalist music player",
"main": "./out/main/index.js",
"author": "imsyy",
@@ -48,7 +48,7 @@
"@pixi/filter-color-matrix": "^7.4.2",
"@pixi/sprite": "^7.4.2",
"@vueuse/core": "^10.11.1",
"NeteaseCloudMusicApi": "^4.22.0",
"NeteaseCloudMusicApi": "^4.23.0",
"axios": "^1.7.7",
"change-case": "^5.4.4",
"dayjs": "^1.11.13",
@@ -69,6 +69,7 @@
"pinia": "^2.2.2",
"pinia-plugin-persistedstate": "^3.2.3",
"plyr": "^3.7.8",
"vue-virt-list": "^1.5.1",
"vue-virtual-scroller": "2.0.0-beta.8"
},
"devDependencies": {
@@ -107,7 +108,7 @@
"vite": "^5.4.3",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-wasm": "^3.3.0",
"vue": "3.4.38",
"vue": "3.5.10",
"vue-router": "^4.4.3",
"vue-tsc": "^2.1.6"
}

271
pnpm-lock.yaml generated
View File

@@ -16,7 +16,7 @@ importers:
version: 0.2.2
'@applemusic-like-lyrics/vue':
specifier: ^0.1.5
version: 0.1.5(@pixi/app@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/filter-blur@7.4.2(@pixi/core@7.4.2))(@pixi/filter-bulge-pinch@5.1.1(@pixi/core@7.4.2))(@pixi/filter-color-matrix@7.4.2(@pixi/core@7.4.2))(@pixi/sprite@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(jss-preset-default@10.10.0)(jss@10.10.0)(vue@3.4.38(typescript@5.5.4))
version: 0.1.5(@pixi/app@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/filter-blur@7.4.2(@pixi/core@7.4.2))(@pixi/filter-bulge-pinch@5.1.1(@pixi/core@7.4.2))(@pixi/filter-color-matrix@7.4.2(@pixi/core@7.4.2))(@pixi/sprite@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(jss-preset-default@10.10.0)(jss@10.10.0)(vue@3.5.10(typescript@5.5.4))
'@electron-toolkit/preload':
specifier: ^3.0.1
version: 3.0.1(electron@28.3.3)
@@ -52,10 +52,10 @@ importers:
version: 7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))
'@vueuse/core':
specifier: ^10.11.1
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
version: 10.11.1(vue@3.5.10(typescript@5.5.4))
NeteaseCloudMusicApi:
specifier: ^4.22.0
version: 4.22.0
specifier: ^4.23.0
version: 4.23.0
axios:
specifier: ^1.7.7
version: 1.7.7
@@ -109,16 +109,19 @@ importers:
version: 7.14.0
pinia:
specifier: ^2.2.2
version: 2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
version: 2.2.2(typescript@5.5.4)(vue@3.5.10(typescript@5.5.4))
pinia-plugin-persistedstate:
specifier: ^3.2.3
version: 3.2.3(pinia@2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))
version: 3.2.3(pinia@2.2.2(typescript@5.5.4)(vue@3.5.10(typescript@5.5.4)))
plyr:
specifier: ^3.7.8
version: 3.7.8
vue-virt-list:
specifier: ^1.5.1
version: 1.5.1(vue@3.5.10(typescript@5.5.4))
vue-virtual-scroller:
specifier: 2.0.0-beta.8
version: 2.0.0-beta.8(vue@3.4.38(typescript@5.5.4))
version: 2.0.0-beta.8(vue@3.5.10(typescript@5.5.4))
devDependencies:
'@electron-toolkit/tsconfig':
specifier: ^1.0.1
@@ -155,7 +158,7 @@ importers:
version: 7.18.0(eslint@8.57.0)(typescript@5.5.4)
'@vitejs/plugin-vue':
specifier: ^5.1.3
version: 5.1.3(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0))(vue@3.4.38(typescript@5.5.4))
version: 5.1.3(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0))(vue@3.5.10(typescript@5.5.4))
ajv:
specifier: ^8.17.1
version: 8.17.1
@@ -188,7 +191,7 @@ importers:
version: 4.28.1
naive-ui:
specifier: ^2.39.0
version: 2.39.0(vue@3.4.38(typescript@5.5.4))
version: 2.39.0(vue@3.5.10(typescript@5.5.4))
node-taglib-sharp:
specifier: ^5.2.3
version: 5.2.3
@@ -206,10 +209,10 @@ importers:
version: 5.5.4
unplugin-auto-import:
specifier: ^0.18.2
version: 0.18.2(@vueuse/core@10.11.1(vue@3.4.38(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3)
version: 0.18.2(@vueuse/core@10.11.1(vue@3.5.10(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3)
unplugin-vue-components:
specifier: ^0.27.4
version: 0.27.4(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.4.38(typescript@5.5.4))(webpack-sources@3.2.3)
version: 0.27.4(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.5.10(typescript@5.5.4))(webpack-sources@3.2.3)
vite:
specifier: ^5.4.3
version: 5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0)
@@ -220,11 +223,11 @@ importers:
specifier: ^3.3.0
version: 3.3.0(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0))
vue:
specifier: 3.4.38
version: 3.4.38(typescript@5.5.4)
specifier: 3.5.10
version: 3.5.10(typescript@5.5.4)
vue-router:
specifier: ^4.4.3
version: 4.4.3(vue@3.4.38(typescript@5.5.4))
version: 4.4.3(vue@3.5.10(typescript@5.5.4))
vue-tsc:
specifier: ^2.1.6
version: 2.1.6(typescript@5.5.4)
@@ -1001,23 +1004,23 @@ packages:
'@volar/typescript@2.4.2':
resolution: {integrity: sha512-m2uZduhaHO1SZuagi30OsjI/X1gwkaEAC+9wT/nCNAtJ5FqXEkKvUncHmffG7ESDZPlFFUBK4vJ0D9Hfr+f2EA==}
'@vue/compiler-core@3.4.38':
resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==}
'@vue/compiler-core@3.5.10':
resolution: {integrity: sha512-iXWlk+Cg/ag7gLvY0SfVucU8Kh2CjysYZjhhP70w9qI4MvSox4frrP+vDGvtQuzIcgD8+sxM6lZvCtdxGunTAA==}
'@vue/compiler-core@3.5.3':
resolution: {integrity: sha512-adAfy9boPkP233NTyvLbGEqVuIfK/R0ZsBsIOW4BZNfb4BRpRW41Do1u+ozJpsb+mdoy80O20IzAsHaihRb5qA==}
'@vue/compiler-dom@3.4.38':
resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==}
'@vue/compiler-dom@3.5.10':
resolution: {integrity: sha512-DyxHC6qPcktwYGKOIy3XqnHRrrXyWR2u91AjP+nLkADko380srsC2DC3s7Y1Rk6YfOlxOlvEQKa9XXmLI+W4ZA==}
'@vue/compiler-dom@3.5.3':
resolution: {integrity: sha512-wnzFArg9zpvk/811CDOZOadJRugf1Bgl/TQ3RfV4nKfSPok4hi0w10ziYUQR6LnnBAUlEXYLUfZ71Oj9ds/+QA==}
'@vue/compiler-sfc@3.4.38':
resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==}
'@vue/compiler-sfc@3.5.10':
resolution: {integrity: sha512-to8E1BgpakV7224ZCm8gz1ZRSyjNCAWEplwFMWKlzCdP9DkMKhRRwt0WkCjY7jkzi/Vz3xgbpeig5Pnbly4Tow==}
'@vue/compiler-ssr@3.4.38':
resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==}
'@vue/compiler-ssr@3.5.10':
resolution: {integrity: sha512-hxP4Y3KImqdtyUKXDRSxKSRkSm1H9fCvhojEYrnaoWhE4w/y8vwWhnosJoPPe2AXm5sU7CSbYYAgkt2ZPhDz+A==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
@@ -1033,22 +1036,22 @@ packages:
typescript:
optional: true
'@vue/reactivity@3.4.38':
resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==}
'@vue/reactivity@3.5.10':
resolution: {integrity: sha512-kW08v06F6xPSHhid9DJ9YjOGmwNDOsJJQk0ax21wKaUYzzuJGEuoKNU2Ujux8FLMrP7CFJJKsHhXN9l2WOVi2g==}
'@vue/runtime-core@3.4.38':
resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==}
'@vue/runtime-core@3.5.10':
resolution: {integrity: sha512-9Q86I5Qq3swSkFfzrZ+iqEy7Vla325M7S7xc1NwKnRm/qoi1Dauz0rT6mTMmscqx4qz0EDJ1wjB+A36k7rl8mA==}
'@vue/runtime-dom@3.4.38':
resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==}
'@vue/runtime-dom@3.5.10':
resolution: {integrity: sha512-t3x7ht5qF8ZRi1H4fZqFzyY2j+GTMTDxRheT+i8M9Ph0oepUxoadmbwlFwMoW7RYCpNQLpP2Yx3feKs+fyBdpA==}
'@vue/server-renderer@3.4.38':
resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==}
'@vue/server-renderer@3.5.10':
resolution: {integrity: sha512-IVE97tt2kGKwHNq9yVO0xdh1IvYfZCShvDSy46JIh5OQxP1/EXSpoDqetVmyIzL7CYOWnnmMkVqd7YK2QSWkdw==}
peerDependencies:
vue: 3.4.38
vue: 3.5.10
'@vue/shared@3.4.38':
resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==}
'@vue/shared@3.5.10':
resolution: {integrity: sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ==}
'@vue/shared@3.5.3':
resolution: {integrity: sha512-Jp2v8nylKBT+PlOUjun2Wp/f++TfJVFjshLzNtJDdmFJabJa7noGMncqXRM1vXGX+Yo2V7WykQFNxusSim8SCA==}
@@ -1066,8 +1069,8 @@ packages:
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
engines: {node: '>=10.0.0'}
NeteaseCloudMusicApi@4.22.0:
resolution: {integrity: sha512-xbEA+Bs8TRCRBNVBzuJMvymEOXRXSmhi9/qVtGKkPES14WRz8Kqjkt9+A9K1eU2QEvLrcOBM98aqWVZQh25HZQ==}
NeteaseCloudMusicApi@4.23.0:
resolution: {integrity: sha512-nTvEnrfkTKNWEPCHF/qbNLAQVLb90PaPjdBuht/po6OpGTwX9sWm7RXsqMr7TGw10mkR94CIvx9Ecb+H8gV8yA==}
engines: {node: '>=12'}
hasBin: true
@@ -2964,6 +2967,10 @@ packages:
resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==}
engines: {node: ^10 || ^12 || >=14}
postcss@8.4.47:
resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -3293,6 +3300,10 @@ packages:
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
@@ -3701,13 +3712,22 @@ packages:
peerDependencies:
typescript: '>=5.0.0'
vue-virt-list@1.5.1:
resolution: {integrity: sha512-xinjAEdBAkvMWeqkcxbuDDQmsEauibflDYTw3u1VLCPkOzhtjGvZqubtb131nznBd5RR58LusfvsSn2OL31giA==}
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^2.0.0 || >=3.0.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
vue-virtual-scroller@2.0.0-beta.8:
resolution: {integrity: sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==}
peerDependencies:
vue: ^3.2.0
vue@3.4.38:
resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==}
vue@3.5.10:
resolution: {integrity: sha512-Vy2kmJwHPlouC/tSnIgXVg03SG+9wSqT1xu1Vehc+ChsXsRd7jLkKgMltVEFOzUdBr3uFwBCG+41LJtfAcBRng==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -3851,10 +3871,10 @@ snapshots:
'@applemusic-like-lyrics/lyric@0.2.2': {}
'@applemusic-like-lyrics/vue@0.1.5(@pixi/app@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/filter-blur@7.4.2(@pixi/core@7.4.2))(@pixi/filter-bulge-pinch@5.1.1(@pixi/core@7.4.2))(@pixi/filter-color-matrix@7.4.2(@pixi/core@7.4.2))(@pixi/sprite@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(jss-preset-default@10.10.0)(jss@10.10.0)(vue@3.4.38(typescript@5.5.4))':
'@applemusic-like-lyrics/vue@0.1.5(@pixi/app@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/filter-blur@7.4.2(@pixi/core@7.4.2))(@pixi/filter-bulge-pinch@5.1.1(@pixi/core@7.4.2))(@pixi/filter-color-matrix@7.4.2(@pixi/core@7.4.2))(@pixi/sprite@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(jss-preset-default@10.10.0)(jss@10.10.0)(vue@3.5.10(typescript@5.5.4))':
dependencies:
'@applemusic-like-lyrics/core': 0.1.3(@pixi/app@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/filter-blur@7.4.2(@pixi/core@7.4.2))(@pixi/filter-bulge-pinch@5.1.1(@pixi/core@7.4.2))(@pixi/filter-color-matrix@7.4.2(@pixi/core@7.4.2))(@pixi/sprite@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))(jss-preset-default@10.10.0)(jss@10.10.0)
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
transitivePeerDependencies:
- '@pixi/app'
- '@pixi/core'
@@ -3993,9 +4013,9 @@ snapshots:
dependencies:
css-render: 0.15.14
'@css-render/vue3-ssr@0.15.14(vue@3.4.38(typescript@5.5.4))':
'@css-render/vue3-ssr@0.15.14(vue@3.5.10(typescript@5.5.4))':
dependencies:
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
'@develar/schema-utils@2.6.5':
dependencies:
@@ -4613,10 +4633,10 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-vue@5.1.3(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0))(vue@3.4.38(typescript@5.5.4))':
'@vitejs/plugin-vue@5.1.3(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0))(vue@3.5.10(typescript@5.5.4))':
dependencies:
vite: 5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.33.0)
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
'@volar/language-core@2.4.2':
dependencies:
@@ -4630,10 +4650,10 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.0.8
'@vue/compiler-core@3.4.38':
'@vue/compiler-core@3.5.10':
dependencies:
'@babel/parser': 7.25.6
'@vue/shared': 3.4.38
'@vue/shared': 3.5.10
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.0
@@ -4646,32 +4666,32 @@ snapshots:
estree-walker: 2.0.2
source-map-js: 1.2.0
'@vue/compiler-dom@3.4.38':
'@vue/compiler-dom@3.5.10':
dependencies:
'@vue/compiler-core': 3.4.38
'@vue/shared': 3.4.38
'@vue/compiler-core': 3.5.10
'@vue/shared': 3.5.10
'@vue/compiler-dom@3.5.3':
dependencies:
'@vue/compiler-core': 3.5.3
'@vue/shared': 3.5.3
'@vue/compiler-sfc@3.4.38':
'@vue/compiler-sfc@3.5.10':
dependencies:
'@babel/parser': 7.25.6
'@vue/compiler-core': 3.4.38
'@vue/compiler-dom': 3.4.38
'@vue/compiler-ssr': 3.4.38
'@vue/shared': 3.4.38
'@vue/compiler-core': 3.5.10
'@vue/compiler-dom': 3.5.10
'@vue/compiler-ssr': 3.5.10
'@vue/shared': 3.5.10
estree-walker: 2.0.2
magic-string: 0.30.11
postcss: 8.4.45
postcss: 8.4.47
source-map-js: 1.2.0
'@vue/compiler-ssr@3.4.38':
'@vue/compiler-ssr@3.5.10':
dependencies:
'@vue/compiler-dom': 3.4.38
'@vue/shared': 3.4.38
'@vue/compiler-dom': 3.5.10
'@vue/shared': 3.5.10
'@vue/compiler-vue2@2.7.16':
dependencies:
@@ -4693,54 +4713,54 @@ snapshots:
optionalDependencies:
typescript: 5.5.4
'@vue/reactivity@3.4.38':
'@vue/reactivity@3.5.10':
dependencies:
'@vue/shared': 3.4.38
'@vue/shared': 3.5.10
'@vue/runtime-core@3.4.38':
'@vue/runtime-core@3.5.10':
dependencies:
'@vue/reactivity': 3.4.38
'@vue/shared': 3.4.38
'@vue/reactivity': 3.5.10
'@vue/shared': 3.5.10
'@vue/runtime-dom@3.4.38':
'@vue/runtime-dom@3.5.10':
dependencies:
'@vue/reactivity': 3.4.38
'@vue/runtime-core': 3.4.38
'@vue/shared': 3.4.38
'@vue/reactivity': 3.5.10
'@vue/runtime-core': 3.5.10
'@vue/shared': 3.5.10
csstype: 3.1.3
'@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.5.4))':
'@vue/server-renderer@3.5.10(vue@3.5.10(typescript@5.5.4))':
dependencies:
'@vue/compiler-ssr': 3.4.38
'@vue/shared': 3.4.38
vue: 3.4.38(typescript@5.5.4)
'@vue/compiler-ssr': 3.5.10
'@vue/shared': 3.5.10
vue: 3.5.10(typescript@5.5.4)
'@vue/shared@3.4.38': {}
'@vue/shared@3.5.10': {}
'@vue/shared@3.5.3': {}
'@vueuse/core@10.11.1(vue@3.4.38(typescript@5.5.4))':
'@vueuse/core@10.11.1(vue@3.5.10(typescript@5.5.4))':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.11.1
'@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.5.4))
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
'@vueuse/shared': 10.11.1(vue@3.5.10(typescript@5.5.4))
vue-demi: 0.14.10(vue@3.5.10(typescript@5.5.4))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
'@vueuse/metadata@10.11.1': {}
'@vueuse/shared@10.11.1(vue@3.4.38(typescript@5.5.4))':
'@vueuse/shared@10.11.1(vue@3.5.10(typescript@5.5.4))':
dependencies:
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
vue-demi: 0.14.10(vue@3.5.10(typescript@5.5.4))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
'@xmldom/xmldom@0.8.10': {}
NeteaseCloudMusicApi@4.22.0:
NeteaseCloudMusicApi@4.23.0:
dependencies:
axios: 1.7.7
crypto-js: 4.2.0
@@ -6627,10 +6647,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
naive-ui@2.39.0(vue@3.4.38(typescript@5.5.4)):
naive-ui@2.39.0(vue@3.5.10(typescript@5.5.4)):
dependencies:
'@css-render/plugin-bem': 0.15.14(css-render@0.15.14)
'@css-render/vue3-ssr': 0.15.14(vue@3.4.38(typescript@5.5.4))
'@css-render/vue3-ssr': 0.15.14(vue@3.5.10(typescript@5.5.4))
'@types/katex': 0.16.7
'@types/lodash': 4.17.7
'@types/lodash-es': 4.17.12
@@ -6645,10 +6665,10 @@ snapshots:
lodash-es: 4.17.21
seemly: 0.3.8
treemate: 0.3.11
vdirs: 0.1.8(vue@3.4.38(typescript@5.5.4))
vooks: 0.2.12(vue@3.4.38(typescript@5.5.4))
vue: 3.4.38(typescript@5.5.4)
vueuc: 0.4.58(vue@3.4.38(typescript@5.5.4))
vdirs: 0.1.8(vue@3.5.10(typescript@5.5.4))
vooks: 0.2.12(vue@3.5.10(typescript@5.5.4))
vue: 3.5.10(typescript@5.5.4)
vueuc: 0.4.58(vue@3.5.10(typescript@5.5.4))
nanoid@3.3.7: {}
@@ -6830,15 +6850,15 @@ snapshots:
picomatch@2.3.1: {}
pinia-plugin-persistedstate@3.2.3(pinia@2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))):
pinia-plugin-persistedstate@3.2.3(pinia@2.2.2(typescript@5.5.4)(vue@3.5.10(typescript@5.5.4))):
dependencies:
pinia: 2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
pinia: 2.2.2(typescript@5.5.4)(vue@3.5.10(typescript@5.5.4))
pinia@2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)):
pinia@2.2.2(typescript@5.5.4)(vue@3.5.10(typescript@5.5.4)):
dependencies:
'@vue/devtools-api': 6.6.3
vue: 3.4.38(typescript@5.5.4)
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
vue: 3.5.10(typescript@5.5.4)
vue-demi: 0.14.10(vue@3.5.10(typescript@5.5.4))
optionalDependencies:
typescript: 5.5.4
@@ -6902,6 +6922,12 @@ snapshots:
picocolors: 1.1.0
source-map-js: 1.2.0
postcss@8.4.47:
dependencies:
nanoid: 3.3.7
picocolors: 1.1.0
source-map-js: 1.2.1
prelude-ls@1.2.1: {}
prettier@3.3.3: {}
@@ -7263,6 +7289,8 @@ snapshots:
source-map-js@1.2.0: {}
source-map-js@1.2.1: {}
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
@@ -7493,7 +7521,7 @@ snapshots:
unpipe@1.0.0: {}
unplugin-auto-import@0.18.2(@vueuse/core@10.11.1(vue@3.4.38(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3):
unplugin-auto-import@0.18.2(@vueuse/core@10.11.1(vue@3.5.10(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.0(rollup@4.21.2)
@@ -7504,12 +7532,12 @@ snapshots:
unimport: 3.11.1(rollup@4.21.2)(webpack-sources@3.2.3)
unplugin: 1.13.1(webpack-sources@3.2.3)
optionalDependencies:
'@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4))
'@vueuse/core': 10.11.1(vue@3.5.10(typescript@5.5.4))
transitivePeerDependencies:
- rollup
- webpack-sources
unplugin-vue-components@0.27.4(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.4.38(typescript@5.5.4))(webpack-sources@3.2.3):
unplugin-vue-components@0.27.4(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.5.10(typescript@5.5.4))(webpack-sources@3.2.3):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.0(rollup@4.21.2)
@@ -7521,7 +7549,7 @@ snapshots:
minimatch: 9.0.5
mlly: 1.7.1
unplugin: 1.13.1(webpack-sources@3.2.3)
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
optionalDependencies:
'@babel/parser': 7.25.6
transitivePeerDependencies:
@@ -7570,10 +7598,10 @@ snapshots:
vary@1.1.2: {}
vdirs@0.1.8(vue@3.4.38(typescript@5.5.4)):
vdirs@0.1.8(vue@3.5.10(typescript@5.5.4)):
dependencies:
evtd: 0.2.4
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
verror@1.10.0:
dependencies:
@@ -7612,16 +7640,16 @@ snapshots:
sass: 1.78.0
terser: 5.33.0
vooks@0.2.12(vue@3.4.38(typescript@5.5.4)):
vooks@0.2.12(vue@3.5.10(typescript@5.5.4)):
dependencies:
evtd: 0.2.4
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
vscode-uri@3.0.8: {}
vue-demi@0.14.10(vue@3.4.38(typescript@5.5.4)):
vue-demi@0.14.10(vue@3.5.10(typescript@5.5.4)):
dependencies:
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
vue-eslint-parser@9.4.3(eslint@8.57.0):
dependencies:
@@ -7636,18 +7664,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
vue-observe-visibility@2.0.0-alpha.1(vue@3.4.38(typescript@5.5.4)):
vue-observe-visibility@2.0.0-alpha.1(vue@3.5.10(typescript@5.5.4)):
dependencies:
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
vue-resize@2.0.0-alpha.1(vue@3.4.38(typescript@5.5.4)):
vue-resize@2.0.0-alpha.1(vue@3.5.10(typescript@5.5.4)):
dependencies:
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
vue-router@4.4.3(vue@3.4.38(typescript@5.5.4)):
vue-router@4.4.3(vue@3.5.10(typescript@5.5.4)):
dependencies:
'@vue/devtools-api': 6.6.3
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.10(typescript@5.5.4)
vue-tsc@2.1.6(typescript@5.5.4):
dependencies:
@@ -7656,33 +7684,38 @@ snapshots:
semver: 7.6.3
typescript: 5.5.4
vue-virtual-scroller@2.0.0-beta.8(vue@3.4.38(typescript@5.5.4)):
vue-virt-list@1.5.1(vue@3.5.10(typescript@5.5.4)):
dependencies:
vue: 3.5.10(typescript@5.5.4)
vue-demi: 0.14.10(vue@3.5.10(typescript@5.5.4))
vue-virtual-scroller@2.0.0-beta.8(vue@3.5.10(typescript@5.5.4)):
dependencies:
mitt: 2.1.0
vue: 3.4.38(typescript@5.5.4)
vue-observe-visibility: 2.0.0-alpha.1(vue@3.4.38(typescript@5.5.4))
vue-resize: 2.0.0-alpha.1(vue@3.4.38(typescript@5.5.4))
vue: 3.5.10(typescript@5.5.4)
vue-observe-visibility: 2.0.0-alpha.1(vue@3.5.10(typescript@5.5.4))
vue-resize: 2.0.0-alpha.1(vue@3.5.10(typescript@5.5.4))
vue@3.4.38(typescript@5.5.4):
vue@3.5.10(typescript@5.5.4):
dependencies:
'@vue/compiler-dom': 3.4.38
'@vue/compiler-sfc': 3.4.38
'@vue/runtime-dom': 3.4.38
'@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.5.4))
'@vue/shared': 3.4.38
'@vue/compiler-dom': 3.5.10
'@vue/compiler-sfc': 3.5.10
'@vue/runtime-dom': 3.5.10
'@vue/server-renderer': 3.5.10(vue@3.5.10(typescript@5.5.4))
'@vue/shared': 3.5.10
optionalDependencies:
typescript: 5.5.4
vueuc@0.4.58(vue@3.4.38(typescript@5.5.4)):
vueuc@0.4.58(vue@3.5.10(typescript@5.5.4)):
dependencies:
'@css-render/vue3-ssr': 0.15.14(vue@3.4.38(typescript@5.5.4))
'@css-render/vue3-ssr': 0.15.14(vue@3.5.10(typescript@5.5.4))
'@juggle/resize-observer': 3.4.0
css-render: 0.15.14
evtd: 0.2.4
seemly: 0.3.8
vdirs: 0.1.8(vue@3.4.38(typescript@5.5.4))
vooks: 0.2.12(vue@3.4.38(typescript@5.5.4))
vue: 3.4.38(typescript@5.5.4)
vdirs: 0.1.8(vue@3.5.10(typescript@5.5.4))
vooks: 0.2.12(vue@3.5.10(typescript@5.5.4))
vue: 3.5.10(typescript@5.5.4)
webpack-sources@3.2.3:
optional: true

View File

@@ -0,0 +1,396 @@
<template>
<div :class="['song-card', { play: musicStore.playSong.id === song.id }]">
<!-- 序号 -->
<div class="num" @dblclick.stop>
<n-text v-if="musicStore.playSong.id !== song.id" depth="3">
{{ index + 1 }}
</n-text>
<SvgIcon v-else :size="22" name="Music" />
<!-- 播放暂停 -->
<SvgIcon
:size="28"
:name="statusStore.playStatus ? 'Pause' : 'Play'"
class="status"
@click="player.playOrPause()"
/>
<!-- 播放 -->
<SvgIcon :size="28" name="Play" class="play" @click="player.addNextSong(song, true)" />
</div>
<!-- 标题 -->
<div class="title">
<!-- 封面 -->
<s-image
v-if="!hiddenCover"
:key="song.cover"
:src="song.path ? song.cover : song.coverSize?.s || song.cover"
class="cover"
@update:show.once="localCover"
/>
<!-- 信息 -->
<div class="info">
<!-- 名称 -->
<div class="name">
<n-ellipsis
:line-clamp="1"
:tooltip="{
placement: 'top',
width: 'trigger',
}"
class="name-text"
>
{{ song?.name || "未知曲目" }}
</n-ellipsis>
<!-- 音质 -->
<n-tag
v-if="song?.path && song?.quality"
:bordered="false"
:type="song.quality === 'Hi-Res' ? 'warning' : 'info'"
class="quality"
round
>
{{ song.quality }}
</n-tag>
<!-- 特权 -->
<n-tag v-if="song.originCoverType === 1" :bordered="false" type="primary" round>
</n-tag>
<n-tag v-if="song.free === 1" :bordered="false" type="error" round> VIP </n-tag>
<n-tag v-if="song.free === 4" :bordered="false" type="error" round> EP </n-tag>
<!-- 云盘 -->
<n-tag v-if="song?.pc" :bordered="false" class="cloud" type="info" round>
<template #icon>
<SvgIcon name="Cloud" />
</template>
</n-tag>
<!-- MV -->
<n-tag
v-if="song?.mv"
:bordered="false"
class="mv"
type="warning"
round
@click.stop="
router.push({
name: 'video',
query: { id: song.mv },
})
"
>
MV
</n-tag>
</div>
<!-- 歌手 -->
<div v-if="Array.isArray(song.artists)" class="artists text-hidden">
<n-text
v-for="ar in song.artists"
:key="ar.id"
class="ar"
@click="openJumpArtist(song.artists)"
>
{{ ar.name }}
</n-text>
</div>
<div v-else-if="song.type === 'radio'" class="artists">
<n-text class="ar"> 电台节目 </n-text>
</div>
<div v-else class="artists text-hidden" @click="openJumpArtist(song.artists)">
<n-text class="ar"> {{ song.artists || "未知艺术家" }} </n-text>
</div>
<!-- 别名 -->
<n-text v-if="song.alia" class="alia" depth="3">{{ song.alia }}</n-text>
</div>
</div>
<!-- 专辑 -->
<div v-if="song.type !== 'radio' && !hiddenAlbum" class="album text-hidden">
<n-text
v-if="isObject(song.album)"
class="album-text"
@click="
router.push({
name: 'album',
query: { id: song.album?.id },
})
"
>
{{ song.album?.name || "未知专辑" }}
</n-text>
<n-text v-else class="album-text">
{{ song.album || "未知专辑" }}
</n-text>
</div>
<!-- 操作 -->
<div v-if="song.type !== 'radio'" class="actions" @click.stop @dblclick.stop>
<!-- 喜欢歌曲 -->
<SvgIcon
:name="dataStore.isLikeSong(song.id) ? 'Favorite' : 'FavoriteBorder'"
:size="20"
@click.stop="toLikeSong(song, !dataStore.isLikeSong(song.id))"
@delclick.stop
/>
</div>
<!-- 更新日期 -->
<n-text v-if="song.type === 'radio'" class="meta date" depth="3">
{{ formatTimestamp(song.updateTime) }}
</n-text>
<!-- 播放量 -->
<n-text v-if="song.type === 'radio'" class="meta" depth="3">
{{ formatNumber(song.playCount || 0) }}
</n-text>
<!-- 时长 -->
<n-text class="meta" depth="3">{{ msToTime(song.duration) }}</n-text>
<!-- 大小 -->
<n-text v-if="song.path && song.size && !hiddenSize" class="meta size" depth="3">
{{ song.size }}M
</n-text>
</div>
</template>
<script setup lang="ts">
import type { SongType } from "@/types/main";
import { useStatusStore, useMusicStore, useDataStore } from "@/stores";
import player from "@/utils/player";
import { formatNumber, isElectron } from "@/utils/helper";
import blob from "@/utils/blob";
import { openJumpArtist } from "@/utils/modal";
import { isObject } from "lodash-es";
import { toLikeSong } from "@/utils/auth";
import { formatTimestamp, msToTime } from "@/utils/time";
const props = defineProps<{
// 歌曲
song: SongType;
// 索引
index: number;
// 隐藏信息
hiddenCover?: boolean;
hiddenAlbum?: boolean;
hiddenSize?: boolean;
}>();
const router = useRouter();
const dataStore = useDataStore();
const musicStore = useMusicStore();
const statusStore = useStatusStore();
// 加载本地歌曲封面
const localCover = async (show: boolean) => {
if (!isElectron || !show || !props.song.path) return;
if (props.song.cover || props.song.cover === "/images/song.jpg?assest") return;
// 获取封面
const coverData = await window.electron.ipcRenderer.invoke("get-music-cover", props.song.path);
if (!coverData) return;
const { data, format } = coverData;
const blobURL = blob.createBlobURL(data, format, props.song.path);
if (blobURL) props.song.cover = blobURL;
};
</script>
<style lang="scss" scoped>
.song-card {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 100%;
flex: 1;
border-radius: 12px;
padding: 8px 12px;
border: 2px solid rgba(var(--primary), 0.12);
background-color: var(--surface-container-hex);
transition:
background-color 0.3s var(--n-bezier),
border-color 0.3s var(--n-bezier);
cursor: pointer;
.num {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
min-width: 40px;
font-weight: bold;
margin-right: 12px;
.n-icon {
transition:
opacity 0.3s,
transform 0.3s;
}
.status,
.play {
position: absolute;
opacity: 0;
transform: scale(0.8);
&:active {
opacity: 0.6 !important;
}
}
}
.title {
flex: 1;
display: flex;
align-items: center;
padding: 4px 20px 4px 0;
.cover {
width: 50px;
height: 50px;
min-width: 50px;
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.info {
display: flex;
flex-direction: column;
.name {
display: flex;
flex-direction: row;
align-items: center;
font-size: 16px;
:deep(.name-text) {
margin-right: 6px;
}
.n-tag {
--n-height: 20px;
font-size: 12px;
cursor: pointer;
margin-right: 6px;
pointer-events: none;
&:last-child {
margin-right: 0;
}
}
.quality {
font-size: 10px;
}
.cloud {
padding: 0 10px;
align-items: center;
justify-content: center;
:deep(.n-tag__icon) {
margin-right: 0;
width: 100%;
}
.n-icon {
font-size: 12px;
color: var(--n-text-color);
}
}
.mv {
pointer-events: auto;
}
}
.artists {
margin-top: 2px;
font-size: 13px;
.ar {
display: inline-flex;
transition: opacity 0.3s;
opacity: 0.6;
cursor: pointer;
&::after {
content: "/";
margin: 0 4px;
}
&:last-child {
&::after {
display: none;
}
}
&:hover {
opacity: 0.8;
}
}
}
.alia {
margin-top: 2px;
font-size: 12px;
opacity: 0.8;
}
}
.sort {
margin-left: 6px;
&::after {
content: " )";
}
&::before {
content: "( ";
}
}
}
.album {
flex: 1;
line-clamp: 2;
-webkit-line-clamp: 2;
padding-right: 20px;
&:hover {
.album-text {
color: var(--primary-hex);
}
}
}
.actions {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
.n-icon {
transition: transform 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.15);
}
&:active {
transform: scale(1);
}
}
}
.meta {
width: 50px;
font-size: 13px;
text-align: center;
&.size {
width: 60px;
}
&.date {
width: 80px;
}
}
&.play {
border-color: rgba(var(--primary), 0.58);
background-color: rgba(var(--primary), 0.28);
}
&.header {
border: none;
background-color: transparent;
.n-text {
opacity: 0.6;
}
.title {
position: relative;
padding: 0 20px 0 0;
&.has-sort {
&::after {
content: "";
position: absolute;
opacity: 0;
top: 0;
left: -8px;
width: 100%;
height: 100%;
border-radius: 8px;
background-color: rgba(var(--primary), 0.08);
transition: opacity 0.3s;
}
&:hover {
&::after {
opacity: 1;
}
}
}
}
}
}
</style>

View File

@@ -37,6 +37,6 @@ onMounted(() => loadSVG(props.name));
margin: 0;
padding: 0;
// transition: all 0.3s;
color: var(--primary-hex);
// color: var(--primary-hex);
}
</style>

View File

@@ -10,32 +10,17 @@
>
<!-- 封面 -->
<div class="cover">
<n-image
<s-image
:key="item.cover"
:src="
type === 'video' ? `${item.cover}?param=464y260` : item.coverSize?.m || item.cover
"
:default-src="
type !== 'video' ? '/images/album.jpg?assest' : '/images/video.jpg?assest'
"
class="cover-img"
preview-disabled
lazy
@load="coverLoaded"
>
<template #placeholder>
<div class="cover-loading">
<img
v-if="type !== 'video'"
src="/images/album.jpg?assest"
class="loading-img"
alt="loading-img"
once
/>
<img
v-else
src="/images/video.jpg?assest"
class="loading-img"
alt="loading-img"
/>
</div>
</template>
</n-image>
<template v-if="item.playCount">
<!-- 遮罩 -->
<div v-if="type !== 'album'" class="cover-mask" />
@@ -129,7 +114,7 @@
<script setup lang="ts">
import type { CoverType, SongType } from "@/types/main";
import { albumDetail } from "@/api/album";
import { coverLoaded, formatNumber } from "@/utils/helper";
import { formatNumber } from "@/utils/helper";
import { useMusicStore, useStatusStore } from "@/stores";
import { debounce } from "lodash-es";
import { formatSongsList } from "@/utils/format";
@@ -255,7 +240,7 @@ const getListData = async (id: number): Promise<SongType[]> => {
:deep(img) {
width: 100%;
height: 100%;
opacity: 0;
// opacity: 0;
transition: opacity 0.35s ease-in-out;
}
.cover-img {

View File

@@ -82,22 +82,13 @@
<!-- 标题 -->
<div class="title">
<!-- 封面 -->
<n-image
<s-image
v-if="!hiddenCover"
:key="item.cover"
:src="item.path ? item.cover : item.coverSize?.s || item.cover"
fallback-src="/images/song.jpg?assest"
class="cover"
preview-disabled
v-visible="(show: boolean) => localCover(show, item?.path, index)"
@load="coverLoaded"
>
<template #placeholder>
<div class="cover-loading">
<img src="/images/song.jpg?assest" class="loading-img" alt="loading-img" />
</div>
</template>
</n-image>
@update:show.once="(show: boolean) => localCover(show, item?.path, index)"
/>
<!-- 信息 -->
<div class="info">
<!-- 名称 -->
@@ -392,14 +383,6 @@ const scrollTo = (index: number) => {
scrollerRef.value?.scrollToItem(index);
};
// 封面加载完成
const coverLoaded = (e: Event) => {
const target = e.target as HTMLElement | null;
if (target && target.nodeType === Node.ELEMENT_NODE) {
target.style.opacity = "1";
}
};
// 加载本地歌曲封面
const localCover = async (show: boolean, path: string, index: number) => {
if (!isElectron || !show || !path) return;
@@ -527,12 +510,6 @@ onActivated(() => {
align-items: center;
justify-content: center;
overflow: hidden;
:deep(img) {
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 0.35s ease-in-out;
}
}
.info {
display: flex;

View File

@@ -5,7 +5,7 @@
<n-scrollbar class="scrollbar">
<n-flex class="date" justify="center">
<n-tag round>生效日期2024 7 16 </n-tag>
<n-tag type="warning" round>更新日期2024 7 16 </n-tag>
<n-tag type="warning" round>更新日期2024 9 28 </n-tag>
</n-flex>
<n-p>
欢迎使用 SPlayer以下简称本软件本软件是一个本地音乐播放软件可能会调用第三方 API

View File

@@ -29,6 +29,17 @@
/>
</div>
</Transition>
<!-- 独立歌词 -->
<Transition name="fade" mode="out-in">
<div
v-if="isShowComment && !statusStore.pureLyricMode"
:key="instantLyrics.content"
class="lrc-instant"
>
<span class="lrc">{{ instantLyrics.content }}</span>
<span v-if="instantLyrics.tran" class="lrc-tran">{{ instantLyrics.tran }}</span>
</div>
</Transition>
<!-- 菜单 -->
<PlayerMenu @mouseenter.stop="stopHide" @mouseleave.stop="playerMove" />
<!-- 主内容 -->
@@ -121,6 +132,15 @@ const playerContentKey = computed(() => {
// 是否显示评论
const isShowComment = computed(() => !musicStore.playSong.path && statusStore.showPlayerComment);
// 当前实时歌词
const instantLyrics = computed(() => {
const isYrc = musicStore.songLyric.yrcData?.length && settingStore.showYrc;
const content = isYrc
? musicStore.songLyric.yrcData[statusStore.lyricIndex]
: musicStore.songLyric.lrcData[statusStore.lyricIndex];
return { content: content?.content, tran: content?.tran };
});
// 播放器主色
const mainColor = computed(() => {
const mainColor = statusStore.songCoverTheme?.main;
@@ -222,6 +242,23 @@ onBeforeUnmount(() => {
}
}
}
.lrc-instant {
position: absolute;
top: 0;
height: 80px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
pointer-events: none;
.lrc {
font-size: 18px;
}
.lrc-tran {
font-size: 14px;
opacity: 0.6;
}
}
.player-content {
display: flex;
flex-direction: row;

View File

@@ -56,7 +56,7 @@
:key="textIndex"
:class="{
'content-text': true,
'content-long': text.duration >= 1.5,
'content-long': text.duration >= 1.5 && playSeek <= text.endTime,
'end-with-space': text.endsWithSpace,
}"
>
@@ -149,8 +149,8 @@
import type { LyricContentType } from "@/types/main";
import { NScrollbar } from "naive-ui";
import { useMusicStore, useSettingStore, useStatusStore } from "@/stores";
import player from "@/utils/player";
import { openSetting } from "@/utils/modal";
import player from "@/utils/player";
const musicStore = useMusicStore();
const statusStore = useStatusStore();
@@ -364,7 +364,11 @@ onBeforeUnmount(() => {
);
-webkit-mask-size: 220% 100%;
-webkit-mask-repeat: no-repeat;
transition: opacity 0.3s !important;
transition:
opacity 0.3s,
filter 0.5s,
margin 0.3s,
padding 0.3s !important;
}
&.end-with-space {
margin-right: 12px;

View File

@@ -0,0 +1,66 @@
<!-- 图片组件 -->
<template>
<div ref="imgRef" class="s-image">
<Transition name="fade" mode="out-in">
<img :key="imgSrc" :src="imgSrc" :alt="alt || 'image'" />
</Transition>
<img v-if="!isLoaded" class="loading" :src="src" @load="imageLoaded" />
</div>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{
src: string | undefined;
defaultSrc?: string;
alt?: string;
}>(),
{
defaultSrc: "/images/song.jpg?assest",
},
);
const emit = defineEmits<{
// 加载完成
load: [e: Event];
// 可视状态变化
"update:show": [show: boolean];
}>();
// 图片数据
const imgRef = ref<HTMLImageElement>();
const imgSrc = ref<string>(props.defaultSrc);
// 是否加载完成
const isLoaded = ref<boolean>(false);
// 是否可视
const isCanLook = useElementVisibility(imgRef);
// 图片加载完成
const imageLoaded = (e: Event) => {
isLoaded.value = true;
// 加载完成
emit("load", e);
};
// 可视状态变化
watchOnce(isCanLook, (show) => {
if (show) imgSrc.value = props.src || props.defaultSrc;
emit("update:show", show);
});
</script>
<style lang="scss" scoped>
.s-image {
position: relative;
img {
width: 100%;
height: 100%;
}
.loading {
position: absolute;
opacity: 0;
}
}
</style>

3
src/types/main.d.ts vendored
View File

@@ -1,5 +1,4 @@
import { sortOptions } from "@/utils/helper";
import { songLevelData } from "@/utils/meta";
import { songLevelData, sortOptions } from "@/utils/meta";
export type MetaData = {
id: number;

View File

@@ -53,29 +53,6 @@ export const throttleDirective = {
};
// 元素可见
// export const visibleDirective = {
// mounted(el: HTMLElement, binding: any) {
// const { modifiers, value } = binding;
// const { stop } = useIntersectionObserver(
// el,
// ([entry]) => {
// if (entry.isIntersecting) {
// el.classList.add("visible");
// // 触发回调
// if (typeof value === "function") value(entry);
// // 只触发一次
// if (modifiers.once) stop();
// } else if (!modifiers.once) {
// el.classList.remove("visible");
// }
// },
// {
// threshold: 0.1,
// },
// );
// },
// };
export const visibleDirective = {
mounted(el: HTMLElement, binding: any) {
const { modifiers, value } = binding;

View File

@@ -72,7 +72,7 @@ export const sortOptions = {
timeDown: { name: "时长降序", show: "all", icon: renderIcon("SortClockDown") },
dateUp: { name: "日期升序", show: "radio", icon: renderIcon("SortDateUp") },
dateDown: { name: "日期降序", show: "radio", icon: renderIcon("SortDateDown") },
};
} as const;
// 自定义图片工具栏
export const renderToolbar = ({ nodes }: ImageRenderToolbarProps) => {

View File

@@ -69,10 +69,12 @@ class Player {
/**
* 清理播放器
*/
private cleanPlayer() {
private async cleanPlayer() {
Howler.unload();
// this.player?.stop();
// this.player?.unload();
// 延时防止 bug
await sleep(50);
}
/**
* 获取当前播放歌曲
@@ -201,7 +203,7 @@ class Player {
* @param src 播放地址
* @param autoPlay 是否自动播放
*/
private createPlayer(src: string, autoPlay: boolean = true) {
private async createPlayer(src: string, autoPlay: boolean = true) {
// 获取数据
const dataStore = useDataStore();
const musicStore = useMusicStore();
@@ -210,7 +212,7 @@ class Player {
// 播放信息
const { id, path, type } = musicStore.playSong;
// 清理播放器
this.cleanPlayer();
await this.cleanPlayer();
// 禁用自动解锁
Howler.autoUnlock = false;
// 创建播放器
@@ -530,7 +532,7 @@ class Player {
statusStore.playLoading = true;
// 本地歌曲
if (path) {
this.createPlayer(path, autoPlay);
await this.createPlayer(path, autoPlay);
// 获取歌曲元信息
await this.parseLocalMusicInfo(path);
}
@@ -542,7 +544,7 @@ class Player {
// 正常播放地址
if (url) {
statusStore.playUblock = false;
this.createPlayer(url, autoPlay);
await this.createPlayer(url, autoPlay);
}
// 尝试解灰
else if (isElectron && type !== "radio" && settingStore.useSongUnlock) {
@@ -550,7 +552,7 @@ class Player {
if (unlockUrl) {
statusStore.playUblock = true;
console.log("🎼 Song unlock successfully:", unlockUrl);
this.createPlayer(unlockUrl, autoPlay);
await this.createPlayer(unlockUrl, autoPlay);
} else {
statusStore.playUblock = false;
// 是否为最后一首
@@ -850,7 +852,7 @@ class Player {
statusStore.$patch({ playIndex, lyricIndex: -1 });
// 清理并播放
await this.resetStatus();
this.initPlayer();
await this.initPlayer();
}
} else {
const playIndex =
@@ -858,7 +860,7 @@ class Player {
statusStore.$patch({ playIndex, lyricIndex: -1 });
// 清理并播放
await this.resetStatus();
this.initPlayer();
await this.initPlayer();
}
// 更改播放歌单
musicStore.playPlaylistId = pid ?? 0;
@@ -952,7 +954,7 @@ class Player {
const statusStore = useStatusStore();
// 停止播放
await this.resetStatus();
this.cleanPlayer();
await this.cleanPlayer();
// 清空数据
statusStore.$patch({
playListShow: false,

View File

@@ -43,6 +43,14 @@
hiddenCover
hiddenSize
/>
<!-- <SongListNew
v-if="dataStore.historyList.length > 0"
:data="dataStore.historyList"
:loading="true"
hiddenPadding
hiddenCover
hiddenSize
/> -->
<n-empty
v-else
description="暂无记录,快去播放一些歌曲吧"

View File

@@ -83,7 +83,7 @@ const dailySongsTitle = computed(() => {
const day = new Date().getDate();
return h("div", { class: "date" }, [
h("div", { class: "date-icon" }, [
h(SvgIcon, { name: "Calendar-Empty", size: 30 }),
h(SvgIcon, { name: "Calendar-Empty", size: 30, depth: 2 }),
h(NText, null, () => day),
]),
h(NText, { class: "name" }, () => ["每日推荐"]),