mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
✨ feat: 移动端基础适配
- 修复未登录时无法使用本地歌曲 - 修复部分样式异常
This commit is contained in:
@@ -2,3 +2,5 @@ node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/add.yml
vendored
2
.github/ISSUE_TEMPLATE/add.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: 添加功能
|
||||
description: 请填写希望添加的功能的具体信息
|
||||
title: "添加功能"
|
||||
title: 【添加功能】请填写标题
|
||||
labels: [add]
|
||||
body:
|
||||
- type: input
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -1,5 +1,6 @@
|
||||
name: 遇到问题
|
||||
description: 关于使用过程中遇到的问题
|
||||
title: 【遇到问题】请填写标题
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: input
|
||||
|
||||
@@ -4,3 +4,5 @@ pnpm-lock.yaml
|
||||
LICENSE.md
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# 是否使用单引号而不是双引号
|
||||
singleQuote: false
|
||||
# 是否在语句末尾使用分号
|
||||
semi: true
|
||||
# 每行的最大打印宽度
|
||||
printWidth: 100
|
||||
# 是否在对象和数组的末尾加上逗号
|
||||
trailingComma: all
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
> - 感谢您的尊重与理解
|
||||
|
||||
- 本项目采用 [Vue 3](https://cn.vuejs.org/) 全家桶和 [Naïve UI](https://www.naiveui.com/) 组件库及 [Electron](https://www.electronjs.org/zh/docs/latest/) 开发
|
||||
- 支持网页端与客户端,由于设备有限,目前仅适配 `Win`,其他平台可自行构建
|
||||
- ~~仅对移动端做了基础适配,**不保证功能全部可用**~~
|
||||
- 欢迎各位大佬指点和 `Star` 哦 😍
|
||||
- 支持网页端与客户端,由于设备有限,目前仅适配 `Win`,其他平台可自行解决兼容性后进行构建
|
||||
- 仅对移动端做了基础适配,**不保证功能全部可用**
|
||||
> 请注意,本程序不打算开发移动端,也不会对移动端进行完美适配,仅保证基础可用性
|
||||
- 欢迎各位大佬 `Star` 😍
|
||||
|
||||
## 👀 Demo
|
||||
|
||||
@@ -33,7 +34,7 @@
|
||||
- 支持手机号登录
|
||||
- 自动进行每日签到及云贝签到
|
||||
- 封面主题色自适应
|
||||
- 本地歌曲管理及分类 ~~以及音乐标签编辑~~
|
||||
- 本地歌曲管理及分类(建议先使用 [音乐标签](https://www.cnblogs.com/vinlxc/p/11347744.html) 进行匹配后再使用)
|
||||
- **支持播放部分无版权歌曲(可能会与原曲不匹配,客户端独占功能)**
|
||||
- 下载歌曲(最高支持 Hi-Res)
|
||||
- 新建歌单及歌单编辑
|
||||
|
||||
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -40,6 +40,7 @@ declare module 'vue' {
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NFlex: typeof import('naive-ui')['NFlex']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
@@ -99,6 +100,7 @@ declare module 'vue' {
|
||||
SearchInp: typeof import('./src/components/Search/SearchInp.vue')['default']
|
||||
SearchSuggestions: typeof import('./src/components/Search/SearchSuggestions.vue')['default']
|
||||
SongList: typeof import('./src/components/List/SongList.vue')['default']
|
||||
SongListDrawer: typeof import('./src/components/List/SongListDrawer.vue')['default']
|
||||
SongListDropdown: typeof import('./src/components/List/SongListDropdown.vue')['default']
|
||||
SpecialCover: typeof import('./src/components/Cover/SpecialCover.vue')['default']
|
||||
SpecialCoverCard: typeof import('./src/components/Cover/SpecialCoverCard.vue')['default']
|
||||
|
||||
10
package.json
10
package.json
@@ -27,7 +27,7 @@
|
||||
"@electron-toolkit/utils": "^2.0.1",
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
"NeteaseCloudMusicApi": "^4.14.0",
|
||||
"axios": "^1.6.3",
|
||||
"axios": "^1.6.5",
|
||||
"colorthief": "^2.4.0",
|
||||
"electron-dl": "^3.5.1",
|
||||
"electron-store": "^8.1.0",
|
||||
@@ -52,21 +52,21 @@
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"ajv": "^8.12.0",
|
||||
"electron": "^27.2.0",
|
||||
"electron": "^27.2.1",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron-log": "^5.0.3",
|
||||
"electron-vite": "^1.0.29",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"naive-ui": "^2.36.0",
|
||||
"naive-ui": "^2.37.0",
|
||||
"prettier": "^3.1.1",
|
||||
"sass": "^1.69.6",
|
||||
"sass": "^1.69.7",
|
||||
"terser": "^5.26.0",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite": "^4.5.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-pwa": "^0.17.4",
|
||||
"vue": "^3.4.3"
|
||||
"vue": "3.4.4"
|
||||
}
|
||||
}
|
||||
|
||||
261
pnpm-lock.yaml
generated
261
pnpm-lock.yaml
generated
@@ -7,10 +7,10 @@ settings:
|
||||
dependencies:
|
||||
'@electron-toolkit/preload':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(electron@27.2.0)
|
||||
version: 2.0.0(electron@27.2.1)
|
||||
'@electron-toolkit/utils':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1(electron@27.2.0)
|
||||
version: 2.0.1(electron@27.2.1)
|
||||
'@material/material-color-utilities':
|
||||
specifier: ^0.2.7
|
||||
version: 0.2.7
|
||||
@@ -18,8 +18,8 @@ dependencies:
|
||||
specifier: ^4.14.0
|
||||
version: 4.14.0
|
||||
axios:
|
||||
specifier: ^1.6.3
|
||||
version: 1.6.3
|
||||
specifier: ^1.6.5
|
||||
version: 1.6.5
|
||||
colorthief:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0
|
||||
@@ -55,7 +55,7 @@ dependencies:
|
||||
version: 0.2.6
|
||||
pinia:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.7(vue@3.4.3)
|
||||
version: 2.1.7(vue@3.4.4)
|
||||
pinia-plugin-persistedstate:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1(pinia@2.1.7)
|
||||
@@ -67,7 +67,7 @@ dependencies:
|
||||
version: 6.0.2
|
||||
vue-router:
|
||||
specifier: ^4.2.5
|
||||
version: 4.2.5(vue@3.4.3)
|
||||
version: 4.2.5(vue@3.4.4)
|
||||
vue-slider-component:
|
||||
specifier: 4.1.0-beta.7
|
||||
version: 4.1.0-beta.7
|
||||
@@ -81,7 +81,7 @@ devDependencies:
|
||||
version: 1.6.1
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^4.6.2
|
||||
version: 4.6.2(vite@4.5.1)(vue@3.4.3)
|
||||
version: 4.6.2(vite@4.5.1)(vue@3.4.4)
|
||||
'@vue/eslint-config-prettier':
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0(eslint@8.56.0)(prettier@3.1.1)
|
||||
@@ -89,8 +89,8 @@ devDependencies:
|
||||
specifier: ^8.12.0
|
||||
version: 8.12.0
|
||||
electron:
|
||||
specifier: ^27.2.0
|
||||
version: 27.2.0
|
||||
specifier: ^27.2.1
|
||||
version: 27.2.1
|
||||
electron-builder:
|
||||
specifier: ^24.9.1
|
||||
version: 24.9.1
|
||||
@@ -107,14 +107,14 @@ devDependencies:
|
||||
specifier: ^9.19.2
|
||||
version: 9.19.2(eslint@8.56.0)
|
||||
naive-ui:
|
||||
specifier: ^2.36.0
|
||||
version: 2.36.0(vue@3.4.3)
|
||||
specifier: ^2.37.0
|
||||
version: 2.37.0(vue@3.4.4)
|
||||
prettier:
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1
|
||||
sass:
|
||||
specifier: ^1.69.6
|
||||
version: 1.69.6
|
||||
specifier: ^1.69.7
|
||||
version: 1.69.7
|
||||
terser:
|
||||
specifier: ^5.26.0
|
||||
version: 5.26.0
|
||||
@@ -123,10 +123,10 @@ devDependencies:
|
||||
version: 0.16.7(rollup@2.79.1)
|
||||
unplugin-vue-components:
|
||||
specifier: ^0.25.2
|
||||
version: 0.25.2(rollup@2.79.1)(vue@3.4.3)
|
||||
version: 0.25.2(rollup@2.79.1)(vue@3.4.4)
|
||||
vite:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(sass@1.69.6)(terser@5.26.0)
|
||||
version: 4.5.1(sass@1.69.7)(terser@5.26.0)
|
||||
vite-plugin-compression:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1(vite@4.5.1)
|
||||
@@ -134,8 +134,8 @@ devDependencies:
|
||||
specifier: ^0.17.4
|
||||
version: 0.17.4(vite@4.5.1)(workbox-build@7.0.0)(workbox-window@7.0.0)
|
||||
vue:
|
||||
specifier: ^3.4.3
|
||||
version: 3.4.3
|
||||
specifier: 3.4.4
|
||||
version: 3.4.4
|
||||
|
||||
packages:
|
||||
|
||||
@@ -1342,12 +1342,12 @@ packages:
|
||||
css-render: 0.15.12
|
||||
dev: true
|
||||
|
||||
/@css-render/vue3-ssr@0.15.12(vue@3.4.3):
|
||||
/@css-render/vue3-ssr@0.15.12(vue@3.4.4):
|
||||
resolution: {integrity: sha512-AQLGhhaE0F+rwybRCkKUdzBdTEM/5PZBYy+fSYe1T9z9+yxMuV/k7ZRqa4M69X+EI1W8pa4kc9Iq2VjQkZx4rg==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.11
|
||||
dependencies:
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
dev: true
|
||||
|
||||
/@develar/schema-utils@2.6.5:
|
||||
@@ -1366,20 +1366,20 @@ packages:
|
||||
eslint: 8.56.0
|
||||
dev: true
|
||||
|
||||
/@electron-toolkit/preload@2.0.0(electron@27.2.0):
|
||||
/@electron-toolkit/preload@2.0.0(electron@27.2.1):
|
||||
resolution: {integrity: sha512-zpZDzbqJTZQC5d4LRs2EKruKWnqah+T75s+niBYFemYLtiW5TTZcWi3Q8UxHqnwTudDMuWJb233aaS2yjx3Xiw==}
|
||||
peerDependencies:
|
||||
electron: '>=13.0.0'
|
||||
dependencies:
|
||||
electron: 27.2.0
|
||||
electron: 27.2.1
|
||||
dev: false
|
||||
|
||||
/@electron-toolkit/utils@2.0.1(electron@27.2.0):
|
||||
/@electron-toolkit/utils@2.0.1(electron@27.2.1):
|
||||
resolution: {integrity: sha512-3nnjd3D1NIjxdzNrvR5fkJ3kbJNbRkpHppv0/pSbMX6I0DaBzpPXeSfDYuJJKzZrAc3CmGcJa0MU4+AjEOlT4g==}
|
||||
peerDependencies:
|
||||
electron: '>=13.0.0'
|
||||
dependencies:
|
||||
electron: 27.2.0
|
||||
electron: 27.2.1
|
||||
dev: false
|
||||
|
||||
/@electron/asar@3.2.8:
|
||||
@@ -1930,7 +1930,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.4
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 18.19.4
|
||||
'@types/node': 18.19.5
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
/@types/debug@4.1.12:
|
||||
@@ -1950,7 +1950,7 @@ packages:
|
||||
/@types/fs-extra@9.0.13:
|
||||
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
|
||||
dependencies:
|
||||
'@types/node': 20.10.6
|
||||
'@types/node': 20.10.7
|
||||
dev: true
|
||||
|
||||
/@types/http-cache-semantics@4.0.4:
|
||||
@@ -1963,7 +1963,7 @@ packages:
|
||||
/@types/keyv@3.1.4:
|
||||
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||
dependencies:
|
||||
'@types/node': 18.19.4
|
||||
'@types/node': 18.19.5
|
||||
|
||||
/@types/lodash-es@4.17.12:
|
||||
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
|
||||
@@ -1979,13 +1979,13 @@ packages:
|
||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||
dev: true
|
||||
|
||||
/@types/node@18.19.4:
|
||||
resolution: {integrity: sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==}
|
||||
/@types/node@18.19.5:
|
||||
resolution: {integrity: sha512-22MG6T02Hos2JWfa1o5jsIByn+bc5iOt1IS4xyg6OG68Bu+wMonVZzdrgCw693++rpLE9RUT/Bx15BeDzO0j+g==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
/@types/node@20.10.6:
|
||||
resolution: {integrity: sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==}
|
||||
/@types/node@20.10.7:
|
||||
resolution: {integrity: sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: true
|
||||
@@ -1994,7 +1994,7 @@ packages:
|
||||
resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@types/node': 20.10.6
|
||||
'@types/node': 20.10.7
|
||||
xmlbuilder: 15.1.1
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -2002,13 +2002,13 @@ packages:
|
||||
/@types/resolve@1.17.1:
|
||||
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
|
||||
dependencies:
|
||||
'@types/node': 20.10.6
|
||||
'@types/node': 20.10.7
|
||||
dev: true
|
||||
|
||||
/@types/responselike@1.0.3:
|
||||
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
||||
dependencies:
|
||||
'@types/node': 18.19.4
|
||||
'@types/node': 18.19.5
|
||||
|
||||
/@types/trusted-types@2.0.7:
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
@@ -2024,57 +2024,57 @@ packages:
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@types/node': 18.19.4
|
||||
'@types/node': 18.19.5
|
||||
optional: true
|
||||
|
||||
/@ungap/structured-clone@1.2.0:
|
||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-vue@4.6.2(vite@4.5.1)(vue@3.4.3):
|
||||
/@vitejs/plugin-vue@4.6.2(vite@4.5.1)(vue@3.4.4):
|
||||
resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
vite: ^4.0.0 || ^5.0.0
|
||||
vue: ^3.2.25
|
||||
dependencies:
|
||||
vite: 4.5.1(sass@1.69.6)(terser@5.26.0)
|
||||
vue: 3.4.3
|
||||
vite: 4.5.1(sass@1.69.7)(terser@5.26.0)
|
||||
vue: 3.4.4
|
||||
dev: true
|
||||
|
||||
/@vue/compiler-core@3.4.3:
|
||||
resolution: {integrity: sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg==}
|
||||
/@vue/compiler-core@3.4.4:
|
||||
resolution: {integrity: sha512-U5AdCN+6skzh2bSJrkMj2KZsVkUpgK8/XlxjSRYQZhNPcvt9/kmgIMpFEiTyK+Dz5E1J+8o8//BEIX+bakgVSw==}
|
||||
dependencies:
|
||||
'@babel/parser': 7.23.6
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/shared': 3.4.4
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/@vue/compiler-dom@3.4.3:
|
||||
resolution: {integrity: sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg==}
|
||||
/@vue/compiler-dom@3.4.4:
|
||||
resolution: {integrity: sha512-iSwkdDULCN+Vr8z6uwdlL044GJ/nUmECxP9vu7MzEs4Qma0FwDLYvnvRcyO0ZITuu3Os4FptGUDnhi1kOLSaGw==}
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.4.3
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/compiler-core': 3.4.4
|
||||
'@vue/shared': 3.4.4
|
||||
|
||||
/@vue/compiler-sfc@3.4.3:
|
||||
resolution: {integrity: sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw==}
|
||||
/@vue/compiler-sfc@3.4.4:
|
||||
resolution: {integrity: sha512-OTFcU6vUxUNHBcarzkp4g6d25nvcmDvFDzPRvSrIsByFFPRYN+y3b+j9HxYwt6nlWvGyFCe0roeJdJlfYxbCBg==}
|
||||
dependencies:
|
||||
'@babel/parser': 7.23.6
|
||||
'@vue/compiler-core': 3.4.3
|
||||
'@vue/compiler-dom': 3.4.3
|
||||
'@vue/compiler-ssr': 3.4.3
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/compiler-core': 3.4.4
|
||||
'@vue/compiler-dom': 3.4.4
|
||||
'@vue/compiler-ssr': 3.4.4
|
||||
'@vue/shared': 3.4.4
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.5
|
||||
postcss: 8.4.32
|
||||
postcss: 8.4.33
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/@vue/compiler-ssr@3.4.3:
|
||||
resolution: {integrity: sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA==}
|
||||
/@vue/compiler-ssr@3.4.4:
|
||||
resolution: {integrity: sha512-1DU9DflSSQlx/M61GEBN+NbT/anUki2ooDo9IXfTckCeKA/2IKNhY8KbG3x6zkd3KGrxzteC7de6QL88vEb41Q==}
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.3
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/compiler-dom': 3.4.4
|
||||
'@vue/shared': 3.4.4
|
||||
|
||||
/@vue/devtools-api@6.5.1:
|
||||
resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==}
|
||||
@@ -2094,35 +2094,35 @@ packages:
|
||||
- '@types/eslint'
|
||||
dev: true
|
||||
|
||||
/@vue/reactivity@3.4.3:
|
||||
resolution: {integrity: sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg==}
|
||||
/@vue/reactivity@3.4.4:
|
||||
resolution: {integrity: sha512-DFsuJBf6sfhd5SYzJmcBTUG9+EKqjF31Gsk1NJtnpJm9liSZ806XwGJUeNBVQIanax7ODV7Lmk/k17BgxXNuTg==}
|
||||
dependencies:
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/shared': 3.4.4
|
||||
|
||||
/@vue/runtime-core@3.4.3:
|
||||
resolution: {integrity: sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ==}
|
||||
/@vue/runtime-core@3.4.4:
|
||||
resolution: {integrity: sha512-zWWwNQAj5JdxrmOA1xegJm+c4VtyIbDEKgQjSb4va5v7gGTCh0ZjvLI+htGFdVXaO9bs2J3C81p5p+6jrPK8Bw==}
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.4.3
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/reactivity': 3.4.4
|
||||
'@vue/shared': 3.4.4
|
||||
|
||||
/@vue/runtime-dom@3.4.3:
|
||||
resolution: {integrity: sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A==}
|
||||
/@vue/runtime-dom@3.4.4:
|
||||
resolution: {integrity: sha512-Nlh2ap1J/eJQ6R0g+AIRyGNwpTJQACN0dk8I8FRLH8Ev11DSvfcPOpn4+Kbg5xAMcuq0cHB8zFYxVrOgETrrvg==}
|
||||
dependencies:
|
||||
'@vue/runtime-core': 3.4.3
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/runtime-core': 3.4.4
|
||||
'@vue/shared': 3.4.4
|
||||
csstype: 3.1.3
|
||||
|
||||
/@vue/server-renderer@3.4.3(vue@3.4.3):
|
||||
resolution: {integrity: sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw==}
|
||||
/@vue/server-renderer@3.4.4(vue@3.4.4):
|
||||
resolution: {integrity: sha512-+AjoiKcC41k7SMJBYkDO9xs79/Of8DiThS9mH5l2MK+EY0to3psI0k+sElvVqQvsoZTjHMEuMz0AEgvm2T+CwA==}
|
||||
peerDependencies:
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.4.3
|
||||
'@vue/shared': 3.4.3
|
||||
vue: 3.4.3
|
||||
'@vue/compiler-ssr': 3.4.4
|
||||
'@vue/shared': 3.4.4
|
||||
vue: 3.4.4
|
||||
|
||||
/@vue/shared@3.4.3:
|
||||
resolution: {integrity: sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==}
|
||||
/@vue/shared@3.4.4:
|
||||
resolution: {integrity: sha512-abSgiVRhfjfl3JALR/cSuBl74hGJ3SePgf1mKzodf1eMWLwHZbfEGxT2cNJSsNiw44jEgrO7bNkhchaWA7RwNw==}
|
||||
|
||||
/@xmldom/xmldom@0.8.10:
|
||||
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
|
||||
@@ -2135,7 +2135,7 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
axios: 1.6.3
|
||||
axios: 1.6.5
|
||||
crypto-js: 4.2.0
|
||||
express: 4.18.2
|
||||
express-fileupload: 1.4.3
|
||||
@@ -2394,8 +2394,8 @@ packages:
|
||||
resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
|
||||
dev: false
|
||||
|
||||
/axios@1.6.3:
|
||||
resolution: {integrity: sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==}
|
||||
/axios@1.6.5:
|
||||
resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.4
|
||||
form-data: 4.0.0
|
||||
@@ -2529,8 +2529,8 @@ packages:
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001572
|
||||
electron-to-chromium: 1.4.616
|
||||
caniuse-lite: 1.0.30001576
|
||||
electron-to-chromium: 1.4.623
|
||||
node-releases: 2.0.14
|
||||
update-browserslist-db: 1.0.13(browserslist@4.22.2)
|
||||
dev: true
|
||||
@@ -2643,8 +2643,8 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/caniuse-lite@1.0.30001572:
|
||||
resolution: {integrity: sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==}
|
||||
/caniuse-lite@1.0.30001576:
|
||||
resolution: {integrity: sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==}
|
||||
dev: true
|
||||
|
||||
/caseless@0.12.0:
|
||||
@@ -3198,8 +3198,8 @@ packages:
|
||||
type-fest: 2.19.0
|
||||
dev: false
|
||||
|
||||
/electron-to-chromium@1.4.616:
|
||||
resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==}
|
||||
/electron-to-chromium@1.4.623:
|
||||
resolution: {integrity: sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==}
|
||||
dev: true
|
||||
|
||||
/electron-updater@6.1.7:
|
||||
@@ -3234,19 +3234,19 @@ packages:
|
||||
esbuild: 0.18.20
|
||||
magic-string: 0.30.5
|
||||
picocolors: 1.0.0
|
||||
vite: 4.5.1(sass@1.69.6)(terser@5.26.0)
|
||||
vite: 4.5.1(sass@1.69.7)(terser@5.26.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/electron@27.2.0:
|
||||
resolution: {integrity: sha512-no/iMICVLI/5G0IqgKFbB89HDN88DWwKeRO+dPfJPkpJISdEX8Cx/sMEOFuuRa4VNInNe5CKCqRWExK5z3AdcQ==}
|
||||
/electron@27.2.1:
|
||||
resolution: {integrity: sha512-bYUzyptYrUIFtPnyF2x6DnhF1E9FCthctjbNSKMqg7dG4NqSwyuZzEku3Wts55u8R+ddNFbLoLwRHHLvYTCQlA==}
|
||||
engines: {node: '>= 12.20.55'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@electron/get': 2.0.3
|
||||
'@types/node': 18.19.4
|
||||
'@types/node': 18.19.5
|
||||
extract-zip: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -4556,7 +4556,7 @@ packages:
|
||||
resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/node': 20.10.6
|
||||
'@types/node': 20.10.7
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
@@ -4978,18 +4978,19 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/naive-ui@2.36.0(vue@3.4.3):
|
||||
resolution: {integrity: sha512-r1ydtEm1Ryf/aWpbLCf32mQAGK99jd1eXgpkCtIomcBRZeAtusfy6zCtIpCppoCuIKM3BW5DMafhVxilubk/lQ==}
|
||||
/naive-ui@2.37.0(vue@3.4.4):
|
||||
resolution: {integrity: sha512-TcuXM1zysnK6i/7o2ZqNjcLp3QMmcdSLWWiXcpEk+xdGpkJzs53/OXNpF4CoDM/npjha7qqtB8Pl17YPN5egFw==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
dependencies:
|
||||
'@css-render/plugin-bem': 0.15.12(css-render@0.15.12)
|
||||
'@css-render/vue3-ssr': 0.15.12(vue@3.4.3)
|
||||
'@css-render/vue3-ssr': 0.15.12(vue@3.4.4)
|
||||
'@types/katex': 0.16.7
|
||||
'@types/lodash': 4.14.202
|
||||
'@types/lodash-es': 4.17.12
|
||||
async-validator: 4.2.5
|
||||
css-render: 0.15.12
|
||||
csstype: 3.1.3
|
||||
date-fns: 2.30.0
|
||||
date-fns-tz: 2.0.0(date-fns@2.30.0)
|
||||
evtd: 0.2.4
|
||||
@@ -4998,10 +4999,10 @@ packages:
|
||||
lodash-es: 4.17.21
|
||||
seemly: 0.3.8
|
||||
treemate: 0.3.11
|
||||
vdirs: 0.1.8(vue@3.4.3)
|
||||
vooks: 0.2.12(vue@3.4.3)
|
||||
vue: 3.4.3
|
||||
vueuc: 0.4.58(vue@3.4.3)
|
||||
vdirs: 0.1.8(vue@3.4.4)
|
||||
vooks: 0.2.12(vue@3.4.4)
|
||||
vue: 3.4.4
|
||||
vueuc: 0.4.58(vue@3.4.4)
|
||||
dev: true
|
||||
|
||||
/nanoid@3.3.7:
|
||||
@@ -5286,10 +5287,10 @@ packages:
|
||||
peerDependencies:
|
||||
pinia: ^2.0.0
|
||||
dependencies:
|
||||
pinia: 2.1.7(vue@3.4.3)
|
||||
pinia: 2.1.7(vue@3.4.4)
|
||||
dev: false
|
||||
|
||||
/pinia@2.1.7(vue@3.4.3):
|
||||
/pinia@2.1.7(vue@3.4.4):
|
||||
resolution: {integrity: sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.4.0
|
||||
@@ -5302,8 +5303,8 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.5.1
|
||||
vue: 3.4.3
|
||||
vue-demi: 0.14.6(vue@3.4.3)
|
||||
vue: 3.4.4
|
||||
vue-demi: 0.14.6(vue@3.4.4)
|
||||
dev: false
|
||||
|
||||
/pkg-types@1.0.3:
|
||||
@@ -5358,8 +5359,8 @@ packages:
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/postcss@8.4.32:
|
||||
resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
|
||||
/postcss@8.4.33:
|
||||
resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
@@ -5749,8 +5750,8 @@ packages:
|
||||
truncate-utf8-bytes: 1.0.2
|
||||
dev: true
|
||||
|
||||
/sass@1.69.6:
|
||||
resolution: {integrity: sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==}
|
||||
/sass@1.69.7:
|
||||
resolution: {integrity: sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -6473,7 +6474,7 @@ packages:
|
||||
- rollup
|
||||
dev: true
|
||||
|
||||
/unplugin-vue-components@0.25.2(rollup@2.79.1)(vue@3.4.3):
|
||||
/unplugin-vue-components@0.25.2(rollup@2.79.1)(vue@3.4.4):
|
||||
resolution: {integrity: sha512-OVmLFqILH6w+eM8fyt/d/eoJT9A6WO51NZLf1vC5c1FZ4rmq2bbGxTy8WP2Jm7xwFdukaIdv819+UI7RClPyCA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
@@ -6496,7 +6497,7 @@ packages:
|
||||
minimatch: 9.0.3
|
||||
resolve: 1.22.8
|
||||
unplugin: 1.6.0
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
@@ -6567,13 +6568,13 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/vdirs@0.1.8(vue@3.4.3):
|
||||
/vdirs@0.1.8(vue@3.4.4):
|
||||
resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.11
|
||||
dependencies:
|
||||
evtd: 0.2.4
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
dev: true
|
||||
|
||||
/verror@1.10.0:
|
||||
@@ -6604,7 +6605,7 @@ packages:
|
||||
chalk: 4.1.2
|
||||
debug: 4.3.4
|
||||
fs-extra: 10.1.0
|
||||
vite: 4.5.1(sass@1.69.6)(terser@5.26.0)
|
||||
vite: 4.5.1(sass@1.69.7)(terser@5.26.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -6620,14 +6621,14 @@ packages:
|
||||
debug: 4.3.4
|
||||
fast-glob: 3.3.2
|
||||
pretty-bytes: 6.1.1
|
||||
vite: 4.5.1(sass@1.69.6)(terser@5.26.0)
|
||||
vite: 4.5.1(sass@1.69.7)(terser@5.26.0)
|
||||
workbox-build: 7.0.0
|
||||
workbox-window: 7.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite@4.5.1(sass@1.69.6)(terser@5.26.0):
|
||||
/vite@4.5.1(sass@1.69.7)(terser@5.26.0):
|
||||
resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
@@ -6656,24 +6657,24 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
esbuild: 0.18.20
|
||||
postcss: 8.4.32
|
||||
postcss: 8.4.33
|
||||
rollup: 3.29.4
|
||||
sass: 1.69.6
|
||||
sass: 1.69.7
|
||||
terser: 5.26.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/vooks@0.2.12(vue@3.4.3):
|
||||
/vooks@0.2.12(vue@3.4.4):
|
||||
resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
dependencies:
|
||||
evtd: 0.2.4
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
dev: true
|
||||
|
||||
/vue-demi@0.14.6(vue@3.4.3):
|
||||
/vue-demi@0.14.6(vue@3.4.4):
|
||||
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
@@ -6685,7 +6686,7 @@ packages:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
dependencies:
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
dev: false
|
||||
|
||||
/vue-eslint-parser@9.3.2(eslint@8.56.0):
|
||||
@@ -6706,46 +6707,46 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vue-router@4.2.5(vue@3.4.3):
|
||||
/vue-router@4.2.5(vue@3.4.4):
|
||||
resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.5.1
|
||||
vue: 3.4.3
|
||||
vue: 3.4.4
|
||||
dev: false
|
||||
|
||||
/vue-slider-component@4.1.0-beta.7:
|
||||
resolution: {integrity: sha512-Qb7K920ZG7PoQswoF6Ias+i3W2rd3k4fpk04JUl82kEUcN86Yg6et7bVSKWt/7VpQe8a5IT3BqCKSCOZ7AJgCA==}
|
||||
dev: false
|
||||
|
||||
/vue@3.4.3:
|
||||
resolution: {integrity: sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA==}
|
||||
/vue@3.4.4:
|
||||
resolution: {integrity: sha512-suZXgDVT8lRNhKmxdkwOsR0oyUi8is7mtqI18qW97JLoyorEbE9B2Sb4Ws/mR/+0AgA/JUtsv1ytlRSH3/pDIA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.3
|
||||
'@vue/compiler-sfc': 3.4.3
|
||||
'@vue/runtime-dom': 3.4.3
|
||||
'@vue/server-renderer': 3.4.3(vue@3.4.3)
|
||||
'@vue/shared': 3.4.3
|
||||
'@vue/compiler-dom': 3.4.4
|
||||
'@vue/compiler-sfc': 3.4.4
|
||||
'@vue/runtime-dom': 3.4.4
|
||||
'@vue/server-renderer': 3.4.4(vue@3.4.4)
|
||||
'@vue/shared': 3.4.4
|
||||
|
||||
/vueuc@0.4.58(vue@3.4.3):
|
||||
/vueuc@0.4.58(vue@3.4.4):
|
||||
resolution: {integrity: sha512-Wnj/N8WbPRSxSt+9ji1jtDHPzda5h2OH/0sFBhvdxDRuyCZbjGg3/cKMaKqEoe+dErTexG2R+i6Q8S/Toq1MYg==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.11
|
||||
dependencies:
|
||||
'@css-render/vue3-ssr': 0.15.12(vue@3.4.3)
|
||||
'@css-render/vue3-ssr': 0.15.12(vue@3.4.4)
|
||||
'@juggle/resize-observer': 3.4.0
|
||||
css-render: 0.15.12
|
||||
evtd: 0.2.4
|
||||
seemly: 0.3.8
|
||||
vdirs: 0.1.8(vue@3.4.3)
|
||||
vooks: 0.2.12(vue@3.4.3)
|
||||
vue: 3.4.3
|
||||
vdirs: 0.1.8(vue@3.4.4)
|
||||
vooks: 0.2.12(vue@3.4.4)
|
||||
vue: 3.4.4
|
||||
dev: true
|
||||
|
||||
/webidl-conversions@4.0.2:
|
||||
|
||||
BIN
public/images/pic/artist.jpg
Normal file
BIN
public/images/pic/artist.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/pic/video.png
Normal file
BIN
public/images/pic/video.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@@ -224,7 +224,7 @@ onUnmounted(() => {
|
||||
.sider-all {
|
||||
height: 100%;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
@media (max-width: 900px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,3 +97,25 @@ export const likeMv = (t, mvid) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 全部 mv
|
||||
* @param {string} area - 地区,可选值为全部,内地,港台,欧美,日本,韩国,不填则为全部
|
||||
* @param {string} type - 类型,可选值为全部,官方版,原生,现场版,网易出品,不填则为全部
|
||||
* @param {string} order - 排序,可选值为上升最快,最热,最新,不填则为上升最快
|
||||
* @param {number} [limit=12] - 返回数量,默认12
|
||||
* @param {number} [offset=0] - 偏移数量,默认0
|
||||
*/
|
||||
export const allMv = (area, type, order, limit = 12, offset = 0) => {
|
||||
return axios({
|
||||
method: "GET",
|
||||
url: "/mv/all",
|
||||
params: {
|
||||
area,
|
||||
type,
|
||||
order,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="play-btn" @click.stop>
|
||||
<n-button
|
||||
:loading="playLoading"
|
||||
color="#efefefde"
|
||||
color="#efefef"
|
||||
tag="div"
|
||||
type="primary"
|
||||
class="play"
|
||||
@@ -30,13 +30,16 @@ import { useRouter } from "vue-router";
|
||||
import { getAllPlayList } from "@/api/playlist";
|
||||
import { getAlbumDetail } from "@/api/album";
|
||||
import { getDjProgram } from "@/api/dj";
|
||||
import { musicData, siteStatus } from "@/stores";
|
||||
import { musicData, siteStatus, siteData } from "@/stores";
|
||||
import { playOrPause, initPlayer } from "@/utils/Player";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
import formatData from "@/utils/formatData";
|
||||
|
||||
const router = useRouter();
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const { userLikeData } = storeToRefs(data);
|
||||
const { playList, playSongData } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode, playState } = storeToRefs(status);
|
||||
|
||||
@@ -58,7 +61,7 @@ const playListData = ref(null);
|
||||
|
||||
// 是否处于当前歌单
|
||||
const isHasSongs = computed(() => {
|
||||
if (!playListData.value) return -1;
|
||||
if (!playListData.value || playListData.value === 400) return -1;
|
||||
const songId = music.getPlaySongData?.id;
|
||||
const existingIndex = playListData.value.findIndex((song) => song.id === songId);
|
||||
return existingIndex;
|
||||
@@ -71,8 +74,16 @@ const getPlaylistData = async () => {
|
||||
// 按列表类别获取数据
|
||||
switch (props.type) {
|
||||
case "playlist": {
|
||||
const result = await getAllPlayList(props.id, 500);
|
||||
return formatData(result.songs, "song");
|
||||
if (props.id === 1024) {
|
||||
console.log("播放我喜欢的音乐");
|
||||
const id = userLikeData.value.playlists?.[0]?.id || null;
|
||||
if (!isLogin() || !id) return 400;
|
||||
const result = await getAllPlayList(id, 500);
|
||||
return formatData(result.songs, "song");
|
||||
} else {
|
||||
const result = await getAllPlayList(props.id, 500);
|
||||
return formatData(result.songs, "song");
|
||||
}
|
||||
}
|
||||
case "album": {
|
||||
const result = await getAlbumDetail(props.id);
|
||||
@@ -103,6 +114,10 @@ const playAllSongs = async () => {
|
||||
playLoading.value = false;
|
||||
return $message.error("获取播放列表时出现错误");
|
||||
}
|
||||
if (playListData.value === 400) {
|
||||
playLoading.value = false;
|
||||
return $message.error("请登录后使用");
|
||||
}
|
||||
console.log("不在歌单内");
|
||||
// 更改模式和歌单
|
||||
playHeartbeatMode.value = false;
|
||||
|
||||
@@ -36,7 +36,17 @@
|
||||
>
|
||||
<template #placeholder>
|
||||
<div :class="['cover-loading', type]">
|
||||
<img class="loading-img" src="/images/pic/album.jpg?assest" alt="song" />
|
||||
<img
|
||||
class="loading-img"
|
||||
:src="
|
||||
type === 'mv'
|
||||
? '/images/pic/video.png?assest'
|
||||
: type === 'artist'
|
||||
? '/images/pic/artist.jpg?assest'
|
||||
: '/images/pic/album.jpg?assest'
|
||||
"
|
||||
alt="song"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div :class="['cover-loading', type]">
|
||||
<n-spin size="small" />
|
||||
|
||||
@@ -33,9 +33,7 @@
|
||||
</template>
|
||||
</n-image>
|
||||
<!-- 播放按钮 -->
|
||||
<n-icon v-if="showIcon" class="play" @click.stop>
|
||||
<SvgIcon icon="play-circle" />
|
||||
</n-icon>
|
||||
<CoverPlayBtn v-if="showIcon" :id="data.id" class="play" />
|
||||
<!-- 日期 -->
|
||||
<div v-if="showDate" class="cover-date">
|
||||
<n-icon class="date-icon">
|
||||
@@ -145,15 +143,24 @@ const props = defineProps({
|
||||
.play {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
font-size: 60px;
|
||||
color: #ffffffe6;
|
||||
transform: scale(0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition:
|
||||
transform 0.3s,
|
||||
opacity 0.3s;
|
||||
cursor: pointer;
|
||||
:deep(.play) {
|
||||
--n-width: 50px;
|
||||
--n-height: 50px;
|
||||
--n-font-size: 20px;
|
||||
.n-icon {
|
||||
font-size: 60px !important;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
&:active {
|
||||
transform: scale(1);
|
||||
@@ -212,7 +219,6 @@ const props = defineProps({
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--n-close-color-hover);
|
||||
transform: translate3d(-2px, 0, 0);
|
||||
.cover-main-img {
|
||||
filter: brightness(0.8);
|
||||
}
|
||||
|
||||
@@ -123,15 +123,18 @@
|
||||
<script setup>
|
||||
import { NText, NIcon } from "naive-ui";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteStatus } from "@/stores";
|
||||
import { musicData, siteStatus, siteSettings } from "@/stores";
|
||||
import { initPlayer, fadePlayOrPause, changePlayIndex, soundStop } from "@/utils/Player";
|
||||
import SvgIcon from "@/components/Global/SvgIcon";
|
||||
import debounce from "@/utils/debounce";
|
||||
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const settings = siteSettings();
|
||||
const { useMusicCache } = storeToRefs(settings);
|
||||
const { playSongData, playList } = storeToRefs(music);
|
||||
const { coverTheme, showFullPlayer, playListShow, playIndex, playMode } = storeToRefs(status);
|
||||
const { coverTheme, showFullPlayer, playListShow, playIndex, playMode, playLoading } =
|
||||
storeToRefs(status);
|
||||
|
||||
const playListRef = ref(null);
|
||||
|
||||
@@ -158,6 +161,11 @@ const playlistOpen = () => {
|
||||
|
||||
// 播放歌曲
|
||||
const playSong = debounce(async (song, index) => {
|
||||
// 若开启了缓存且正在加载
|
||||
if (useMusicCache.value && playLoading.value) {
|
||||
$message.warning("歌曲正在缓冲中,请稍后");
|
||||
return false;
|
||||
}
|
||||
// 更改模式
|
||||
if (playMode.value !== "dj") playMode.value = "normal";
|
||||
// 更改播放索引
|
||||
@@ -352,6 +360,7 @@ const removeSong = async (index) => {
|
||||
.main-playlist {
|
||||
width: 400px !important;
|
||||
border-radius: 12px 0 0 12px;
|
||||
transition: width 0.3s;
|
||||
.n-drawer-header {
|
||||
height: 70px;
|
||||
box-sizing: border-box;
|
||||
@@ -380,5 +389,9 @@ const removeSong = async (index) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
width: 100% !important;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,16 +4,19 @@
|
||||
<div v-if="data?.[0]?.id" class="song-list">
|
||||
<div v-if="showTitle" class="song-list-header">
|
||||
<n-text class="num" depth="3"> # </n-text>
|
||||
<n-text :class="{ info: true, 'has-cover': data[0].cover && showCover }" depth="3">
|
||||
<n-text :class="['info', { 'has-cover': data[0].cover && showCover }]" depth="3">
|
||||
{{ type === "song" ? "歌曲" : "声音" }}
|
||||
</n-text>
|
||||
<n-text v-if="data[0].album && showAlbum" class="album" depth="3"> 专辑 </n-text>
|
||||
<n-text v-if="data[0].updateTime && type === 'dj'" class="update" depth="3">
|
||||
<n-text v-if="data[0].album && showAlbum" class="album hidden" depth="3"> 专辑 </n-text>
|
||||
<n-text v-if="data[0].updateTime && type === 'dj'" class="update hidden" depth="3">
|
||||
更新日期
|
||||
</n-text>
|
||||
<n-text v-if="data[0].playCount && type === 'dj'" class="count" depth="3"> 播放量 </n-text>
|
||||
<n-text v-if="data[0].duration" class="duration" depth="3"> 时长 </n-text>
|
||||
<n-text v-if="data[0].size" class="size" depth="3"> 大小 </n-text>
|
||||
<n-text v-if="type !== 'dj'" class="control" />
|
||||
<n-text v-if="data[0].playCount && type === 'dj'" class="count hidden" depth="3">
|
||||
播放量
|
||||
</n-text>
|
||||
<n-text v-if="data[0].duration" class="duration hidden" depth="3"> 时长 </n-text>
|
||||
<n-text v-if="data[0].size" class="size hidden" depth="3"> 大小 </n-text>
|
||||
</div>
|
||||
<n-card
|
||||
v-for="(item, index) in data.slice(
|
||||
@@ -31,6 +34,7 @@
|
||||
}"
|
||||
:class="music.getPlaySongData?.id === item?.id ? 'songs play' : 'songs'"
|
||||
hoverable
|
||||
@click="checkCanClick(data, item, songsIndex + index)"
|
||||
@dblclick.stop="playSong(data, item, songsIndex + index)"
|
||||
@contextmenu="
|
||||
songListDropdownRef?.openDropdown($event, data, item, songsIndex + index, sourceId, type)
|
||||
@@ -40,7 +44,7 @@
|
||||
<n-text v-if="music.getPlaySongData?.id !== item?.id" class="num" depth="3">
|
||||
{{ songsIndex + index + 1 }}
|
||||
</n-text>
|
||||
<n-icon v-else class="play" size="22">
|
||||
<n-icon v-else class="num" size="22">
|
||||
<SvgIcon icon="music-note" />
|
||||
</n-icon>
|
||||
<!-- 封面 -->
|
||||
@@ -67,7 +71,10 @@
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<!-- 名称 -->
|
||||
<n-text class="name" depth="2">{{ item?.name || "未知曲目" }}</n-text>
|
||||
<!-- @click.stop="type !== 'dj' && !item.path ? router.push(`/song?id=${item.id}`) : null" -->
|
||||
<n-text class="name" depth="2">
|
||||
{{ item?.name || "未知曲目" }}
|
||||
</n-text>
|
||||
<!-- 特权 -->
|
||||
<n-tag
|
||||
v-if="showPrivilege && item.fee === 1 && userData.detail?.profile?.vipType !== 11"
|
||||
@@ -140,7 +147,7 @@
|
||||
<template v-if="showAlbum && type !== 'dj'">
|
||||
<n-text
|
||||
v-if="item.album"
|
||||
class="album"
|
||||
class="album hidden"
|
||||
@click.stop="
|
||||
typeof item.album === 'object' ? router.push(`/album?id=${item.album.id}`) : null
|
||||
"
|
||||
@@ -148,7 +155,7 @@
|
||||
>
|
||||
{{ typeof item.album === "object" ? item.album?.name || "未知专辑" : item.album }}
|
||||
</n-text>
|
||||
<n-text v-else class="album">未知专辑</n-text>
|
||||
<n-text v-else class="album hidden">未知专辑</n-text>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<div v-if="type !== 'dj'" class="action">
|
||||
@@ -168,20 +175,32 @@
|
||||
"
|
||||
/>
|
||||
</n-icon>
|
||||
<!-- 更多操作 -->
|
||||
<n-icon
|
||||
class="more mobile"
|
||||
depth="3"
|
||||
size="20"
|
||||
@click.stop="
|
||||
songListDrawerRef?.drawerOpen(data, item, songsIndex + index, sourceId, type)
|
||||
"
|
||||
@dblclick.stop
|
||||
>
|
||||
<SvgIcon icon="more" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<!-- 更新日期 -->
|
||||
<n-text v-if="type === 'dj' && item.updateTime" class="update" depth="3">
|
||||
<n-text v-if="type === 'dj' && item.updateTime" class="update hidden" depth="3">
|
||||
{{ getTimestampTime(item.updateTime, false) }}
|
||||
</n-text>
|
||||
<!-- 播放量 -->
|
||||
<n-text v-if="type === 'dj' && item.playCount" class="count" depth="3">
|
||||
<n-text v-if="type === 'dj' && item.playCount" class="count hidden" depth="3">
|
||||
{{ item.playCount }}次
|
||||
</n-text>
|
||||
<!-- 时长 -->
|
||||
<n-text v-if="item.duration" class="duration" depth="3">{{ item.duration }}</n-text>
|
||||
<n-text v-if="item.duration" class="duration hidden" depth="3">{{ item.duration }}</n-text>
|
||||
<n-text v-else class="duration"> -- </n-text>
|
||||
<!-- 大小 -->
|
||||
<n-text v-if="item.size" class="size" depth="3">{{ item.size }}M</n-text>
|
||||
<n-text v-if="item.size" class="size hidden" depth="3">{{ item.size }}M</n-text>
|
||||
</n-card>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
@@ -191,7 +210,21 @@
|
||||
@pageNumberChange="pageNumberChange"
|
||||
/>
|
||||
<!-- 右键菜单 -->
|
||||
<SongListDropdown ref="songListDropdownRef" @playSong="playSong" />
|
||||
<SongListDropdown
|
||||
ref="songListDropdownRef"
|
||||
@playSong="playSong"
|
||||
@delCloudSong="delCloudSong"
|
||||
@deletePlaylistSong="deletePlaylistSong"
|
||||
@delLocalSong="delLocalSong"
|
||||
/>
|
||||
<!-- 移动端菜单 -->
|
||||
<SongListDrawer
|
||||
ref="songListDrawerRef"
|
||||
@playSong="playSong"
|
||||
@delCloudSong="delCloudSong"
|
||||
@deletePlaylistSong="deletePlaylistSong"
|
||||
@delLocalSong="delLocalSong"
|
||||
/>
|
||||
<!-- 定位歌曲 -->
|
||||
<Transition name="shrink" mode="out-in">
|
||||
<n-card
|
||||
@@ -227,15 +260,17 @@
|
||||
size="large"
|
||||
/>
|
||||
<!-- 加载动画 -->
|
||||
<n-spin v-else class="loading" size="small">
|
||||
<template #description> 加载中 </template>
|
||||
</n-spin>
|
||||
<div v-else class="loading">
|
||||
<n-skeleton :repeat="10" text />
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useRouter } from "vue-router";
|
||||
import { setCloudDel } from "@/api/cloud";
|
||||
import { addSongToPlayList } from "@/api/playlist";
|
||||
import { siteData, siteSettings, musicData, siteStatus } from "@/stores";
|
||||
import { initPlayer, fadePlayOrPause, addSongToNext } from "@/utils/Player";
|
||||
import { getTimestampTime } from "@/utils/timeTools";
|
||||
@@ -246,9 +281,9 @@ const dataStore = siteData();
|
||||
const status = siteStatus();
|
||||
const settings = siteSettings();
|
||||
const { userData } = storeToRefs(dataStore);
|
||||
const { playIndex, playMode, playHeartbeatMode } = storeToRefs(status);
|
||||
const { loadSize, playSearch } = storeToRefs(settings);
|
||||
const { loadSize, playSearch, useMusicCache } = storeToRefs(settings);
|
||||
const { playList, playSongData, playSongSource } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode, playLoading } = storeToRefs(status);
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const props = defineProps({
|
||||
@@ -297,7 +332,8 @@ const props = defineProps({
|
||||
// 分页数据
|
||||
const pageNumber = ref(1);
|
||||
|
||||
// 右键菜单
|
||||
// 子组件
|
||||
const songListDrawerRef = ref(null);
|
||||
const songListDropdownRef = ref(null);
|
||||
|
||||
// 当前索引
|
||||
@@ -324,6 +360,11 @@ const checkHasPlaying = (isScoll = null) => {
|
||||
// 播放歌曲
|
||||
const playSong = async (data, song, index) => {
|
||||
console.log(data, song, index);
|
||||
// 若开启了缓存且正在加载
|
||||
if (useMusicCache.value && playLoading.value) {
|
||||
$message.warning("歌曲正在缓冲中,请稍后");
|
||||
return false;
|
||||
}
|
||||
// 更改模式
|
||||
playMode.value = props.type === "song" ? "normal" : "dj";
|
||||
// 检查当前页面
|
||||
@@ -370,6 +411,71 @@ const pageNumberChange = (page) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 检查是否可执行双击
|
||||
const checkCanClick = (data, item, index) => {
|
||||
if (window.innerWidth > 700) return false;
|
||||
playSong(data, item, index);
|
||||
};
|
||||
|
||||
// 云盘歌曲删除
|
||||
const delCloudSong = (data, song, index) => {
|
||||
console.log(data, song, index);
|
||||
$dialog.warning({
|
||||
title: "确认删除",
|
||||
content: `确认从云盘中删除 ${song.name}?该操作无法撤销!`,
|
||||
positiveText: "删除",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
const result = await setCloudDel(song.id);
|
||||
if (result.code == 200) {
|
||||
data.splice(index, 1);
|
||||
$message.success("删除成功");
|
||||
} else {
|
||||
$message.error("删除失败,请重试");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 歌单歌曲删除
|
||||
const deletePlaylistSong = (pid, song, data, index) => {
|
||||
$dialog.warning({
|
||||
title: "确认删除",
|
||||
content: `确认从歌单中移除 ${song.name}?该操作无法撤销!`,
|
||||
positiveText: "删除",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
const result = await addSongToPlayList(pid, song?.id, "del");
|
||||
if (result.status === 200) {
|
||||
data.length === 1 ? data.splice(0, 1, "empty") : data.splice(index, 1);
|
||||
$message.success("歌曲删除成功");
|
||||
} else {
|
||||
$message.error("歌曲删除失败,请重试");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 本地歌曲删除
|
||||
const delLocalSong = (data, song, index) => {
|
||||
$dialog.warning({
|
||||
title: "确认删除",
|
||||
content: `确认从本地磁盘中删除 ${song.name}?该操作无法撤销!`,
|
||||
positiveText: "删除",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
console.log(data, song, index);
|
||||
const result = await electron.ipcRenderer.invoke("deleteFile", song?.path);
|
||||
if (result) {
|
||||
data.length === 1 ? data.splice(0, 1, "empty") : data.splice(index, 1);
|
||||
$message.success("歌曲删除成功");
|
||||
} else {
|
||||
$message.error("歌曲删除失败,请重试");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 监听歌曲变化
|
||||
watch(
|
||||
() => music.getPlaySongData?.id,
|
||||
@@ -409,16 +515,20 @@ onBeforeUnmount(() => {
|
||||
.has-cover {
|
||||
margin-right: 66px;
|
||||
}
|
||||
.control {
|
||||
width: 40px;
|
||||
}
|
||||
.update {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
margin-right: auto;
|
||||
}
|
||||
.count {
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
.duration {
|
||||
width: 40px;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
.size {
|
||||
@@ -459,8 +569,7 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.num,
|
||||
.play {
|
||||
.num {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
min-width: 30px;
|
||||
@@ -564,6 +673,9 @@ onBeforeUnmount(() => {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.more {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.update {
|
||||
width: 80px;
|
||||
@@ -574,7 +686,7 @@ onBeforeUnmount(() => {
|
||||
text-align: center;
|
||||
}
|
||||
.duration {
|
||||
width: 40px;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
.size {
|
||||
@@ -636,12 +748,49 @@ onBeforeUnmount(() => {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.song-list-header,
|
||||
.songs {
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.songs {
|
||||
.num {
|
||||
font-size: 12px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
}
|
||||
.info {
|
||||
.title {
|
||||
.name {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
.artist {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.action {
|
||||
width: 60px;
|
||||
justify-content: flex-end;
|
||||
.more {
|
||||
display: inline-block;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.loading {
|
||||
margin: 60px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
:deep(.n-skeleton) {
|
||||
&:nth-of-type(1) {
|
||||
margin-top: 0;
|
||||
}
|
||||
height: 80px;
|
||||
margin-top: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
323
src/components/List/SongListDrawer.vue
Normal file
323
src/components/List/SongListDrawer.vue
Normal file
@@ -0,0 +1,323 @@
|
||||
<!-- 歌曲列表 - 移动端菜单 -->
|
||||
<template>
|
||||
<n-drawer
|
||||
v-model:show="drawerShow"
|
||||
:auto-focus="false"
|
||||
height="calc(100vh - 200px)"
|
||||
placement="bottom"
|
||||
class="song-list-drawer"
|
||||
@after-leave="drawerShow = false"
|
||||
@mask-click="drawerShow = false"
|
||||
>
|
||||
<n-drawer-content :native-scrollbar="false" :body-content-style="{ padding: 0 }" closable>
|
||||
<template #header>
|
||||
<div v-if="!songData?.path" class="song-data">
|
||||
<n-image
|
||||
:src="songData?.coverSize?.s || songData?.cover"
|
||||
class="cover"
|
||||
preview-disabled
|
||||
/>
|
||||
<div class="song-detail">
|
||||
<n-text class="name">{{ songData?.name || "未知曲目" }}</n-text>
|
||||
<template v-if="songType === 'song'">
|
||||
<div v-if="songData?.artists && Array.isArray(songData.artists)" class="all-ar">
|
||||
<n-text v-for="ar in songData.artists" :key="ar.id" class="ar" depth="3">
|
||||
{{ ar.name }}
|
||||
</n-text>
|
||||
</div>
|
||||
<div v-else class="all-ar">
|
||||
<n-text class="ar" depth="3">
|
||||
{{ songData.artists || "未知艺术家" }}
|
||||
</n-text>
|
||||
</div>
|
||||
</template>
|
||||
<n-text v-else class="ar">
|
||||
{{ songData?.artists || "未知艺术家" }}
|
||||
</n-text>
|
||||
</div>
|
||||
</div>
|
||||
<n-text v-else>更多操作</n-text>
|
||||
</template>
|
||||
<div class="all-menu">
|
||||
<div
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
emit('playSong', playlistData, songData, songIndex);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="play" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 立即播放 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isSong && playMode !== 'dj' && music.getPlaySongData?.id !== songData.id && !isFm"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
playMode = 'song';
|
||||
addSongToNext(songData);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="play-next" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 下一首播放 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isSong && !isLocalSong"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
addPlaylistRef?.openAddToPlaylist(songData?.id);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="playlist-add" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 添加到歌单 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isSong && !isLocalSong"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
router.push({
|
||||
path: '/comment',
|
||||
query: {
|
||||
id: songData.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="20">
|
||||
<SvgIcon icon="comment-text" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 查看评论 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isSong && isHasMv"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
router.push({
|
||||
path: '/videos-player',
|
||||
query: {
|
||||
id: songData.mv,
|
||||
},
|
||||
});
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="video" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 观看 MV </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isCloud && isUserPlaylist"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
emit('deletePlaylistSong', playlistData, songData, songIndex);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="delete" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 从歌单中删除 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isCloud"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
emit('delCloudSong', playlistData, songData, songIndex);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="delete" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 从云盘中删除 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isCloud"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
cloudSongMatchRef?.openCloudSongMatch(songData, songIndex);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="edit" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 云盘歌曲纠正 </n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="isSong && !isLocalSong"
|
||||
class="menu-item"
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
downloadSongRef?.openDownloadModal(songData);
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="22">
|
||||
<SvgIcon icon="download" />
|
||||
</n-icon>
|
||||
<n-text class="name"> 下载歌曲 </n-text>
|
||||
</div>
|
||||
</div>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
<!-- 添加到歌单 -->
|
||||
<AddPlaylist ref="addPlaylistRef" />
|
||||
<!-- 下载歌曲 -->
|
||||
<DownloadSong ref="downloadSongRef" />
|
||||
<!-- 云盘歌曲纠正 -->
|
||||
<CloudSongMatch ref="cloudSongMatchRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useRouter } from "vue-router";
|
||||
import { addSongToNext } from "@/utils/Player";
|
||||
import { musicData, siteData, siteStatus } from "@/stores";
|
||||
|
||||
const router = useRouter();
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const { playMode } = storeToRefs(status);
|
||||
const { userData, userLikeData } = storeToRefs(data);
|
||||
const emit = defineEmits(["playSong", "delCloudSong", "deletePlaylistSong", "delLocalSong"]);
|
||||
|
||||
// 子组件
|
||||
const addPlaylistRef = ref(null);
|
||||
const downloadSongRef = ref(null);
|
||||
const cloudSongMatchRef = ref(null);
|
||||
|
||||
// 菜单数据
|
||||
const drawerShow = ref(false);
|
||||
const songType = ref("song");
|
||||
const songData = ref(null);
|
||||
const songIndex = ref(null);
|
||||
const songSourceId = ref(null);
|
||||
const playlistData = ref(null);
|
||||
|
||||
// 歌曲状态
|
||||
const isFm = computed(() => playMode.value === "fm");
|
||||
const isSong = computed(() => songType.value === "song");
|
||||
const isLocalSong = computed(() => !!songData.value?.path);
|
||||
const isHasMv = computed(() => !!songData.value?.mv && songData.value.mv !== 0);
|
||||
const isCloud = computed(() => router.currentRoute.value.name === "cloud");
|
||||
const isUserPlaylist = computed(() => {
|
||||
// 用户 id
|
||||
const userId = userData.value?.userId;
|
||||
// 用户歌单
|
||||
const userPlaylistsData = userLikeData.value.playlists?.filter(
|
||||
(playlist) => playlist.userId === userId,
|
||||
);
|
||||
return songSourceId.value !== 0 && userPlaylistsData.some((pl) => pl.id == songSourceId.value);
|
||||
});
|
||||
|
||||
// 开启菜单
|
||||
const drawerOpen = (data, song, index, sourceId, type) => {
|
||||
console.log(song, type);
|
||||
drawerShow.value = true;
|
||||
songData.value = song;
|
||||
songType.value = type;
|
||||
songIndex.value = index;
|
||||
songSourceId.value = sourceId;
|
||||
playlistData.value = data;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
drawerOpen,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.song-data {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.cover {
|
||||
margin-right: 12px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.song-detail {
|
||||
.name {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.all-ar {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
.ar {
|
||||
display: inline-flex;
|
||||
&::after {
|
||||
content: "/";
|
||||
margin: 0 4px;
|
||||
}
|
||||
&:last-child {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.all-menu {
|
||||
.menu-item {
|
||||
padding: 12px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
transition: background-color 0.3s;
|
||||
cursor: pointer;
|
||||
.n-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.name {
|
||||
transform: translateY(1px);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--n-close-color-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.song-list-drawer {
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
</style>
|
||||
@@ -27,18 +27,15 @@ import { storeToRefs } from "pinia";
|
||||
import { musicData, siteData, siteStatus } from "@/stores";
|
||||
import { useRouter } from "vue-router";
|
||||
import { addSongToNext } from "@/utils/Player";
|
||||
import { setCloudDel } from "@/api/cloud";
|
||||
import { addSongToPlayList } from "@/api/playlist";
|
||||
import { copyData } from "@/utils/helper";
|
||||
import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const emit = defineEmits(["playSong"]);
|
||||
const emit = defineEmits(["playSong", "delCloudSong", "deletePlaylistSong", "delLocalSong"]);
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const router = useRouter();
|
||||
const status = siteStatus();
|
||||
const { playMode } = storeToRefs(status);
|
||||
const { playSongData } = storeToRefs(music);
|
||||
const { userData, userLikeData } = storeToRefs(data);
|
||||
|
||||
// 右键菜单数据
|
||||
@@ -74,7 +71,11 @@ const renderSong = (song, isSong) => {
|
||||
className: "song-data",
|
||||
},
|
||||
[
|
||||
h(NImage, { src: song?.coverSize?.s || song?.cover, class: "cover" }),
|
||||
h(NImage, {
|
||||
src: song?.coverSize?.s || song?.cover,
|
||||
class: "cover",
|
||||
previewDisabled: true,
|
||||
}),
|
||||
h("div", { class: "song-detail" }, [
|
||||
h(NText, { class: "name" }, () => [song?.name || "未知曲目"]),
|
||||
isSong
|
||||
@@ -111,8 +112,8 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
// 当前状态
|
||||
const isFm = playMode.value === "fm";
|
||||
const isSong = type === "song";
|
||||
const isLocalSong = song?.path ? true : false;
|
||||
const isHasMv = song.mv && song.mv !== 0 ? true : false;
|
||||
const isLocalSong = !!song?.path;
|
||||
const isHasMv = !!song?.mv && song.mv !== 0;
|
||||
const isCloud = router.currentRoute.value.name === "cloud";
|
||||
const isUserPlaylist = sourceId !== 0 && userPlaylistsData.some((pl) => pl.id == sourceId);
|
||||
// 生成菜单
|
||||
@@ -143,7 +144,7 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
{
|
||||
key: "next-play",
|
||||
label: "下一首播放",
|
||||
show: isSong && playMode.value !== "dj" && playSongData.value?.id !== song.id && !isFm,
|
||||
show: isSong && playMode.value !== "dj" && music.getPlaySongData?.id !== song.id && !isFm,
|
||||
props: {
|
||||
onClick: () => {
|
||||
playMode.value = "song";
|
||||
@@ -239,7 +240,7 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
show: !isCloud && isUserPlaylist,
|
||||
props: {
|
||||
onClick: () => {
|
||||
deletePlaylistSong(sourceId, song, data, index);
|
||||
emit("deletePlaylistSong", data, song, index);
|
||||
},
|
||||
},
|
||||
icon: renderIcon("delete"),
|
||||
@@ -261,7 +262,7 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
show: isCloud,
|
||||
props: {
|
||||
onClick: () => {
|
||||
delCloudSong(data, song, index);
|
||||
emit("delCloudSong", data, song, index);
|
||||
},
|
||||
},
|
||||
icon: renderIcon("delete"),
|
||||
@@ -280,10 +281,10 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
{
|
||||
key: "delete",
|
||||
label: "从本地磁盘中删除",
|
||||
show: isLocalSong && playSongData.value?.id !== song.id,
|
||||
show: isLocalSong && music.getPlaySongData?.id !== song.id,
|
||||
props: {
|
||||
onClick: () => {
|
||||
delLocalSong(data, song, index);
|
||||
emit("delLocalSong", data, song, index);
|
||||
},
|
||||
},
|
||||
icon: renderIcon("delete"),
|
||||
@@ -330,65 +331,6 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 云盘歌曲删除
|
||||
const delCloudSong = (data, song, index) => {
|
||||
console.log(data, song, index);
|
||||
$dialog.warning({
|
||||
title: "确认删除",
|
||||
content: `确认从云盘中删除 ${song.name}?该操作无法撤销!`,
|
||||
positiveText: "删除",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
const result = await setCloudDel(song.id);
|
||||
if (result.code == 200) {
|
||||
data.splice(index, 1);
|
||||
$message.success("删除成功");
|
||||
} else {
|
||||
$message.error("删除失败,请重试");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 歌单歌曲删除
|
||||
const deletePlaylistSong = (pid, song, data, index) => {
|
||||
$dialog.warning({
|
||||
title: "确认删除",
|
||||
content: `确认从歌单中移除 ${song.name}?该操作无法撤销!`,
|
||||
positiveText: "删除",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
const result = await addSongToPlayList(pid, song?.id, "del");
|
||||
if (result.status === 200) {
|
||||
data.length === 1 ? data.splice(0, 1, "empty") : data.splice(index, 1);
|
||||
$message.success("歌曲删除成功");
|
||||
} else {
|
||||
$message.error("歌曲删除失败,请重试");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 本地歌曲删除
|
||||
const delLocalSong = (data, song, index) => {
|
||||
$dialog.warning({
|
||||
title: "确认删除",
|
||||
content: `确认从本地磁盘中删除 ${song.name}?该操作无法撤销!`,
|
||||
positiveText: "删除",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
console.log(data, song, index);
|
||||
const result = await electron.ipcRenderer.invoke("deleteFile", song?.path);
|
||||
if (result) {
|
||||
data.length === 1 ? data.splice(0, 1, "empty") : data.splice(index, 1);
|
||||
$message.success("歌曲删除成功");
|
||||
} else {
|
||||
$message.error("歌曲删除失败,请重试");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDropdown,
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
</n-card>
|
||||
</Transition>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="closeCloudSongMatch"> 取消 </n-button>
|
||||
<n-button
|
||||
:disabled="!cloudMatchValue.asid"
|
||||
@@ -87,7 +87,7 @@
|
||||
>
|
||||
纠正
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<!-- 隐私歌单 -->
|
||||
<n-checkbox v-model:checked="createPrivacy"> 设为隐私歌单 </n-checkbox>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="closeCreatePlaylist"> 取消 </n-button>
|
||||
<n-button
|
||||
:disabled="!createName"
|
||||
@@ -30,7 +30,7 @@
|
||||
>
|
||||
新建
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
当前为云盘歌曲,下载的文件均为最高音质
|
||||
</n-alert>
|
||||
<n-radio-group v-model:value="downloadChoose" class="download-group" name="downloadGroup">
|
||||
<n-space vertical>
|
||||
<n-flex vertical>
|
||||
<n-radio
|
||||
v-for="item in downloadLevel"
|
||||
:key="item"
|
||||
@@ -32,13 +32,13 @@
|
||||
</n-text>
|
||||
</div>
|
||||
</n-radio>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
<n-text v-else>歌曲信息获取中</n-text>
|
||||
</Transition>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="closeDownloadModal"> 关闭 </n-button>
|
||||
<n-button
|
||||
:disabled="!downloadChoose"
|
||||
@@ -48,7 +48,7 @@
|
||||
>
|
||||
下载
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="loginModalShow"
|
||||
style="width: 400px"
|
||||
class="login"
|
||||
:auto-focus="false"
|
||||
:mask-closable="false"
|
||||
:bordered="false"
|
||||
:close-on-esc="false"
|
||||
:closable="false"
|
||||
style="width: 400px"
|
||||
preset="card"
|
||||
transform-origin="center"
|
||||
>
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="closeUpdateModal"> 取消 </n-button>
|
||||
<n-button type="primary" @click="toUpdatePlayList"> 编辑 </n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</n-text>
|
||||
</Transition>
|
||||
</div>
|
||||
<div class="navigation">
|
||||
<n-flex :class="['navigation', { hidden: searchInputFocus }]" :size="6">
|
||||
<n-button :focusable="false" class="nav-icon" quaternary @click="router.go(-1)">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
@@ -45,7 +45,7 @@
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</div>
|
||||
</n-flex>
|
||||
<!-- 搜索框 -->
|
||||
<SearchInp />
|
||||
<!-- GitHub -->
|
||||
@@ -69,7 +69,6 @@
|
||||
<div class="right">
|
||||
<!-- 全局菜单 -->
|
||||
<n-dropdown
|
||||
v-if="!showSider"
|
||||
:show="mainMenuShow"
|
||||
:show-arrow="true"
|
||||
:options="mainMenuOptions"
|
||||
@@ -78,7 +77,7 @@
|
||||
>
|
||||
<n-button
|
||||
:style="{ pointerEvents: mainMenuShow ? 'none' : 'auto' }"
|
||||
class="main-menu"
|
||||
:class="['main-menu', { show: !showSider }]"
|
||||
secondary
|
||||
strong
|
||||
round
|
||||
@@ -111,7 +110,7 @@ import packageJson from "@/../package.json";
|
||||
const router = useRouter();
|
||||
const status = siteStatus();
|
||||
const settings = siteSettings();
|
||||
const { asideMenuCollapsed } = storeToRefs(status);
|
||||
const { asideMenuCollapsed, searchInputFocus } = storeToRefs(status);
|
||||
const { showGithub, showSider, themeAutoCover } = storeToRefs(settings);
|
||||
|
||||
// 站点信息
|
||||
@@ -192,18 +191,32 @@ const mainMenuOptions = computed(() => [
|
||||
}
|
||||
}
|
||||
.navigation {
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
height: 34px;
|
||||
width: 86px;
|
||||
min-width: 86px;
|
||||
transition:
|
||||
width 0.3s,
|
||||
min-width 0.3s,
|
||||
opacity 0.3s;
|
||||
overflow: hidden;
|
||||
-webkit-app-region: no-drag;
|
||||
.nav-icon {
|
||||
border-radius: 8px;
|
||||
padding: 0 8px;
|
||||
&:first-child {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.n-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
width: 0px;
|
||||
min-width: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.github {
|
||||
margin-left: 12px;
|
||||
@@ -212,7 +225,15 @@ const mainMenuOptions = computed(() => [
|
||||
.main-menu {
|
||||
-webkit-app-region: no-drag;
|
||||
margin-right: 12px;
|
||||
display: none;
|
||||
&.show {
|
||||
display: flex;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-sider {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
@@ -226,5 +247,25 @@ const mainMenuOptions = computed(() => [
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.left {
|
||||
.logo {
|
||||
width: auto;
|
||||
padding-left: 0;
|
||||
margin-right: 12px;
|
||||
.site-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.left {
|
||||
width: 100%;
|
||||
}
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -219,6 +219,15 @@ const userMenuSelect = (key) => {
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
padding: 0;
|
||||
.avatar {
|
||||
margin: 0;
|
||||
}
|
||||
.user-data {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -91,6 +91,14 @@ const pointOpacity = (index) => {
|
||||
margin-right: 12px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--cover-main-color);
|
||||
@media (max-width: 900px) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes breathe {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}"
|
||||
class="full-player"
|
||||
@mousemove="controlShowChange"
|
||||
@mouseleave="playerControlShow = false"
|
||||
@mouseleave="closePlayerControlShow"
|
||||
>
|
||||
<!-- 遮罩 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
@@ -55,7 +55,7 @@
|
||||
</div>
|
||||
<div class="right">
|
||||
<!-- 全屏切换 -->
|
||||
<n-icon @click.stop="screenfullChange">
|
||||
<n-icon class="hidden" @click.stop="screenfullChange">
|
||||
<SvgIcon
|
||||
:icon="screenfullStatus ? 'fullscreen-exit-rounded' : 'fullscreen-rounded'"
|
||||
/>
|
||||
@@ -77,7 +77,7 @@
|
||||
<!-- 封面 -->
|
||||
<PlayerCover />
|
||||
<!-- 信息 -->
|
||||
<div v-if="playCoverType === 'cover' || !isHasLrc" :class="['data', playCoverType]">
|
||||
<div v-show="playCoverType === 'cover' || !isHasLrc" :class="['data', playCoverType]">
|
||||
<div class="desc">
|
||||
<div class="title">
|
||||
<span class="name">{{ music.getPlaySongData.name || "未知曲目" }}</span>
|
||||
@@ -272,9 +272,16 @@ const screenfullChange = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭控制中心
|
||||
const closePlayerControlShow = () => {
|
||||
if (window.innerWidth <= 700) return false;
|
||||
playerControlShow.value = false;
|
||||
};
|
||||
|
||||
// 控制中心显隐
|
||||
const controlShowChange = throttle(() => {
|
||||
playerControlShow.value = true;
|
||||
if (window.innerWidth <= 700) return false;
|
||||
if (controlTimeOut.value) {
|
||||
clearTimeout(controlTimeOut.value);
|
||||
}
|
||||
@@ -655,6 +662,41 @@ onUnmounted(() => {
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.menu {
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.main-player {
|
||||
.content {
|
||||
width: 100%;
|
||||
.data {
|
||||
display: block !important;
|
||||
&.record {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
&.no-lrc {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
.right {
|
||||
display: none;
|
||||
.data {
|
||||
.name {
|
||||
font-size: 24px;
|
||||
.name-alias {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.other {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 局外样式
|
||||
.title-tip {
|
||||
|
||||
@@ -493,8 +493,8 @@ onMounted(() => {
|
||||
}
|
||||
&.record,
|
||||
&.pure {
|
||||
height: calc(100vh - 340px);
|
||||
margin-bottom: 20px;
|
||||
height: calc(100vh - 300px);
|
||||
margin-bottom: 40px;
|
||||
.lrc-line {
|
||||
margin-bottom: -12px;
|
||||
transform: scale(0.76);
|
||||
@@ -503,5 +503,21 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
:deep(.n-scrollbar-content) {
|
||||
padding: 0 20px !important;
|
||||
}
|
||||
.lrc-line {
|
||||
.lrc-content {
|
||||
font-size: 6.5vw !important;
|
||||
}
|
||||
.lrc-fy {
|
||||
font-size: 4.5vw !important;
|
||||
}
|
||||
.lrc-roma {
|
||||
font-size: 4vw !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div :key="playMode" class="menu">
|
||||
<!-- 时间进度 -->
|
||||
<div class="time">
|
||||
<div class="time hidden">
|
||||
<n-text class="played" depth="3">{{ playTimeData.played }}</n-text>
|
||||
<n-text depth="3">{{ playTimeData.durationTime }}</n-text>
|
||||
</div>
|
||||
@@ -221,7 +221,7 @@
|
||||
trigger="hover"
|
||||
@select="playModeChange"
|
||||
>
|
||||
<div class="mode" @click.stop @dblclick.stop>
|
||||
<div class="mode hidden" @click.stop @dblclick.stop>
|
||||
<n-icon size="22">
|
||||
<SvgIcon
|
||||
:icon="
|
||||
@@ -241,7 +241,7 @@
|
||||
<!-- 倍速 -->
|
||||
<n-popover :show-arrow="false" trigger="hover" placement="top-end" raw>
|
||||
<template #trigger>
|
||||
<div class="speed" @click.stop="(playRate = 1), setRate(1)" @dblclick.stop>
|
||||
<div class="speed hidden" @click.stop="(playRate = 1), setRate(1)" @dblclick.stop>
|
||||
<n-icon v-if="playRate === 1" size="22">
|
||||
<SvgIcon icon="speed-rounded" />
|
||||
</n-icon>
|
||||
@@ -269,7 +269,12 @@
|
||||
<!-- 音量 -->
|
||||
<n-popover trigger="hover" :show-arrow="false" raw>
|
||||
<template #trigger>
|
||||
<n-icon class="volume" size="22" @click.stop="setVolumeMute" @wheel="changeVolume">
|
||||
<n-icon
|
||||
class="volume hidden"
|
||||
size="22"
|
||||
@click.stop="setVolumeMute"
|
||||
@wheel="changeVolume"
|
||||
>
|
||||
<SvgIcon v-if="playVolume === 0" icon="no-sound-rounded" />
|
||||
<SvgIcon
|
||||
v-else-if="playVolume > 0 && playVolume < 0.4"
|
||||
@@ -288,7 +293,7 @@
|
||||
padding: '10px 0',
|
||||
width: '50px',
|
||||
}"
|
||||
class="slider-content"
|
||||
class="slider-content hidden"
|
||||
@wheel="changeVolume"
|
||||
>
|
||||
<n-slider
|
||||
@@ -643,12 +648,6 @@ watch(
|
||||
}
|
||||
}
|
||||
&.record {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
max-height: 60px;
|
||||
min-height: 60px;
|
||||
max-width: 60px;
|
||||
min-width: 60px;
|
||||
.cover-img {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -860,6 +859,31 @@ watch(
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.menu {
|
||||
.time {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.control {
|
||||
margin-left: auto;
|
||||
.play-prev,
|
||||
.play-next {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.show-bar {
|
||||
bottom: 0;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<!-- 喜欢歌曲 -->
|
||||
<n-icon
|
||||
v-if="!music.getPlaySongData.path"
|
||||
class="favorite"
|
||||
size="24"
|
||||
@click.stop="
|
||||
data.changeLikeList(
|
||||
@@ -32,7 +31,7 @@
|
||||
<!-- 添加到歌单 -->
|
||||
<n-icon
|
||||
v-if="!music.getPlaySongData.path"
|
||||
class="favorite"
|
||||
class="hidden"
|
||||
size="24"
|
||||
@click.stop="addPlaylistRef?.openAddToPlaylist(music.getPlaySongData?.id)"
|
||||
>
|
||||
@@ -41,7 +40,7 @@
|
||||
<!-- 下载 -->
|
||||
<n-icon
|
||||
v-if="!music.getPlaySongData.path"
|
||||
class="favorite"
|
||||
class="hidden"
|
||||
size="24"
|
||||
@click.stop="downloadSongRef?.openDownloadModal(music.getPlaySongData)"
|
||||
>
|
||||
@@ -113,7 +112,7 @@
|
||||
<!-- MV -->
|
||||
<n-icon
|
||||
v-if="music.getPlaySongData.mv"
|
||||
class="favorite"
|
||||
class="hidden"
|
||||
size="22"
|
||||
@click.stop="
|
||||
(showFullPlayer = false), router.push(`/videos-player?id=${music.getPlaySongData.mv}`)
|
||||
@@ -124,6 +123,7 @@
|
||||
<!-- 评论 -->
|
||||
<n-icon
|
||||
v-if="!music.getPlaySongData?.path"
|
||||
class="hidden"
|
||||
size="22"
|
||||
@click.stop="
|
||||
(showFullPlayer = false), router.push(`/comment?id=${music.getPlaySongData?.id}`)
|
||||
@@ -132,7 +132,7 @@
|
||||
<SvgIcon icon="comment-text" />
|
||||
</n-icon>
|
||||
<!-- 播放模式 -->
|
||||
<n-icon v-if="playMode === 'normal'" size="22" @click.stop="togglePlayMode">
|
||||
<n-icon v-if="playMode === 'normal'" class="hidden" size="22" @click.stop="togglePlayMode">
|
||||
<SvgIcon
|
||||
:icon="
|
||||
playHeartbeatMode
|
||||
@@ -381,5 +381,21 @@ const controlMove = (e) => {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
.left,
|
||||
.right {
|
||||
opacity: 1;
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.center {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -206,5 +206,18 @@ const { playState } = storeToRefs(status);
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
&.record {
|
||||
.pointer {
|
||||
width: 12vh;
|
||||
top: -6vh;
|
||||
}
|
||||
.cover-img {
|
||||
width: 40vh;
|
||||
height: 40vh;
|
||||
min-width: 40vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,25 +2,26 @@
|
||||
<template>
|
||||
<Transition name="fadeDown" mode="out-in" @before-enter="getSearchHotData">
|
||||
<n-card
|
||||
v-if="status.searchInputFocus && !searchValue && (data.searchHistory[0] || searchHotData[0])"
|
||||
v-if="searchInputFocus && !searchValue && (searchHistory[0] || searchHotData[0])"
|
||||
class="search-suggestions"
|
||||
content-style="padding: 0"
|
||||
@click="emit('closeSearch')"
|
||||
>
|
||||
<n-scrollbar class="scrollbar">
|
||||
<!-- 历史记录 -->
|
||||
<div v-if="settings.searchHistory && data.searchHistory[0]" class="history">
|
||||
<div v-if="showSearchHistory && searchHistory[0]" class="history">
|
||||
<div class="title">
|
||||
<n-icon>
|
||||
<SvgIcon icon="history" />
|
||||
</n-icon>
|
||||
<n-text>搜索历史</n-text>
|
||||
<n-icon class="history-delete" depth="3" @click="delSearchHistory">
|
||||
<n-icon class="history-delete" depth="3" @click.stop="delSearchHistory">
|
||||
<SvgIcon icon="delete" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-flex>
|
||||
<n-tag
|
||||
v-for="(item, index) in data.searchHistory"
|
||||
v-for="(item, index) in searchHistory"
|
||||
:key="index"
|
||||
:bordered="false"
|
||||
round
|
||||
@@ -28,7 +29,7 @@
|
||||
>
|
||||
{{ item }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
<!-- 热搜榜 -->
|
||||
<div v-if="searchHotData[0]" class="hot-list">
|
||||
@@ -70,6 +71,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { storeToRefs } from "pinia";
|
||||
import { siteData, siteStatus, siteSettings } from "@/stores";
|
||||
import { getSearchHot } from "@/api/search";
|
||||
import { getCacheData } from "@/utils/helper";
|
||||
@@ -77,7 +79,10 @@ import { getCacheData } from "@/utils/helper";
|
||||
const data = siteData();
|
||||
const status = siteStatus();
|
||||
const settings = siteSettings();
|
||||
const emit = defineEmits(["toSearch", "delSearchHistory"]);
|
||||
const { searchHistory } = storeToRefs(data);
|
||||
const { searchInputFocus } = storeToRefs(status);
|
||||
const { showSearchHistory } = storeToRefs(settings);
|
||||
const emit = defineEmits(["toSearch", "closeSearch"]);
|
||||
|
||||
// 搜索内容
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@@ -117,7 +122,7 @@ const delSearchHistory = () => {
|
||||
positiveText: "确认",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: () => {
|
||||
data.searchHistory = [];
|
||||
searchHistory.value = [];
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -176,7 +181,7 @@ onBeforeMount(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-space {
|
||||
.n-flex {
|
||||
margin-top: 8px;
|
||||
.n-tag {
|
||||
font-size: 13px;
|
||||
@@ -267,5 +272,14 @@ onBeforeMount(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 512px) {
|
||||
position: fixed;
|
||||
top: 58px;
|
||||
border-radius: 0px;
|
||||
width: 100%;
|
||||
:deep(.scrollbar) {
|
||||
max-height: calc(100vh - 58px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
<n-input
|
||||
ref="searchInpRef"
|
||||
v-model:value="searchInputValue"
|
||||
:class="status.searchInputFocus ? 'input focus' : 'input'"
|
||||
:class="searchInputFocus ? 'input focus' : 'input'"
|
||||
:input-props="{ autoComplete: false }"
|
||||
:allow-input="noSideSpace"
|
||||
placeholder="搜索音乐 / 视频"
|
||||
round
|
||||
clearable
|
||||
@focus="searchInputFocus"
|
||||
@focus="searchInputToFocus"
|
||||
@keyup.enter="toSearch(searchInputValue)"
|
||||
@click.stop
|
||||
>
|
||||
@@ -22,16 +22,20 @@
|
||||
</n-input>
|
||||
<!-- 搜索框遮罩 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div
|
||||
v-show="status.searchInputFocus"
|
||||
class="search-mask"
|
||||
@click.stop="status.searchInputFocus = false"
|
||||
/>
|
||||
<div v-show="searchInputFocus" class="search-mask" @click.stop="searchInputFocus = false" />
|
||||
</Transition>
|
||||
<!-- 热搜榜及历史 -->
|
||||
<SearchHot :searchValue="searchInputValue?.trim()" @toSearch="toSearch" />
|
||||
<SearchHot
|
||||
:searchValue="searchInputValue?.trim()"
|
||||
@toSearch="toSearch"
|
||||
@closeSearch="closeSearch"
|
||||
/>
|
||||
<!-- 搜索建议 -->
|
||||
<SearchSuggestions :searchValue="searchInputValue?.trim()" @toSearch="toSearch" />
|
||||
<SearchSuggestions
|
||||
:searchValue="searchInputValue?.trim()"
|
||||
@toSearch="toSearch"
|
||||
@closeSearch="closeSearch"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -47,7 +51,9 @@ const router = useRouter();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const data = siteData();
|
||||
const { searchHistory } = storeToRefs(data);
|
||||
const { playSongData } = storeToRefs(music);
|
||||
const { searchInputFocus } = storeToRefs(status);
|
||||
|
||||
const searchInpRef = ref(null);
|
||||
const searchInputValue = ref("");
|
||||
@@ -56,24 +62,31 @@ const searchInputValue = ref("");
|
||||
const noSideSpace = (value) => !value.startsWith(" ");
|
||||
|
||||
// 搜索框 focus
|
||||
const searchInputFocus = () => {
|
||||
const searchInputToFocus = () => {
|
||||
searchInpRef.value?.focus();
|
||||
status.searchInputFocus = true;
|
||||
searchInputFocus.value = true;
|
||||
};
|
||||
|
||||
// 添加搜索历史
|
||||
const setSearchHistory = (name) => {
|
||||
if (!name || !name?.trim()) return false;
|
||||
const index = data.searchHistory.indexOf(name);
|
||||
const index = searchHistory.value.indexOf(name);
|
||||
if (index !== -1) {
|
||||
data.searchHistory.splice(index, 1);
|
||||
searchHistory.value.splice(index, 1);
|
||||
}
|
||||
data.searchHistory.unshift(name);
|
||||
if (data.searchHistory.length > 30) {
|
||||
data.searchHistory.pop();
|
||||
searchHistory.value.unshift(name);
|
||||
if (searchHistory.value.length > 30) {
|
||||
searchHistory.value.pop();
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭搜索
|
||||
const closeSearch = () => {
|
||||
// 取消聚焦状态
|
||||
status.searchInputFocus = false;
|
||||
searchInpRef.value?.blur();
|
||||
};
|
||||
|
||||
// 直接播放单曲
|
||||
const toPlaySong = async (id) => {
|
||||
try {
|
||||
@@ -96,8 +109,7 @@ const toPlaySong = async (id) => {
|
||||
const toSearch = (val, type = "song") => {
|
||||
if (!val) return false;
|
||||
// 取消聚焦状态
|
||||
status.searchInputFocus = false;
|
||||
searchInpRef.value?.blur();
|
||||
closeSearch();
|
||||
// 触发测试
|
||||
if (Number(val) === 114514) return router.push("/test");
|
||||
// 判断类型
|
||||
@@ -174,5 +186,21 @@ const toSearch = (val, type = "song") => {
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
width: 100%;
|
||||
margin-right: 12px;
|
||||
.input {
|
||||
width: 100%;
|
||||
&.focus {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 512px) {
|
||||
.search-mask {
|
||||
background-color: transparent;
|
||||
backdrop-filter: blur(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
@after-leave="changeSuggestionsHeights"
|
||||
>
|
||||
<n-card
|
||||
v-if="status.searchInputFocus && searchValue"
|
||||
v-if="searchInputFocus && searchValue"
|
||||
class="search-suggestions"
|
||||
content-style="padding: 0"
|
||||
:style="{
|
||||
height: `${suggestionsHeights}px`,
|
||||
border: suggestionsHeights === 0 ? 'none' : null,
|
||||
}"
|
||||
@click="emit('closeSearch')"
|
||||
>
|
||||
<n-scrollbar class="scrollbar">
|
||||
<!-- 直接搜索 -->
|
||||
@@ -61,12 +62,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { storeToRefs } from "pinia";
|
||||
import { siteStatus } from "@/stores";
|
||||
import { getSearchSuggest } from "@/api/search";
|
||||
import debounce from "@/utils/debounce";
|
||||
|
||||
const status = siteStatus();
|
||||
const emit = defineEmits(["toSearch"]);
|
||||
const { searchInputFocus } = storeToRefs(status);
|
||||
const emit = defineEmits(["toSearch", "closeSearch"]);
|
||||
|
||||
// 搜索内容
|
||||
const props = defineProps({
|
||||
@@ -233,5 +236,17 @@ watch(
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 512px) {
|
||||
position: fixed;
|
||||
top: 58px;
|
||||
border-radius: 0px;
|
||||
width: 100%;
|
||||
max-height: calc(100vh - 58px);
|
||||
min-height: calc(100vh - 58px);
|
||||
:deep(.scrollbar) {
|
||||
max-height: calc(100vh - 58px);
|
||||
min-height: calc(100vh - 58px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,15 +47,15 @@
|
||||
<n-text class="close-tip">确认关闭软件吗?</n-text>
|
||||
<n-checkbox v-model:checked="closeTipCheckbox"> 记住且不再询问 </n-checkbox>
|
||||
<template #footer>
|
||||
<n-space justify="space-between">
|
||||
<n-flex justify="space-between">
|
||||
<n-button strong secondary @click="closeCloseTip('cancel')"> 取消 </n-button>
|
||||
<n-space class="type">
|
||||
<n-flex class="type">
|
||||
<n-button strong secondary @click="closeCloseTip('close')"> 退出 </n-button>
|
||||
<n-button type="primary" strong secondary @click="closeCloseTip('hide')">
|
||||
最小化
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +37,18 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
if (typeof $changeLogin !== "undefined") $changeLogin();
|
||||
}
|
||||
}
|
||||
// 是否为本地功能
|
||||
else if (to.meta.needLocal) {
|
||||
if (checkPlatform.electron()) {
|
||||
next();
|
||||
} else {
|
||||
$message.error("客户端独占功能");
|
||||
if (typeof $loadingBar !== "undefined" && !checkPlatform.electron()) {
|
||||
$loadingBar.error();
|
||||
}
|
||||
next("/403");
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -112,6 +112,15 @@ const routes = [
|
||||
},
|
||||
component: () => import("@/views/Comment.vue"),
|
||||
},
|
||||
// 歌曲详情
|
||||
{
|
||||
path: "/song",
|
||||
name: "song",
|
||||
meta: {
|
||||
title: "歌曲详情",
|
||||
},
|
||||
component: () => import("@/views/Song.vue"),
|
||||
},
|
||||
// 最近播放
|
||||
{
|
||||
path: "/history",
|
||||
@@ -244,7 +253,7 @@ const routes = [
|
||||
name: "local",
|
||||
meta: {
|
||||
title: "本地歌曲",
|
||||
needLogin: true,
|
||||
needLocal: true,
|
||||
show: checkPlatform.electron(),
|
||||
},
|
||||
component: () => import("@/views/Local/index.vue"),
|
||||
|
||||
@@ -8,7 +8,7 @@ const useSiteSettingsStore = defineStore("siteSettings", {
|
||||
closeTip: true, // 关闭软件提醒弹窗
|
||||
closeType: "hide", // 关闭方式 close 直接关闭 / hide 最小化到任务栏
|
||||
showTaskbarProgress: false, // 显示歌曲任务栏进度
|
||||
searchHistory: true, // 搜索历史
|
||||
showSearchHistory: true, // 搜索历史
|
||||
autoSignIn: true, // 自动签到
|
||||
showGithub: true,
|
||||
showSider: true, // 显示侧边栏
|
||||
|
||||
@@ -19,7 +19,7 @@ const useSiteStatusStore = defineStore("siteStatus", {
|
||||
// 全屏播放器状态
|
||||
showFullPlayer: false,
|
||||
// 播放器功能显示
|
||||
playerControlShow: false,
|
||||
playerControlShow: true,
|
||||
controlTimeOut: null,
|
||||
// 实时播放进度
|
||||
playSeek: 0,
|
||||
|
||||
@@ -97,6 +97,7 @@ body,
|
||||
.n-tabs {
|
||||
--n-tab-border-radius: 6px !important;
|
||||
.n-tabs-rail {
|
||||
position: relative;
|
||||
.n-tabs-tab-wrapper {
|
||||
.n-tabs-tab {
|
||||
&:hover {
|
||||
|
||||
@@ -24,13 +24,15 @@ let spectrumsData = {
|
||||
analyser: null,
|
||||
audioCtx: null,
|
||||
};
|
||||
// 默认标题
|
||||
let defaultTitle = document.title;
|
||||
|
||||
/**
|
||||
* 初始化播放器
|
||||
*/
|
||||
export const initPlayer = async (playNow = false) => {
|
||||
try {
|
||||
// 停止播放当前歌曲
|
||||
// 停止播放器
|
||||
soundStop();
|
||||
// 获取基础数据
|
||||
const music = musicData();
|
||||
@@ -211,17 +213,19 @@ export const createPlayer = async (src, autoPlay = true) => {
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const settings = siteSettings();
|
||||
const { playMode } = status;
|
||||
const { playSongSource, playList } = music;
|
||||
const { showSpectrums, memorySeek, useMusicCache } = settings;
|
||||
// 当前播放歌曲数据
|
||||
const playSongData = music.getPlaySongData;
|
||||
// 获取播放链接
|
||||
const blobUrl = useMusicCache ? await getBlobUrlFromUrl(src) : src;
|
||||
console.log("播放地址:", blobUrl);
|
||||
// 获取播放链接(非电台及云盘歌曲)
|
||||
const songUrl =
|
||||
useMusicCache && playMode !== "dj" && !playSongData.pc ? await getBlobUrlFromUrl(src) : src;
|
||||
console.log("播放地址:", songUrl);
|
||||
// 初始化播放器
|
||||
if (player) soundStop();
|
||||
player = new Howl({
|
||||
src: [blobUrl],
|
||||
src: [songUrl],
|
||||
format: ["mp3", "flac", "dolby", "webm"],
|
||||
html5: true,
|
||||
preload: "metadata",
|
||||
@@ -255,14 +259,7 @@ export const createPlayer = async (src, autoPlay = true) => {
|
||||
status.playLoading = false;
|
||||
// 发送歌曲名
|
||||
if (checkPlatform.electron()) {
|
||||
const songName = playSongData.name || "未知曲目";
|
||||
const songArtist =
|
||||
status.playMode === "dj"
|
||||
? "电台节目"
|
||||
: Array.isArray(playSongData.artists)
|
||||
? playSongData.artists.map((ar) => ar.name).join(" / ")
|
||||
: playSongData.artists || "未知歌手";
|
||||
electron.ipcRenderer.send("songNameChange", songName + " - " + songArtist);
|
||||
electron.ipcRenderer.send("songNameChange", getPlaySongName());
|
||||
}
|
||||
// 听歌打卡
|
||||
if (isLogin() && !playSongData?.path) {
|
||||
@@ -284,6 +281,8 @@ export const createPlayer = async (src, autoPlay = true) => {
|
||||
if (checkPlatform.electron()) {
|
||||
electron.ipcRenderer.send("songStateChange", true);
|
||||
}
|
||||
// 更改页面标题
|
||||
if (!checkPlatform.electron()) document.title = getPlaySongName();
|
||||
});
|
||||
// 暂停播放
|
||||
player?.on("pause", () => {
|
||||
@@ -295,6 +294,8 @@ export const createPlayer = async (src, autoPlay = true) => {
|
||||
if (checkPlatform.electron()) {
|
||||
electron.ipcRenderer.send("songStateChange", false);
|
||||
}
|
||||
// 更改页面标题
|
||||
if (!checkPlatform.electron()) document.title = defaultTitle || "SPlayer";
|
||||
});
|
||||
// 结束播放
|
||||
player?.on("end", () => {
|
||||
@@ -759,6 +760,25 @@ const updateSpectrums = (analyser, dataArray) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前播放歌曲名
|
||||
*/
|
||||
const getPlaySongName = () => {
|
||||
// pinia
|
||||
const status = siteStatus();
|
||||
const music = musicData();
|
||||
const playSongData = music.getPlaySongData;
|
||||
// 返回歌曲数据
|
||||
const songName = playSongData.name || "未知曲目";
|
||||
const songArtist =
|
||||
status.playMode === "dj"
|
||||
? "电台节目"
|
||||
: Array.isArray(playSongData.artists)
|
||||
? playSongData.artists.map((ar) => ar.name).join(" / ")
|
||||
: playSongData.artists || "未知歌手";
|
||||
return songName + " - " + songArtist;
|
||||
};
|
||||
|
||||
/*
|
||||
* 清除定时器
|
||||
*/
|
||||
|
||||
@@ -10,17 +10,7 @@
|
||||
</div>
|
||||
</n-h4>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<SongList
|
||||
v-if="artistHotSongs"
|
||||
:data="artistHotSongs"
|
||||
:showPagination="false"
|
||||
:showTitle="false"
|
||||
/>
|
||||
<div v-else class="loading">
|
||||
<n-skeleton :repeat="10" text />
|
||||
</div>
|
||||
</Transition>
|
||||
<SongList :data="artistHotSongs" :showPagination="false" :showTitle="false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -90,13 +80,5 @@ onBeforeMount(async () => {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
:deep(.n-skeleton) {
|
||||
&:nth-of-type(1) {
|
||||
margin-top: 0;
|
||||
}
|
||||
height: 70px;
|
||||
margin-top: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
{{ artistData.identify }}
|
||||
</n-text>
|
||||
<!-- 数量 -->
|
||||
<n-space class="num">
|
||||
<n-flex class="num">
|
||||
<div
|
||||
v-if="artistData.size?.music"
|
||||
class="num-item"
|
||||
@@ -69,7 +69,7 @@
|
||||
</n-icon>
|
||||
<n-text depth="3">{{ artistData.size.mv }}</n-text>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 简介 -->
|
||||
<n-ellipsis
|
||||
v-if="artistData?.description"
|
||||
@@ -82,7 +82,7 @@
|
||||
</n-ellipsis>
|
||||
<n-text v-else class="description">哇!竟然没有简介</n-text>
|
||||
<!-- 功能区 -->
|
||||
<n-space class="menu" justify="space-between">
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-button size="large" round strong secondary @click="likeOrDislike(artistId)">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
@@ -91,7 +91,7 @@
|
||||
</template>
|
||||
{{ isLikeOrDislike(artistId) ? "关注歌手" : "取消关注" }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="detail">
|
||||
@@ -326,6 +326,33 @@ onBeforeMount(async () => {
|
||||
.tabs {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.cover {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
min-width: 200px;
|
||||
margin: 0;
|
||||
}
|
||||
.data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
.name {
|
||||
font-size: 26px;
|
||||
}
|
||||
.identify {
|
||||
font-size: 16px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
</div>
|
||||
</n-progress>
|
||||
<!-- 功能区 -->
|
||||
<n-space class="menu" justify="space-between">
|
||||
<n-space class="left">
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-flex class="left">
|
||||
<n-button type="primary" class="play" circle strong secondary @click="playAllSongs">
|
||||
<template #icon>
|
||||
<n-icon size="32">
|
||||
@@ -34,8 +34,8 @@
|
||||
</n-button>
|
||||
<!-- 歌曲上传弹窗 -->
|
||||
<UpCloudSong ref="upCloudSongRef" @getUserCloudData="getUserCloudData" />
|
||||
</n-space>
|
||||
<n-space class="right">
|
||||
</n-flex>
|
||||
<n-flex class="right">
|
||||
<!-- 模糊搜索 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-input
|
||||
@@ -54,8 +54,8 @@
|
||||
</template>
|
||||
</n-input>
|
||||
</Transition>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="userCloudData !== 'empty'" class="list">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</Transition>
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<n-space class="control">
|
||||
<n-flex class="control">
|
||||
<n-button size="large" tag="div" round strong secondary @click="playAllSongs">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
@@ -30,7 +30,7 @@
|
||||
</template>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<SongList :data="dailySongsData.data" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="dsc-artists">
|
||||
<div class="menu">
|
||||
<!-- 字母分类 -->
|
||||
<n-space class="initial">
|
||||
<n-flex class="initial">
|
||||
<n-tag
|
||||
v-for="item in artistInitials"
|
||||
:key="item"
|
||||
@@ -14,9 +14,9 @@
|
||||
>
|
||||
{{ item.value }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 地区分类 -->
|
||||
<n-space class="category">
|
||||
<n-flex class="category">
|
||||
<n-tag
|
||||
v-for="(item, index) in artistTypeNames"
|
||||
:key="item"
|
||||
@@ -31,10 +31,10 @@
|
||||
>
|
||||
{{ item }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
<MainCover :data="artistsData" columns="3 s:4 m:5 l:6" type="artist" />
|
||||
<n-space v-if="arHasMore" justify="center">
|
||||
<n-flex v-if="arHasMore" justify="center">
|
||||
<n-button
|
||||
:loading="arIsLoading"
|
||||
class="load-more"
|
||||
@@ -46,7 +46,7 @@
|
||||
>
|
||||
加载更多
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<!-- 发现 - 最新音乐 -->
|
||||
<template>
|
||||
<div class="dsc-new">
|
||||
<n-space class="menu" justify="space-between">
|
||||
<n-space class="type">
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-flex class="type">
|
||||
<n-tag
|
||||
v-for="(item, index) in newTypeNames"
|
||||
:key="index"
|
||||
@@ -14,8 +14,8 @@
|
||||
>
|
||||
{{ item }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
<n-space class="area">
|
||||
</n-flex>
|
||||
<n-flex class="area">
|
||||
<n-tag
|
||||
v-for="(item, index) in newAreaNames"
|
||||
:key="index"
|
||||
@@ -27,8 +27,8 @@
|
||||
>
|
||||
{{ item.value }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="newTypeChoose === 0" class="new-album">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<div class="dsc-playlists">
|
||||
<!-- 菜单 -->
|
||||
<n-space class="menu" align="center" justify="space-between">
|
||||
<n-flex class="menu" align="center" justify="space-between">
|
||||
<!-- 分类选择 -->
|
||||
<n-button
|
||||
class="cat"
|
||||
@@ -20,12 +20,12 @@
|
||||
{{ catName }}
|
||||
</n-button>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-space v-if="getHaveHqPlaylists(data.plCatList.hqCatList, catName)" align="center">
|
||||
<n-flex v-if="getHaveHqPlaylists(data.plCatList.hqCatList, catName)" align="center">
|
||||
<n-text>精品歌单</n-text>
|
||||
<n-switch v-model:value="hqPlOpen" :round="false" @update:value="hqPlOpenChange" />
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</Transition>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<MainCover :data="allPlData" />
|
||||
<!-- 分页 -->
|
||||
@@ -37,7 +37,7 @@
|
||||
/>
|
||||
<!-- 加载更多 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-space justify="center">
|
||||
<n-flex justify="center">
|
||||
<n-button
|
||||
v-if="hqPlOpen && plHasMore"
|
||||
:loading="plHasLoading"
|
||||
@@ -55,7 +55,7 @@
|
||||
>
|
||||
加载更多
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</Transition>
|
||||
<!-- 分类切换 -->
|
||||
<n-modal v-model:show="catChangeShow" :bordered="false" preset="card">
|
||||
@@ -80,7 +80,7 @@
|
||||
<template #prefix>
|
||||
<n-text class="type"> {{ cat }} </n-text>
|
||||
</template>
|
||||
<n-space>
|
||||
<n-flex>
|
||||
<n-tag
|
||||
v-for="item in data.plCatList.catList.filter((v) => v.category == key)"
|
||||
:key="item"
|
||||
@@ -98,7 +98,7 @@
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</n-h1>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="historyPlaylist?.length" class="list">
|
||||
<n-space class="menu">
|
||||
<n-flex class="menu">
|
||||
<n-button round strong secondary @click="cleanHistory">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
@@ -16,7 +16,7 @@
|
||||
</template>
|
||||
清空列表
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<SongList :data="historyPlaylist" :showCover="false" />
|
||||
<n-divider class="tip" dashed>
|
||||
<n-text :depth="3"> 最多展示 500 条播放历史 </n-text>
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
</n-gi>
|
||||
<!-- 喜欢的音乐 -->
|
||||
<n-gi>
|
||||
<SpecialCover
|
||||
:data="likeSongsCoverData"
|
||||
:showIcon="false"
|
||||
@click="jumpPage('like-songs')"
|
||||
/>
|
||||
<SpecialCover :data="likeSongsCoverData" @click="jumpPage('like-songs')" />
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<PrivateFm class="rec-fm" />
|
||||
@@ -60,7 +56,8 @@ import {
|
||||
getTopArtists,
|
||||
getNewAlbum,
|
||||
} from "@/api/recommend";
|
||||
import { getDjPersonalRec } from "@/api/dj";
|
||||
import { allMv } from "@/api/video";
|
||||
import { getDjRecommend } from "@/api/dj";
|
||||
import { siteData, siteSettings } from "@/stores";
|
||||
import { getCacheData } from "@/utils/helper";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
@@ -91,6 +88,7 @@ const dailySongsCoverData = computed(() => {
|
||||
// 喜欢的音乐
|
||||
const likeSongsCoverData = computed(() => {
|
||||
const likeSongsCover = {
|
||||
id: 1024,
|
||||
name: "喜欢的音乐",
|
||||
desc: "发现你独特的音乐品味",
|
||||
};
|
||||
@@ -129,12 +127,12 @@ const recommendData = ref({
|
||||
mv: {
|
||||
name: "推荐 MV",
|
||||
type: "mv",
|
||||
columns: "1 s:2 m:3 l:4 xl:5",
|
||||
loadingNum: 2,
|
||||
columns: "2 s:2 m:3 l:4 xl:5",
|
||||
loadingNum: 12,
|
||||
data: [],
|
||||
},
|
||||
dj: {
|
||||
name: "专属播客",
|
||||
name: "推荐播客",
|
||||
type: "dj",
|
||||
loadingNum: 6,
|
||||
data: [],
|
||||
@@ -162,9 +160,9 @@ const getRecommendData = async () => {
|
||||
// 歌手
|
||||
getCacheData("recAr", 5, getTopArtists),
|
||||
// MV
|
||||
getCacheData("recMv", 5, getPersonalized, "mv"),
|
||||
getCacheData("recMv", 5, allMv),
|
||||
// 电台
|
||||
getCacheData("recDj", 5, getDjPersonalRec),
|
||||
getCacheData("recDj", 5, getDjRecommend),
|
||||
// 专辑
|
||||
getCacheData("recAl", 5, getNewAlbum),
|
||||
]);
|
||||
@@ -182,9 +180,9 @@ const getRecommendData = async () => {
|
||||
artistRes.status === "fulfilled" &&
|
||||
(recommendData.value.artist.data = formatData(artistRes.value.artists, "artist"));
|
||||
mvRes.status === "fulfilled" &&
|
||||
(recommendData.value.mv.data = formatData(mvRes.value.result, "mv"));
|
||||
(recommendData.value.mv.data = formatData(mvRes.value.data, "mv"));
|
||||
djRes.status === "fulfilled" &&
|
||||
(recommendData.value.dj.data = formatData(djRes.value.data, "dj"));
|
||||
(recommendData.value.dj.data = formatData(djRes.value.djRadios, "dj"));
|
||||
albumRes.status === "fulfilled" &&
|
||||
(recommendData.value.album.data = formatData(albumRes.value.albums, "album"));
|
||||
// 检查是否有任何请求失败
|
||||
@@ -273,6 +271,14 @@ onBeforeMount(() => {
|
||||
margin-left: 20px;
|
||||
max-width: calc(50% - 10px);
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
flex-direction: column;
|
||||
.rec-fm {
|
||||
margin-left: 0;
|
||||
margin-top: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -101,6 +101,7 @@ watch(
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
.num-item {
|
||||
display: flex;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="userLikeData.playlists?.length" class="pl-list">
|
||||
<!-- 分类 -->
|
||||
<n-space class="type">
|
||||
<n-flex class="type">
|
||||
<n-tag
|
||||
v-for="(item, index) in ['我创建的', '我收藏的']"
|
||||
:key="index"
|
||||
@@ -14,7 +14,7 @@
|
||||
>
|
||||
{{ item }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="plTypeChoose === 0" class="list">
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</n-text>
|
||||
</div>
|
||||
<!-- 标签 -->
|
||||
<n-space v-if="albumDetail?.tags" class="tags">
|
||||
<n-flex v-if="albumDetail?.tags" class="tags">
|
||||
<n-tag
|
||||
v-for="(item, index) in albumDetail.tags"
|
||||
:key="index"
|
||||
@@ -58,9 +58,9 @@
|
||||
>
|
||||
{{ item }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 数量 -->
|
||||
<n-space class="num">
|
||||
<n-flex class="num">
|
||||
<div v-if="albumDetail.count" class="num-item">
|
||||
<n-icon depth="3" size="18">
|
||||
<SvgIcon icon="music-note" />
|
||||
@@ -79,7 +79,7 @@
|
||||
</n-icon>
|
||||
<n-text depth="3">{{ getTimestampTime(albumDetail.publishTime) }} 发布</n-text>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 简介 -->
|
||||
<n-ellipsis
|
||||
v-if="albumDetail.description"
|
||||
@@ -101,8 +101,8 @@
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- 功能区 -->
|
||||
<n-space class="menu" justify="space-between">
|
||||
<n-space class="left">
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="albumData === 'empty'"
|
||||
type="primary"
|
||||
@@ -119,7 +119,15 @@
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<n-button size="large" tag="div" round strong secondary @click="likeOrDislike(albumId)">
|
||||
<n-button
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
round
|
||||
strong
|
||||
secondary
|
||||
@click="likeOrDislike(albumId)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon
|
||||
@@ -130,7 +138,7 @@
|
||||
{{ isLikeOrDislike(albumId) ? "收藏专辑" : "取消收藏" }}
|
||||
</n-button>
|
||||
<n-dropdown :options="moreOptions" trigger="hover" placement="bottom-start">
|
||||
<n-button size="large" tag="div" circle strong secondary>
|
||||
<n-button class="more" size="large" tag="div" circle strong secondary>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="format-list-bulleted" />
|
||||
@@ -138,8 +146,8 @@
|
||||
</template>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
</n-space>
|
||||
<n-space class="right">
|
||||
</n-flex>
|
||||
<n-flex class="right">
|
||||
<!-- 模糊搜索 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-input
|
||||
@@ -158,8 +166,8 @@
|
||||
</template>
|
||||
</n-input>
|
||||
</Transition>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<SongList v-if="!searchValue" :data="albumData" :sourceId="albumId" :showAlbum="false" />
|
||||
@@ -194,6 +202,7 @@ import { NIcon } from "naive-ui";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteData, siteStatus } from "@/stores";
|
||||
import { getSongDetail } from "@/api/song";
|
||||
import { getAlbumDetail, likeAlbum } from "@/api/album";
|
||||
import { formatNumber, fuzzySearch } from "@/utils/helper";
|
||||
import { getTimestampTime } from "@/utils/timeTools";
|
||||
@@ -255,7 +264,9 @@ const getAlbumAllData = async (id, justDetail = false) => {
|
||||
// 是否终止
|
||||
if (justDetail) return true;
|
||||
// 全部歌曲
|
||||
albumData.value = formatData(detail.songs, "song");
|
||||
const ids = detail.songs.map((song) => song.id).join(",");
|
||||
const songsDetail = await getSongDetail(ids);
|
||||
albumData.value = formatData(songsDetail.songs, "song");
|
||||
};
|
||||
|
||||
// 播放专辑全部歌曲
|
||||
@@ -391,6 +402,7 @@ onBeforeMount(() => {
|
||||
.name {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
.alia {
|
||||
margin-top: 4px;
|
||||
@@ -470,9 +482,11 @@ onBeforeMount(() => {
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
margin: 26px 0;
|
||||
.left {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
.play {
|
||||
--n-width: 46px;
|
||||
@@ -495,6 +509,84 @@ onBeforeMount(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.detail {
|
||||
.cover {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
min-width: 140px;
|
||||
}
|
||||
.data {
|
||||
.name {
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.alia {
|
||||
font-size: 16px;
|
||||
}
|
||||
.creator {
|
||||
.n-avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.nickname {
|
||||
font-size: 12px;
|
||||
}
|
||||
.create-time {
|
||||
margin-left: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.tags {
|
||||
.pl-tags {
|
||||
font-size: 12px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
.num,
|
||||
.description {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
margin: 20px 0;
|
||||
.left {
|
||||
.play {
|
||||
--n-width: 40px;
|
||||
--n-height: 40px;
|
||||
.n-icon {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
}
|
||||
.like {
|
||||
--n-height: 36px;
|
||||
--n-font-size: 13px;
|
||||
--n-padding: 0 16px;
|
||||
--n-icon-size: 18px;
|
||||
:deep(.n-button__icon) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.n-button__content) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.more {
|
||||
--n-height: 36px;
|
||||
--n-font-size: 13px;
|
||||
--n-icon-size: 18px;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.search {
|
||||
height: 36px;
|
||||
width: 130px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</n-ellipsis>
|
||||
<n-text v-else class="description">太懒了吧,连简介都没写</n-text>
|
||||
<!-- 数量 -->
|
||||
<n-space class="num">
|
||||
<n-flex class="num">
|
||||
<div v-if="djDetail?.count" class="num-item">
|
||||
<n-icon depth="3" size="18">
|
||||
<SvgIcon icon="music-note" />
|
||||
@@ -85,7 +85,7 @@
|
||||
</n-icon>
|
||||
<n-text depth="3">{{ getTimestampTime(djDetail.updateTime) }} 更新</n-text>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="detail">
|
||||
@@ -96,8 +96,8 @@
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- 功能区 -->
|
||||
<n-space class="menu" justify="space-between">
|
||||
<n-space class="left">
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="djData === 'empty'"
|
||||
type="primary"
|
||||
@@ -114,7 +114,15 @@
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<n-button size="large" tag="div" round strong secondary @click="likeOrDislike(djId)">
|
||||
<n-button
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
round
|
||||
strong
|
||||
secondary
|
||||
@click="likeOrDislike(djId)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon
|
||||
@@ -125,7 +133,7 @@
|
||||
{{ isLikeOrDislike(djId) ? "订阅电台" : "取消订阅" }}
|
||||
</n-button>
|
||||
<n-dropdown :options="moreOptions" trigger="hover" placement="bottom-start">
|
||||
<n-button size="large" tag="div" circle strong secondary>
|
||||
<n-button class="more" size="large" tag="div" circle strong secondary>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="format-list-bulleted" />
|
||||
@@ -133,8 +141,8 @@
|
||||
</template>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
</n-space>
|
||||
<n-space class="right">
|
||||
</n-flex>
|
||||
<n-flex class="right">
|
||||
<!-- 模糊搜索 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-input
|
||||
@@ -153,8 +161,8 @@
|
||||
</template>
|
||||
</n-input>
|
||||
</Transition>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="djData !== 'empty'" class="list">
|
||||
@@ -433,6 +441,7 @@ onMounted(async () => {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
.creator {
|
||||
display: flex;
|
||||
@@ -529,6 +538,81 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.detail {
|
||||
.cover {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
min-width: 140px;
|
||||
}
|
||||
.data {
|
||||
.name {
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.creator {
|
||||
.n-avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.nickname {
|
||||
font-size: 12px;
|
||||
}
|
||||
.create-time {
|
||||
margin-left: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.tags {
|
||||
.pl-tags {
|
||||
font-size: 12px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
.num,
|
||||
.description {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
margin: 20px 0;
|
||||
.left {
|
||||
.play {
|
||||
--n-width: 40px;
|
||||
--n-height: 40px;
|
||||
.n-icon {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
}
|
||||
.like {
|
||||
--n-height: 36px;
|
||||
--n-font-size: 13px;
|
||||
--n-padding: 0 16px;
|
||||
--n-icon-size: 18px;
|
||||
:deep(.n-button__icon) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.n-button__content) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.more {
|
||||
--n-height: 36px;
|
||||
--n-font-size: 13px;
|
||||
--n-icon-size: 18px;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.search {
|
||||
height: 36px;
|
||||
width: 130px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</n-text>
|
||||
</div>
|
||||
<!-- 标签 -->
|
||||
<n-space v-if="playListDetail?.tags" class="tags">
|
||||
<n-flex v-if="playListDetail?.tags" class="tags">
|
||||
<n-tag
|
||||
v-for="(item, index) in playListDetail.tags"
|
||||
:key="index"
|
||||
@@ -64,9 +64,9 @@
|
||||
>
|
||||
{{ item }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 数量 -->
|
||||
<n-space class="num">
|
||||
<n-flex class="num">
|
||||
<div v-if="playListDetail.count" class="num-item">
|
||||
<n-icon depth="3" size="18">
|
||||
<SvgIcon icon="music-note" />
|
||||
@@ -85,7 +85,7 @@
|
||||
</n-icon>
|
||||
<n-text depth="3">{{ getTimestampTime(playListDetail.updateTime) }}</n-text>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 简介 -->
|
||||
<n-ellipsis
|
||||
v-if="playListDetail.description"
|
||||
@@ -108,8 +108,8 @@
|
||||
</Transition>
|
||||
<!-- 功能区 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-space :key="isUserPLayList" class="menu" justify="space-between">
|
||||
<n-space class="left">
|
||||
<n-flex :key="isUserPLayList" class="menu" justify="space-between">
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="playListData === null || playListData === 'empty' || loadingMsg !== null"
|
||||
type="primary"
|
||||
@@ -128,6 +128,7 @@
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="!isUserPLayList"
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
round
|
||||
@@ -148,6 +149,7 @@
|
||||
</n-button>
|
||||
<n-button
|
||||
v-else
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
round
|
||||
@@ -163,7 +165,7 @@
|
||||
编辑歌单
|
||||
</n-button>
|
||||
<n-dropdown :options="moreOptions" trigger="hover" placement="bottom-start">
|
||||
<n-button size="large" tag="div" circle strong secondary>
|
||||
<n-button class="more" size="large" tag="div" circle strong secondary>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="format-list-bulleted" />
|
||||
@@ -171,8 +173,8 @@
|
||||
</template>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
</n-space>
|
||||
<n-space class="right">
|
||||
</n-flex>
|
||||
<n-flex class="right">
|
||||
<!-- 模糊搜索 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-input
|
||||
@@ -191,8 +193,8 @@
|
||||
</template>
|
||||
</n-input>
|
||||
</Transition>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</Transition>
|
||||
<!-- 列表 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
@@ -566,6 +568,7 @@ onBeforeUnmount(() => {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
.creator {
|
||||
display: flex;
|
||||
@@ -640,9 +643,11 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
margin: 26px 0;
|
||||
.left {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
.play {
|
||||
--n-width: 46px;
|
||||
@@ -665,6 +670,82 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.detail {
|
||||
.cover {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
min-width: 140px;
|
||||
}
|
||||
.data {
|
||||
.name {
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.creator {
|
||||
.n-avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.nickname {
|
||||
font-size: 12px;
|
||||
}
|
||||
.create-time {
|
||||
margin-left: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.tags {
|
||||
.pl-tags {
|
||||
font-size: 12px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
.num,
|
||||
.description {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
margin: 20px 0;
|
||||
.left {
|
||||
.play {
|
||||
--n-width: 40px;
|
||||
--n-height: 40px;
|
||||
.n-icon {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
}
|
||||
.like {
|
||||
--n-height: 36px;
|
||||
--n-font-size: 13px;
|
||||
--n-padding: 0 16px;
|
||||
--n-icon-size: 18px;
|
||||
:deep(.n-button__icon) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.n-button__content) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.more {
|
||||
--n-height: 36px;
|
||||
--n-font-size: 13px;
|
||||
--n-icon-size: 18px;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.search {
|
||||
height: 36px;
|
||||
width: 130px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="local">
|
||||
<n-h1 class="title">本地歌曲</n-h1>
|
||||
<!-- 数据统计 -->
|
||||
<n-space class="num">
|
||||
<n-flex class="num">
|
||||
<!-- 总数 -->
|
||||
<div class="num-item">
|
||||
<n-icon size="18">
|
||||
@@ -25,7 +25,7 @@
|
||||
/>
|
||||
GB
|
||||
</div>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<!-- 标签页 -->
|
||||
<n-tabs v-model:value="tabValue" class="tabs" type="segment" @update:value="tabChange">
|
||||
<n-tab name="local-songs"> 歌曲 </n-tab>
|
||||
@@ -153,7 +153,7 @@
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
<template #footer>
|
||||
<n-space justify="center">
|
||||
<n-flex justify="center">
|
||||
<n-button class="add-path" strong secondary @click="changeLocalPath">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
@@ -162,7 +162,7 @@
|
||||
</template>
|
||||
添加文件夹
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="videoData" class="detail">
|
||||
<n-text class="title">{{ videoData.name }}</n-text>
|
||||
<n-space class="detail-tag">
|
||||
<n-flex class="detail-tag">
|
||||
<!-- 播放量 -->
|
||||
<div v-if="videoData?.playCount" class="tag-item">
|
||||
<n-icon depth="3" size="18">
|
||||
@@ -33,7 +33,7 @@
|
||||
</n-icon>
|
||||
<n-text depth="3">{{ videoData.publishTime }}</n-text>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</div>
|
||||
<div v-else class="detail">
|
||||
<n-skeleton :repeat="2" text round />
|
||||
@@ -111,7 +111,7 @@
|
||||
{{ videoData.desc }}
|
||||
</n-ellipsis>
|
||||
<n-text v-else>该视频暂无简介</n-text>
|
||||
<n-space v-if="videoData?.videoGroup" class="video-tag">
|
||||
<n-flex v-if="videoData?.videoGroup" class="video-tag">
|
||||
<n-tag
|
||||
v-for="(item, index) in videoData?.videoGroup"
|
||||
:key="index"
|
||||
@@ -121,7 +121,7 @@
|
||||
>
|
||||
{{ item.name }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<n-divider id="to-comments" />
|
||||
</div>
|
||||
<div v-else class="content">
|
||||
@@ -636,5 +636,25 @@ onBeforeUnmount(() => {
|
||||
margin-top: 36px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.player {
|
||||
width: 100%;
|
||||
.detail {
|
||||
height: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 20px;
|
||||
.title {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.video-more {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<n-text depth="3">的相关搜索</n-text>
|
||||
</div>
|
||||
<!-- 标签页 -->
|
||||
<n-tabs v-model:value="tabValue" class="tabs" type="line" @update:value="tabChange">
|
||||
<n-tabs v-model:value="tabValue" class="tabs" type="segment" @update:value="tabChange">
|
||||
<n-tab name="sea-songs"> 单曲 </n-tab>
|
||||
<n-tab name="sea-artists"> 歌手 </n-tab>
|
||||
<n-tab name="sea-albums"> 专辑 </n-tab>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<n-tabs
|
||||
ref="setTabsRef"
|
||||
v-model:value="setTabsValue"
|
||||
type="line"
|
||||
type="segment"
|
||||
@update:value="settingTabChange"
|
||||
>
|
||||
<n-tab name="setTab1"> 常规 </n-tab>
|
||||
@@ -85,7 +85,7 @@
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">显示搜索历史</div>
|
||||
<n-switch v-model:value="searchHistory" :round="false" />
|
||||
<n-switch v-model:value="showSearchHistory" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
@@ -499,7 +499,7 @@
|
||||
默认下载文件夹
|
||||
<n-text class="tip">{{ downloadPath || "不设置则会每次选择保存位置" }}</n-text>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-flex>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-button
|
||||
v-if="downloadPath"
|
||||
@@ -514,7 +514,7 @@
|
||||
<n-button :disabled="!checkPlatform.electron()" strong secondary @click="choosePath">
|
||||
更改
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</div>
|
||||
<!-- 其他 -->
|
||||
@@ -599,7 +599,7 @@ const {
|
||||
lrcMousePause,
|
||||
lyricsFontSize,
|
||||
lyricsBlur,
|
||||
searchHistory,
|
||||
showSearchHistory,
|
||||
autoSignIn,
|
||||
bottomLyricShow,
|
||||
downloadPath,
|
||||
|
||||
@@ -1,4 +1,45 @@
|
||||
<!-- 单曲页面 -->
|
||||
<template>
|
||||
<div class="song">单曲页面 - 待完成</div>
|
||||
<div class="song">
|
||||
单曲页面 - 待完成
|
||||
{{ songDetail }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { getSongDetail } from "@/api/song";
|
||||
import formatData from "@/utils/formatData";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 歌曲信息
|
||||
const songId = ref(router.currentRoute.value.query.id);
|
||||
const songDetail = ref(null);
|
||||
|
||||
// 检查是否具有视频 id
|
||||
const isHasSongId = (id) => {
|
||||
if (!id) {
|
||||
$message.error("参数不完整");
|
||||
return router.go(-1);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取歌曲详情
|
||||
const getSongDetailData = async (id) => {
|
||||
try {
|
||||
const detail = await getSongDetail(id);
|
||||
const data = formatData(detail?.songs?.[0], "song");
|
||||
songDetail.value = data?.[0] ?? null;
|
||||
} catch (error) {
|
||||
console.error("获取歌曲详情失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 若无 id
|
||||
isHasSongId(songId.value);
|
||||
// 获取歌曲详情
|
||||
getSongDetailData(songId.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user