mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-08-12 17:10:37 +00:00
style: 重写新页面
This commit is contained in:
parent
4045f1ad3f
commit
22f8d7a95b
@ -76,7 +76,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.3.4",
|
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.3.4",
|
||||||
"axios": "^1.18.1",
|
"axios": "^1.19.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
@ -89,7 +89,7 @@
|
|||||||
"safe-decode-uri-component": "^1.2.1",
|
"safe-decode-uri-component": "^1.2.1",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
"xml2js": "^0.6.2",
|
"xml2js": "^0.6.2",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.6",
|
"@eslint/eslintrc": "^3.3.6",
|
||||||
@ -104,7 +104,7 @@
|
|||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-html": "^8.1.4",
|
"eslint-plugin-html": "^8.1.4",
|
||||||
"eslint-plugin-prettier": "^5.5.6",
|
"eslint-plugin-prettier": "^5.5.6",
|
||||||
"globals": "^17.7.0",
|
"globals": "^17.8.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"intelli-espower-loader": "^1.1.0",
|
"intelli-espower-loader": "^1.1.0",
|
||||||
"lint-staged": "^16.4.0",
|
"lint-staged": "^16.4.0",
|
||||||
|
|||||||
132
pnpm-lock.yaml
generated
132
pnpm-lock.yaml
generated
@ -12,8 +12,8 @@ importers:
|
|||||||
specifier: ^0.3.4
|
specifier: ^0.3.4
|
||||||
version: 0.3.4
|
version: 0.3.4
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.18.1
|
specifier: ^1.19.0
|
||||||
version: 1.18.1
|
version: 1.19.0
|
||||||
crypto-js:
|
crypto-js:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
@ -51,8 +51,8 @@ importers:
|
|||||||
specifier: ^0.6.2
|
specifier: ^0.6.2
|
||||||
version: 0.6.2
|
version: 0.6.2
|
||||||
yargs:
|
yargs:
|
||||||
specifier: ^18.0.0
|
specifier: ^18.1.0
|
||||||
version: 18.0.0
|
version: 18.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/eslintrc':
|
'@eslint/eslintrc':
|
||||||
specifier: ^3.3.6
|
specifier: ^3.3.6
|
||||||
@ -91,8 +91,8 @@ importers:
|
|||||||
specifier: ^5.5.6
|
specifier: ^5.5.6
|
||||||
version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6)
|
version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6)
|
||||||
globals:
|
globals:
|
||||||
specifier: ^17.7.0
|
specifier: ^17.8.0
|
||||||
version: 17.7.0
|
version: 17.8.0
|
||||||
husky:
|
husky:
|
||||||
specifier: ^9.1.7
|
specifier: ^9.1.7
|
||||||
version: 9.1.7
|
version: 9.1.7
|
||||||
@ -271,8 +271,8 @@ packages:
|
|||||||
'@types/express-fileupload@1.5.1':
|
'@types/express-fileupload@1.5.1':
|
||||||
resolution: {integrity: sha512-DllImBVI1lCyjl2klky/TEwk60mbNebgXv1669h66g9TfptWSrEFq5a/raHSutaFzjSm1tmn9ypdNfu4jPSixQ==}
|
resolution: {integrity: sha512-DllImBVI1lCyjl2klky/TEwk60mbNebgXv1669h66g9TfptWSrEFq5a/raHSutaFzjSm1tmn9ypdNfu4jPSixQ==}
|
||||||
|
|
||||||
'@types/express-serve-static-core@5.1.2':
|
'@types/express-serve-static-core@5.1.3':
|
||||||
resolution: {integrity: sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==}
|
resolution: {integrity: sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==}
|
||||||
|
|
||||||
'@types/express@5.0.6':
|
'@types/express@5.0.6':
|
||||||
resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==}
|
resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==}
|
||||||
@ -386,8 +386,8 @@ packages:
|
|||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
acorn@8.17.0:
|
acorn@8.18.0:
|
||||||
resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
|
resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@ -485,8 +485,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
axios@1.18.1:
|
axios@1.19.0:
|
||||||
resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==}
|
resolution: {integrity: sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==}
|
||||||
|
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
@ -517,14 +517,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==}
|
resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
brace-expansion@1.1.16:
|
brace-expansion@1.1.18:
|
||||||
resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==}
|
resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==}
|
||||||
|
|
||||||
brace-expansion@2.1.2:
|
brace-expansion@2.1.4:
|
||||||
resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==}
|
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
|
||||||
|
|
||||||
brace-expansion@5.0.8:
|
brace-expansion@5.0.9:
|
||||||
resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==}
|
resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
|
|
||||||
braces@3.0.3:
|
braces@3.0.3:
|
||||||
@ -1153,8 +1153,8 @@ packages:
|
|||||||
flatstr@1.0.12:
|
flatstr@1.0.12:
|
||||||
resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==}
|
resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==}
|
||||||
|
|
||||||
flatted@3.4.3:
|
flatted@3.4.4:
|
||||||
resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==}
|
resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
|
||||||
|
|
||||||
follow-redirects@1.16.0:
|
follow-redirects@1.16.0:
|
||||||
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
|
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
|
||||||
@ -1262,8 +1262,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
globals@17.7.0:
|
globals@17.8.0:
|
||||||
resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==}
|
resolution: {integrity: sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
globalthis@1.0.4:
|
globalthis@1.0.4:
|
||||||
@ -1400,8 +1400,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==}
|
resolution: {integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
ip-address@10.2.0:
|
ip-address@10.4.0:
|
||||||
resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==}
|
resolution: {integrity: sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
ipaddr.js@1.9.1:
|
ipaddr.js@1.9.1:
|
||||||
@ -1565,8 +1565,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
js-yaml@4.3.0:
|
js-yaml@4.3.1:
|
||||||
resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==}
|
resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
jsesc@2.5.2:
|
jsesc@2.5.2:
|
||||||
@ -1699,8 +1699,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
minimatch@10.2.5:
|
minimatch@10.2.6:
|
||||||
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
|
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
|
||||||
engines: {node: 18 || 20 || >=22}
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
minimatch@3.1.5:
|
minimatch@3.1.5:
|
||||||
@ -2158,8 +2158,8 @@ packages:
|
|||||||
safer-buffer@2.1.2:
|
safer-buffer@2.1.2:
|
||||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
|
||||||
sax@1.6.0:
|
sax@1.6.1:
|
||||||
resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
|
resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==}
|
||||||
engines: {node: '>=11.0.0'}
|
engines: {node: '>=11.0.0'}
|
||||||
|
|
||||||
secure-json-parse@2.7.0:
|
secure-json-parse@2.7.0:
|
||||||
@ -2400,8 +2400,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tinyexec@1.2.4:
|
tinyexec@1.3.0:
|
||||||
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
|
resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
tinyglobby@0.2.17:
|
tinyglobby@0.2.17:
|
||||||
@ -2655,8 +2655,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
|
resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
yargs@18.0.0:
|
yargs@18.1.0:
|
||||||
resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
|
resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==}
|
||||||
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||||
|
|
||||||
yocto-queue@0.1.0:
|
yocto-queue@0.1.0:
|
||||||
@ -2718,7 +2718,7 @@ snapshots:
|
|||||||
globals: 14.0.0
|
globals: 14.0.0
|
||||||
ignore: 5.3.2
|
ignore: 5.3.2
|
||||||
import-fresh: 3.3.1
|
import-fresh: 3.3.1
|
||||||
js-yaml: 4.3.0
|
js-yaml: 4.3.1
|
||||||
minimatch: 3.1.5
|
minimatch: 3.1.5
|
||||||
strip-json-comments: 3.1.1
|
strip-json-comments: 3.1.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -2775,7 +2775,7 @@ snapshots:
|
|||||||
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.4':
|
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unblockneteasemusic/server': 0.28.0
|
'@unblockneteasemusic/server': 0.28.0
|
||||||
axios: 1.18.1
|
axios: 1.19.0
|
||||||
dotenv: 17.4.2
|
dotenv: 17.4.2
|
||||||
express: 4.22.2
|
express: 4.22.2
|
||||||
https: 1.0.0
|
https: 1.0.0
|
||||||
@ -2831,7 +2831,7 @@ snapshots:
|
|||||||
'@types/busboy': 1.5.4
|
'@types/busboy': 1.5.4
|
||||||
'@types/express': 5.0.6
|
'@types/express': 5.0.6
|
||||||
|
|
||||||
'@types/express-serve-static-core@5.1.2':
|
'@types/express-serve-static-core@5.1.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.2
|
'@types/node': 25.9.2
|
||||||
'@types/qs': 6.15.1
|
'@types/qs': 6.15.1
|
||||||
@ -2841,7 +2841,7 @@ snapshots:
|
|||||||
'@types/express@5.0.6':
|
'@types/express@5.0.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/body-parser': 1.19.6
|
'@types/body-parser': 1.19.6
|
||||||
'@types/express-serve-static-core': 5.1.2
|
'@types/express-serve-static-core': 5.1.3
|
||||||
'@types/serve-static': 2.2.0
|
'@types/serve-static': 2.2.0
|
||||||
|
|
||||||
'@types/http-errors@2.0.5': {}
|
'@types/http-errors@2.0.5': {}
|
||||||
@ -2934,7 +2934,7 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.65.0
|
'@typescript-eslint/types': 8.65.0
|
||||||
'@typescript-eslint/visitor-keys': 8.65.0
|
'@typescript-eslint/visitor-keys': 8.65.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
minimatch: 10.2.5
|
minimatch: 10.2.6
|
||||||
semver: 7.8.5
|
semver: 7.8.5
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
ts-api-utils: 2.5.0(typescript@5.9.3)
|
ts-api-utils: 2.5.0(typescript@5.9.3)
|
||||||
@ -2976,13 +2976,13 @@ snapshots:
|
|||||||
|
|
||||||
acorn-es7-plugin@1.1.7: {}
|
acorn-es7-plugin@1.1.7: {}
|
||||||
|
|
||||||
acorn-jsx@5.3.2(acorn@8.17.0):
|
acorn-jsx@5.3.2(acorn@8.18.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.17.0
|
acorn: 8.18.0
|
||||||
|
|
||||||
acorn@5.7.4: {}
|
acorn@5.7.4: {}
|
||||||
|
|
||||||
acorn@8.17.0: {}
|
acorn@8.18.0: {}
|
||||||
|
|
||||||
agent-base@6.0.2:
|
agent-base@6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3072,7 +3072,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
|
|
||||||
axios@1.18.1:
|
axios@1.19.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.16.0
|
follow-redirects: 1.16.0
|
||||||
form-data: 4.0.6
|
form-data: 4.0.6
|
||||||
@ -3129,16 +3129,16 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
brace-expansion@1.1.16:
|
brace-expansion@1.1.18:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
concat-map: 0.0.1
|
concat-map: 0.0.1
|
||||||
|
|
||||||
brace-expansion@2.1.2:
|
brace-expansion@2.1.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
||||||
brace-expansion@5.0.8:
|
brace-expansion@5.0.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 4.0.4
|
balanced-match: 4.0.4
|
||||||
|
|
||||||
@ -3765,8 +3765,8 @@ snapshots:
|
|||||||
|
|
||||||
espree@10.4.0:
|
espree@10.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.17.0
|
acorn: 8.18.0
|
||||||
acorn-jsx: 5.3.2(acorn@8.17.0)
|
acorn-jsx: 5.3.2(acorn@8.18.0)
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
esprima@2.7.3: {}
|
esprima@2.7.3: {}
|
||||||
@ -3959,14 +3959,14 @@ snapshots:
|
|||||||
|
|
||||||
flat-cache@4.0.1:
|
flat-cache@4.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
flatted: 3.4.3
|
flatted: 3.4.4
|
||||||
keyv: 4.5.4
|
keyv: 4.5.4
|
||||||
|
|
||||||
flat@5.0.2: {}
|
flat@5.0.2: {}
|
||||||
|
|
||||||
flatstr@1.0.12: {}
|
flatstr@1.0.12: {}
|
||||||
|
|
||||||
flatted@3.4.3: {}
|
flatted@3.4.4: {}
|
||||||
|
|
||||||
follow-redirects@1.16.0: {}
|
follow-redirects@1.16.0: {}
|
||||||
|
|
||||||
@ -4085,7 +4085,7 @@ snapshots:
|
|||||||
|
|
||||||
globals@14.0.0: {}
|
globals@14.0.0: {}
|
||||||
|
|
||||||
globals@17.7.0: {}
|
globals@17.8.0: {}
|
||||||
|
|
||||||
globalthis@1.0.4:
|
globalthis@1.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4219,7 +4219,7 @@ snapshots:
|
|||||||
from2: 2.3.0
|
from2: 2.3.0
|
||||||
p-is-promise: 3.0.0
|
p-is-promise: 3.0.0
|
||||||
|
|
||||||
ip-address@10.2.0: {}
|
ip-address@10.4.0: {}
|
||||||
|
|
||||||
ipaddr.js@1.9.1: {}
|
ipaddr.js@1.9.1: {}
|
||||||
|
|
||||||
@ -4378,7 +4378,7 @@ snapshots:
|
|||||||
|
|
||||||
joycon@3.1.1: {}
|
joycon@3.1.1: {}
|
||||||
|
|
||||||
js-yaml@4.3.0:
|
js-yaml@4.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
|
||||||
@ -4418,7 +4418,7 @@ snapshots:
|
|||||||
listr2: 9.0.5
|
listr2: 9.0.5
|
||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
string-argv: 0.3.2
|
string-argv: 0.3.2
|
||||||
tinyexec: 1.2.4
|
tinyexec: 1.3.0
|
||||||
yaml: 2.9.0
|
yaml: 2.9.0
|
||||||
|
|
||||||
listr2@9.0.5:
|
listr2@9.0.5:
|
||||||
@ -4498,17 +4498,17 @@ snapshots:
|
|||||||
|
|
||||||
mimic-response@3.1.0: {}
|
mimic-response@3.1.0: {}
|
||||||
|
|
||||||
minimatch@10.2.5:
|
minimatch@10.2.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 5.0.8
|
brace-expansion: 5.0.9
|
||||||
|
|
||||||
minimatch@3.1.5:
|
minimatch@3.1.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 1.1.16
|
brace-expansion: 1.1.18
|
||||||
|
|
||||||
minimatch@9.0.9:
|
minimatch@9.0.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.1.2
|
brace-expansion: 2.1.4
|
||||||
|
|
||||||
minimist@1.2.8: {}
|
minimist@1.2.8: {}
|
||||||
|
|
||||||
@ -4527,7 +4527,7 @@ snapshots:
|
|||||||
glob: 10.5.0
|
glob: 10.5.0
|
||||||
he: 1.2.0
|
he: 1.2.0
|
||||||
is-path-inside: 3.0.3
|
is-path-inside: 3.0.3
|
||||||
js-yaml: 4.3.0
|
js-yaml: 4.3.1
|
||||||
log-symbols: 4.1.0
|
log-symbols: 4.1.0
|
||||||
minimatch: 9.0.9
|
minimatch: 9.0.9
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
@ -4602,7 +4602,7 @@ snapshots:
|
|||||||
chokidar: 3.6.0
|
chokidar: 3.6.0
|
||||||
debug: 4.4.3(supports-color@5.5.0)
|
debug: 4.4.3(supports-color@5.5.0)
|
||||||
ignore-by-default: 1.0.1
|
ignore-by-default: 1.0.1
|
||||||
minimatch: 10.2.5
|
minimatch: 10.2.6
|
||||||
pstree.remy: 1.1.8
|
pstree.remy: 1.1.8
|
||||||
semver: 7.8.5
|
semver: 7.8.5
|
||||||
simple-update-notifier: 2.0.0
|
simple-update-notifier: 2.0.0
|
||||||
@ -5073,7 +5073,7 @@ snapshots:
|
|||||||
|
|
||||||
safer-buffer@2.1.2: {}
|
safer-buffer@2.1.2: {}
|
||||||
|
|
||||||
sax@1.6.0: {}
|
sax@1.6.1: {}
|
||||||
|
|
||||||
secure-json-parse@2.7.0: {}
|
secure-json-parse@2.7.0: {}
|
||||||
|
|
||||||
@ -5233,7 +5233,7 @@ snapshots:
|
|||||||
|
|
||||||
socks@2.8.9:
|
socks@2.8.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ip-address: 10.2.0
|
ip-address: 10.4.0
|
||||||
smart-buffer: 4.2.0
|
smart-buffer: 4.2.0
|
||||||
|
|
||||||
sonic-boom@1.4.1:
|
sonic-boom@1.4.1:
|
||||||
@ -5387,7 +5387,7 @@ snapshots:
|
|||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
readable-stream: 3.6.2
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
tinyexec@1.2.4: {}
|
tinyexec@1.3.0: {}
|
||||||
|
|
||||||
tinyglobby@0.2.17:
|
tinyglobby@0.2.17:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5620,7 +5620,7 @@ snapshots:
|
|||||||
|
|
||||||
xml2js@0.6.2:
|
xml2js@0.6.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
sax: 1.6.0
|
sax: 1.6.1
|
||||||
xmlbuilder: 11.0.1
|
xmlbuilder: 11.0.1
|
||||||
|
|
||||||
xml@1.0.1: {}
|
xml@1.0.1: {}
|
||||||
@ -5687,12 +5687,12 @@ snapshots:
|
|||||||
y18n: 5.0.8
|
y18n: 5.0.8
|
||||||
yargs-parser: 21.1.1
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
yargs@18.0.0:
|
yargs@18.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cliui: 9.0.1
|
cliui: 9.0.1
|
||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
get-caller-file: 2.0.5
|
get-caller-file: 2.0.5
|
||||||
string-width: 7.2.0
|
string-width: 8.2.2
|
||||||
y18n: 5.0.8
|
y18n: 5.0.8
|
||||||
yargs-parser: 22.0.0
|
yargs-parser: 22.0.0
|
||||||
|
|
||||||
|
|||||||
445
public/api.html
445
public/api.html
@ -3,360 +3,229 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" href="docs/netease.png">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>API 调试界面</title>
|
<title>API 调试界面</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
min-width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, select {
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
flex: 1;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus, select:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
padding: 10px 24px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-result {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-result > div {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-result label {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
flex: 1;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 350px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== 自定义选项区域 ===== */
|
/* ===== 自定义选项区域 ===== */
|
||||||
.section-container {
|
.section-container {
|
||||||
margin-top: 20px;
|
margin-top: 1.25rem;
|
||||||
padding: 16px 20px;
|
padding: 1rem 1.25rem;
|
||||||
background: #fafafa;
|
background: var(--gray-2);
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
|
||||||
}
|
}
|
||||||
|
.section-container + .section-container { margin-top: 0.75rem; }
|
||||||
.section-container + .section-container {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #555;
|
color: var(--foreground);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title .count-badge {
|
.section-title .count-badge {
|
||||||
font-size: 11px;
|
font-size: 0.6875rem;
|
||||||
background: #ddd;
|
background: var(--gray-4);
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
padding: 1px 8px;
|
padding: 0 0.5rem;
|
||||||
border-radius: 10px;
|
border-radius: 9999px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamic-row {
|
.dynamic-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 0.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamic-row .key-input {
|
.dynamic-row .key-input {
|
||||||
flex: 0 0 200px;
|
flex: 0 0 14rem;
|
||||||
padding: 8px 12px;
|
padding: 0.5rem 0.75rem;
|
||||||
border: 1px solid #ddd;
|
background: var(--gray-1);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--input);
|
||||||
font-size: 13px;
|
color: var(--foreground);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamic-row .value-input {
|
.dynamic-row .value-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 8px 12px;
|
padding: 0.5rem 0.75rem;
|
||||||
border: 1px solid #ddd;
|
background: var(--gray-1);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--input);
|
||||||
font-size: 13px;
|
color: var(--foreground);
|
||||||
|
font-size: 0.8125rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamic-row .key-input:focus,
|
.dynamic-row .key-input:focus,
|
||||||
.dynamic-row .value-input:focus {
|
.dynamic-row .value-input:focus { border-color: var(--ring); }
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-btn {
|
.remove-btn {
|
||||||
width: 28px;
|
width: 1.75rem;
|
||||||
height: 28px;
|
height: 1.75rem;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #fff;
|
background: var(--gray-2);
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
border: 1px solid #ddd;
|
border: 1px solid var(--border);
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.2s;
|
transition: all var(--transition);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-btn:hover {
|
.remove-btn:hover {
|
||||||
background: #fee;
|
background: var(--destructive);
|
||||||
color: #e33;
|
color: var(--gray-1);
|
||||||
border-color: #fcc;
|
border-color: var(--destructive);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
background: #fff;
|
background: transparent;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
border: 1px dashed #ccc;
|
border: 1px dashed var(--border);
|
||||||
padding: 6px 16px;
|
padding: 0.375rem 1rem;
|
||||||
border-radius: 6px;
|
font-family: var(--font-mono);
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all var(--transition);
|
||||||
margin-top: 4px;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-btn:hover {
|
.add-btn:hover {
|
||||||
border-color: #999;
|
border-color: var(--foreground);
|
||||||
background: #f5f5f5;
|
background: var(--gray-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 参数编辑区 Tab ===== */
|
/* ===== 参数编辑区 Tab ===== */
|
||||||
.param-container {
|
.param-container { flex: 1; display: flex; flex-direction: column; }
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-header {
|
.param-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.param-header-title {
|
.param-header-title {
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #555;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.param-tabs {
|
.param-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
background: #f0f0f0;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-btn {
|
.tab-btn {
|
||||||
padding: 4px 14px;
|
padding: 0.25rem 0.875rem;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #888;
|
color: var(--muted-foreground);
|
||||||
font-size: 12px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
transition: all var(--transition);
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
}
|
||||||
|
.tab-btn:hover { color: var(--foreground); }
|
||||||
.tab-btn:hover {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
.tab-btn.active {
|
||||||
background: #fff;
|
background: var(--foreground);
|
||||||
color: #333;
|
color: var(--background);
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
.param-panel { flex: 1; display: flex; flex-direction: column; }
|
||||||
|
.param-panel textarea { flex: 1; min-height: 21.875rem; }
|
||||||
|
|
||||||
.param-panel {
|
/* 双栏响应式 */
|
||||||
flex: 1;
|
.data-result { display: flex; gap: 1rem; min-height: 25rem; }
|
||||||
display: flex;
|
.data-result > div { flex: 1; display: flex; flex-direction: column; }
|
||||||
flex-direction: column;
|
.data-result textarea { flex: 1; min-height: 21.875rem; }
|
||||||
}
|
@media (max-width: 640px) {
|
||||||
|
.data-result { flex-direction: column; }
|
||||||
.param-panel textarea {
|
.dynamic-row .key-input { flex: 0 0 8rem; }
|
||||||
flex: 1;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 350px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-panel textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">API 调试</a>
|
||||||
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="shell-main">
|
||||||
|
<div class="shell-container shell-narrow" style="max-width:none">
|
||||||
|
<header class="shell-header">
|
||||||
<h1>API 调试界面</h1>
|
<h1>API 调试界面</h1>
|
||||||
|
<p class="sub">发送任意请求,支持自定义请求头、加密方式与 JSON / 键值对参数</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="shell-card">
|
||||||
<form onsubmit="event.preventDefault(); sendRequest();">
|
<form onsubmit="event.preventDefault(); sendRequest();">
|
||||||
<div class="form-row">
|
<div class="shell-flex" style="margin-bottom:0.75rem">
|
||||||
<label for="uri">URI</label>
|
<label for="uri" style="flex:0 0 5rem;margin:0">URI</label>
|
||||||
<input type="text" id="uri" name="uri" value="/api/song/lyric/v1">
|
<input type="text" id="uri" name="uri" value="/api/song/lyric/v1">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="shell-flex" style="margin-bottom:0.75rem">
|
||||||
<label for="method">请求方法</label>
|
<label for="method" style="flex:0 0 5rem;margin:0">请求方法</label>
|
||||||
<select id="method" name="method">
|
<select id="method" name="method">
|
||||||
<option value="POST" selected>POST</option>
|
<option value="POST" selected>POST</option>
|
||||||
<option value="GET">GET</option>
|
<option value="GET">GET</option>
|
||||||
<option value="PUT">PUT</option>
|
<option value="PUT">PUT</option>
|
||||||
<option value="DELETE">DELETE</option>
|
<option value="DELETE">DELETE</option>
|
||||||
<option value="PATCH">PATCH</option>
|
<option value="PATCH">PATCH</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="shell-flex" style="margin-bottom:0.75rem">
|
||||||
<label for="crypto">加密方式</label>
|
<label for="crypto" style="flex:0 0 5rem;margin:0">加密方式</label>
|
||||||
<select id="crypto" name="crypto">
|
<select id="crypto" name="crypto">
|
||||||
<option value="weapi">weapi</option>
|
<option value="weapi">weapi</option>
|
||||||
<option value="eapi">eapi</option>
|
<option value="eapi">eapi</option>
|
||||||
<option value="api">api</option>
|
<option value="api">api</option>
|
||||||
<option value="linuxapi">linuxapi</option>
|
<option value="linuxapi">linuxapi</option>
|
||||||
<option value="xeapi">xeapi</option>
|
<option value="xeapi">xeapi</option>
|
||||||
<option value="" selected>(默认)</option>
|
<option value="" selected>(默认)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="shell-flex" style="margin-bottom:1rem">
|
||||||
<label for="timeout">超时(ms)</label>
|
<label for="timeout" style="flex:0 0 5rem;margin:0">超时(ms)</label>
|
||||||
<input type="number" id="timeout" name="timeout" value="30000" min="0" step="1000" placeholder="0 表示无超时">
|
<input type="number" id="timeout" name="timeout" value="30000" min="0" step="1000" placeholder="0 表示无超时">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="shell-flex">
|
||||||
<label></label>
|
<span style="flex:0 0 5rem"></span>
|
||||||
<button type="submit">发送请求</button>
|
<button type="submit" class="shell-btn shell-btn--default">发送请求</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- ===== 自定义请求头 ===== -->
|
<!-- ===== 自定义请求头 ===== -->
|
||||||
<div class="section-container" id="headersSection">
|
<section class="shell-card shell-mt-6">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
请求头 (Headers)
|
请求头 (Headers)
|
||||||
<span class="count-badge" id="headerCount">4</span>
|
<span class="count-badge" id="headerCount">4</span>
|
||||||
</div>
|
|
||||||
<div id="headersList"></div>
|
|
||||||
<button class="add-btn" onclick="addHeaderRow('', '')">+ 添加请求头</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="headersList"></div>
|
||||||
|
<button class="add-btn" onclick="addHeaderRow('', '')">+ 添加请求头</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="shell-card shell-mt-6">
|
||||||
<div class="data-result">
|
<div class="data-result">
|
||||||
<div>
|
<div>
|
||||||
<label for="result">响应结果</label>
|
<label for="result" style="font-family:var(--font-mono)">响应结果</label>
|
||||||
<textarea id="result" name="result" readonly></textarea>
|
<textarea id="result" name="result" readonly></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="param-container">
|
||||||
|
<div class="param-header">
|
||||||
|
<span class="param-header-title">请求参数</span>
|
||||||
|
<div class="param-tabs">
|
||||||
|
<button class="tab-btn active" data-mode="json" onclick="switchParamTab('json')">JSON</button>
|
||||||
|
<button class="tab-btn" data-mode="kv" onclick="switchParamTab('kv')">键值对</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-container">
|
<div class="param-panel" id="jsonPanel">
|
||||||
<div class="param-header">
|
<textarea id="data" name="data">
|
||||||
<span class="param-header-title">请求参数</span>
|
|
||||||
<div class="param-tabs">
|
|
||||||
<button class="tab-btn active" data-mode="json" onclick="switchParamTab('json')">JSON</button>
|
|
||||||
<button class="tab-btn" data-mode="kv" onclick="switchParamTab('kv')">键值对</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="param-panel" id="jsonPanel">
|
|
||||||
<textarea id="data" name="data">
|
|
||||||
{
|
{
|
||||||
"cp": false,
|
"cp": false,
|
||||||
"id": "2058263032",
|
"id": "2058263032",
|
||||||
@ -368,17 +237,19 @@
|
|||||||
"ytv": 0,
|
"ytv": 0,
|
||||||
"yv": 0
|
"yv": 0
|
||||||
}</textarea>
|
}</textarea>
|
||||||
</div>
|
|
||||||
<div class="param-panel" id="kvPanel" style="display:none">
|
|
||||||
<div id="kvList"></div>
|
|
||||||
<button class="add-btn" onclick="addKvRow('', '')">+ 添加参数</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="param-panel" id="kvPanel" style="display:none">
|
||||||
|
<div id="kvList"></div>
|
||||||
|
<button class="add-btn" onclick="addKvRow('', '')">+ 添加参数</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// ===== 动态行管理 =====
|
// ===== 动态行管理 =====
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
if (!str) return '';
|
if (!str) return '';
|
||||||
@ -425,6 +296,12 @@
|
|||||||
btn.parentElement.remove();
|
btn.parentElement.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateCount() {
|
||||||
|
const count = document.querySelectorAll('#headersList .dynamic-row').length;
|
||||||
|
const badge = document.getElementById('headerCount');
|
||||||
|
if (badge) badge.textContent = count;
|
||||||
|
}
|
||||||
|
|
||||||
function collectHeaders() {
|
function collectHeaders() {
|
||||||
const headers = {};
|
const headers = {};
|
||||||
document.querySelectorAll('#headersList .dynamic-row').forEach(function (row) {
|
document.querySelectorAll('#headersList .dynamic-row').forEach(function (row) {
|
||||||
|
|||||||
@ -3,468 +3,341 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>API 参数和返回内容解析</title>
|
<title>API 参数和返回内容解析</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 200px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-group {
|
|
||||||
display: flex;
|
|
||||||
gap: 24px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-item input[type="radio"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-item label {
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
padding: 12px 28px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
button + button {
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-section {
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-section label {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.decode-result {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
background: #f9f9f9;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
min-height: 200px;
|
|
||||||
max-height: 400px;
|
|
||||||
overflow: auto;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.decode-result .json-key { color: #881391; }
|
|
||||||
.decode-result .json-string { color: #1a7f37; }
|
|
||||||
.decode-result .json-number { color: #0550ae; }
|
|
||||||
.decode-result .json-boolean { color: #cf222e; }
|
|
||||||
.decode-result .json-null { color: #656d76; }
|
|
||||||
|
|
||||||
.example-section {
|
|
||||||
margin-top: 32px;
|
|
||||||
padding-top: 24px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-section h2 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-section img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* new elements for multi-crypto support */
|
|
||||||
.crypto-bar {
|
.crypto-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 1.25rem;
|
||||||
padding: 12px 16px;
|
padding: 0.75rem 1rem;
|
||||||
background: #fafafa;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.crypto-bar .bar-label {
|
.crypto-bar .bar-label {
|
||||||
font-size: 13px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #555;
|
color: var(--muted-foreground);
|
||||||
margin-right: 8px;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.crypto-btn {
|
.crypto-btn {
|
||||||
padding: 5px 14px;
|
padding: 0.3125rem 0.875rem;
|
||||||
border-radius: 4px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #ddd;
|
background: var(--gray-1);
|
||||||
background: #fff;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
transition: all 0.15s ease;
|
transition: all var(--transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
.crypto-btn:hover {
|
.crypto-btn:hover {
|
||||||
border-color: #333;
|
border-color: var(--foreground);
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
background: #f5f5f5;
|
background: var(--gray-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.crypto-btn.active {
|
.crypto-btn.active {
|
||||||
border-color: #333;
|
border-color: var(--foreground);
|
||||||
background: #333;
|
background: var(--foreground);
|
||||||
color: #fff;
|
color: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.crypto-btn .badge {
|
.crypto-btn .badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 10px;
|
font-size: 0.625rem;
|
||||||
background: rgba(0,0,0,0.08);
|
background: color-mix(in srgb, var(--foreground) 12%, transparent);
|
||||||
border-radius: 4px;
|
border-radius: 2px;
|
||||||
padding: 0 5px;
|
padding: 0 0.3125rem;
|
||||||
margin-left: 3px;
|
margin-left: 0.1875rem;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
.crypto-btn.active .badge {
|
.crypto-btn.active .badge {
|
||||||
background: rgba(255,255,255,0.2);
|
background: color-mix(in srgb, var(--background) 15%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-row {
|
.mode-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 1.25rem;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 1.25rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-row .mode-label {
|
.mode-row .mode-label {
|
||||||
font-size: 13px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #555;
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-row .radio-group {
|
.mode-row .radio-group {
|
||||||
margin-bottom: 0;
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
.radio-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.radio-item input[type="radio"] { cursor: pointer; }
|
||||||
|
.radio-item label { margin: 0; cursor: pointer; }
|
||||||
|
|
||||||
.info-hint {
|
.info-hint {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #888;
|
color: var(--muted-foreground);
|
||||||
margin-top: 6px;
|
margin-top: 0.375rem;
|
||||||
padding: 6px 10px;
|
padding: 0.375rem 0.625rem;
|
||||||
background: #fafafa;
|
background: var(--gray-2);
|
||||||
border-radius: 4px;
|
border-left: 3px solid var(--border);
|
||||||
border-left: 3px solid #999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-hint.warn {
|
.info-hint.warn {
|
||||||
border-left-color: #e67e22;
|
border-left-color: var(--warning);
|
||||||
background: #fef9f0;
|
background: color-mix(in srgb, var(--warning) 8%, var(--gray-2));
|
||||||
color: #d35400;
|
color: var(--warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-row {
|
.action-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-row button + button {
|
.decode-result {
|
||||||
margin-left: 0;
|
white-space: pre-wrap;
|
||||||
}
|
word-break: break-all;
|
||||||
|
background: var(--gray-2);
|
||||||
button.btn-secondary {
|
padding: 1rem;
|
||||||
background: #e8e8e8;
|
border: 1px solid var(--border);
|
||||||
color: #444;
|
min-height: 12.5rem;
|
||||||
}
|
max-height: 25rem;
|
||||||
button.btn-secondary:hover {
|
overflow: auto;
|
||||||
background: #d5d5d5;
|
font-family: var(--font-mono);
|
||||||
}
|
font-size: 0.8125rem;
|
||||||
button.btn-secondary:disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
cursor: not-allowed;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
button.btn-success {
|
|
||||||
background: #4caf50;
|
|
||||||
}
|
|
||||||
button.btn-success:hover {
|
|
||||||
background: #43a047;
|
|
||||||
}
|
|
||||||
button.btn-success:disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
cursor: not-allowed;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
/* JSON 语法高亮(深色适配) */
|
||||||
|
.decode-result .json-key { color: #79c0ff; }
|
||||||
|
.decode-result .json-string { color: #7ee787; }
|
||||||
|
.decode-result .json-number { color: #ffa657; }
|
||||||
|
.decode-result .json-boolean { color: #ff7b72; }
|
||||||
|
.decode-result .json-null { color: #8b949e; }
|
||||||
|
|
||||||
.result-section .section-header {
|
.result-section .section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0.625rem;
|
||||||
}
|
|
||||||
|
|
||||||
.result-section .section-header label {
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
.result-section .section-header label { margin: 0; }
|
||||||
|
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
font-size: 12px;
|
font-family: var(--font-mono);
|
||||||
padding: 3px 12px;
|
font-size: 0.75rem;
|
||||||
border-radius: 4px;
|
padding: 0.1875rem 0.75rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid var(--border);
|
||||||
background: #fff;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
transition: all 0.15s;
|
transition: all var(--transition);
|
||||||
}
|
}
|
||||||
.copy-btn:hover {
|
.copy-btn:hover {
|
||||||
background: #333;
|
background: var(--foreground);
|
||||||
color: #fff;
|
color: var(--background);
|
||||||
border-color: #333;
|
border-color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-section {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.example-section h2 {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.example-section img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-grid {
|
.example-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 16px;
|
gap: 1rem;
|
||||||
margin-top: 12px;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-card {
|
.example-card {
|
||||||
background: #fafafa;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
padding: 1rem;
|
||||||
padding: 16px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-card h3 {
|
.example-card h3 {
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #555;
|
color: var(--foreground);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-card pre {
|
.example-card pre {
|
||||||
font-family: 'Courier New', monospace;
|
background: var(--gray-1);
|
||||||
font-size: 12px;
|
padding: 0.625rem;
|
||||||
line-height: 1.5;
|
overflow: auto;
|
||||||
background: #fff;
|
border: 1px solid var(--border);
|
||||||
padding: 10px;
|
max-height: 7.5rem;
|
||||||
border-radius: 4px;
|
margin-bottom: 0.5rem;
|
||||||
overflow-x: auto;
|
font-size: 0.75rem;
|
||||||
border: 1px solid #e8e8e8;
|
|
||||||
max-height: 120px;
|
|
||||||
overflow-y: auto;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-card .tag {
|
.example-card .tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 10px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.625rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 2px 8px;
|
text-transform: uppercase;
|
||||||
border-radius: 3px;
|
padding: 0.125rem 0.5rem;
|
||||||
margin-bottom: 6px;
|
border: 1px solid var(--border);
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
}
|
}
|
||||||
|
.tag-eapi { color: var(--info); border-color: color-mix(in srgb, var(--info) 50%, transparent); }
|
||||||
.tag-eapi { background: #e3f2fd; color: #1565c0; }
|
.tag-weapi { color: #ff7b72; border-color: color-mix(in srgb, #ff7b72 50%, transparent); }
|
||||||
.tag-weapi { background: #fce4ec; color: #c62828; }
|
.tag-linuxapi { color: var(--success); border-color: color-mix(in srgb, var(--success) 50%, transparent); }
|
||||||
.tag-linuxapi { background: #e8f5e9; color: #2e7d32; }
|
.tag-xeapi { color: #d2a8ff; border-color: color-mix(in srgb, #d2a8ff 50%, transparent); }
|
||||||
.tag-xeapi { background: #f3e5f5; color: #7b1fa2; }
|
.tag-api { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 50%, transparent); }
|
||||||
.tag-api { background: #fff3e0; color: #e65100; }
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.example-grid { grid-template-columns: 1fr; }
|
.example-grid { grid-template-columns: 1fr; }
|
||||||
.crypto-bar { gap: 4px; }
|
.crypto-bar { gap: 0.25rem; }
|
||||||
.crypto-btn { padding: 4px 10px; font-size: 12px; }
|
.crypto-btn { padding: 0.25rem 0.625rem; font-size: 0.75rem; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app" class="container">
|
<header class="shell-topbar">
|
||||||
<h1>API 参数和返回内容解析</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">API 解密</a>
|
||||||
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/api.html">API 调试</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<!-- 加密方式选择 -->
|
<main class="shell-main">
|
||||||
<div class="crypto-bar">
|
<div class="shell-container shell-narrow" style="max-width:56rem">
|
||||||
<span class="bar-label">加密方式</span>
|
<div id="app">
|
||||||
<button
|
<header class="shell-header">
|
||||||
v-for="c in cryptoList"
|
<h1>API 参数和返回内容解析</h1>
|
||||||
:key="c.value"
|
<p class="sub">解密 eapi / weapi / linuxapi / xeapi 的请求与响应数据</p>
|
||||||
class="crypto-btn"
|
</header>
|
||||||
:class="{ active: crypto === c.value }"
|
|
||||||
@click="selectCrypto(c.value)"
|
|
||||||
>
|
|
||||||
{{ c.label }}
|
|
||||||
<span class="badge">{{ c.badge }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mode-row">
|
<!-- 加密方式选择 -->
|
||||||
<span class="mode-label">数据类型</span>
|
<section class="shell-card">
|
||||||
<div class="radio-group">
|
<div class="crypto-bar">
|
||||||
<div class="radio-item">
|
<span class="bar-label">加密方式</span>
|
||||||
<input
|
<button
|
||||||
type="radio"
|
v-for="c in cryptoList"
|
||||||
id="mode-req"
|
:key="c.value"
|
||||||
:value="true"
|
class="crypto-btn"
|
||||||
v-model="isReq"
|
:class="{ active: crypto === c.value }"
|
||||||
:disabled="cryptoInfo.reqDisabled"
|
@click="selectCrypto(c.value)"
|
||||||
/>
|
>
|
||||||
<label for="mode-req">请求数据 request</label>
|
{{ c.label }}
|
||||||
</div>
|
<span class="badge">{{ c.badge }}</span>
|
||||||
<div class="radio-item">
|
</button>
|
||||||
<input
|
</div>
|
||||||
type="radio"
|
|
||||||
id="mode-resp"
|
<div class="mode-row">
|
||||||
:value="false"
|
<span class="mode-label">数据类型</span>
|
||||||
v-model="isReq"
|
<div class="radio-group">
|
||||||
:disabled="cryptoInfo.respDisabled"
|
<div class="radio-item">
|
||||||
/>
|
<input
|
||||||
<label for="mode-resp">返回数据 response</label>
|
type="radio"
|
||||||
</div>
|
id="mode-req"
|
||||||
|
:value="true"
|
||||||
|
v-model="isReq"
|
||||||
|
:disabled="cryptoInfo.reqDisabled"
|
||||||
|
/>
|
||||||
|
<label for="mode-req">请求数据 request</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio-item">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="mode-resp"
|
||||||
|
:value="false"
|
||||||
|
v-model="isReq"
|
||||||
|
:disabled="cryptoInfo.respDisabled"
|
||||||
|
/>
|
||||||
|
<label for="mode-resp">返回数据 response</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="dataInput">{{ cryptoInfo.inputLabel }}</label>
|
||||||
|
<textarea
|
||||||
|
id="dataInput"
|
||||||
|
v-model="encryptedData"
|
||||||
|
:rows="crypto === 'xeapi' ? 8 : 10"
|
||||||
|
:placeholder="cryptoInfo.placeholder"
|
||||||
|
></textarea>
|
||||||
|
<div class="info-hint" :class="{ warn: cryptoInfo.warning }">
|
||||||
|
{{ cryptoInfo.hint }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-row">
|
||||||
|
<button class="shell-btn shell-btn--default" @click="decrypt">解密</button>
|
||||||
|
<button class="shell-btn shell-btn--success" @click="sendToApi" :disabled="!canSend">
|
||||||
|
填入 API 调试
|
||||||
|
</button>
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="loadExample">加载示例</button>
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="clearAll">清空</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-section shell-mt-4">
|
||||||
|
<div class="section-header">
|
||||||
|
<label>解密结果:</label>
|
||||||
|
<button
|
||||||
|
v-if="result && result !== '{}' && result !== 'null'"
|
||||||
|
class="copy-btn"
|
||||||
|
@click="copyResult"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<pre class="decode-result" v-html="highlightedResult"></pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="shell-card shell-mt-6">
|
||||||
|
<div class="example-section" style="margin-top:0;padding-top:0;border-top:none">
|
||||||
|
<h2>使用示例</h2>
|
||||||
|
<img src="/static/eapi_params.png" alt="请求示例" />
|
||||||
|
<img src="/static/eapi_response.png" alt="响应示例" />
|
||||||
|
|
||||||
|
<div class="example-grid">
|
||||||
|
<div class="example-card">
|
||||||
|
<span class="tag tag-eapi">eapi</span>
|
||||||
|
<h3>请求参数解密</h3>
|
||||||
|
<pre>输入 hex 字符串,解密后得到请求 URL 和参数</pre>
|
||||||
|
<button class="copy-btn" @click="loadExampleData('eapi_req')">加载示例</button>
|
||||||
|
</div>
|
||||||
|
<div class="example-card">
|
||||||
|
<span class="tag tag-linuxapi">linuxapi</span>
|
||||||
|
<h3>请求 eparams 解密</h3>
|
||||||
|
<pre>AES-ECB 解密 linuxapi 的 eparams 参数(hex)</pre>
|
||||||
|
<button class="copy-btn" @click="loadExampleData('linuxapi_req')">加载示例</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
<div class="form-group">
|
|
||||||
<label for="dataInput">{{ cryptoInfo.inputLabel }}</label>
|
|
||||||
<textarea
|
|
||||||
id="dataInput"
|
|
||||||
v-model="encryptedData"
|
|
||||||
:rows="crypto === 'xeapi' ? 8 : 10"
|
|
||||||
:placeholder="cryptoInfo.placeholder"
|
|
||||||
></textarea>
|
|
||||||
<div class="info-hint" :class="{ warn: cryptoInfo.warning }">
|
|
||||||
{{ cryptoInfo.hint }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="action-row">
|
|
||||||
<button @click="decrypt">解密</button>
|
|
||||||
<button class="btn-success" @click="sendToApi" :disabled="!canSend">
|
|
||||||
填入 API 调试
|
|
||||||
</button>
|
|
||||||
<button class="btn-secondary" @click="loadExample">加载示例</button>
|
|
||||||
<button class="btn-secondary" @click="clearAll">清空</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="result-section">
|
|
||||||
<div class="section-header">
|
|
||||||
<label>解密结果:</label>
|
|
||||||
<button
|
|
||||||
v-if="result && result !== '{}' && result !== 'null'"
|
|
||||||
class="copy-btn"
|
|
||||||
@click="copyResult"
|
|
||||||
>
|
|
||||||
复制
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<pre class="decode-result" v-html="highlightedResult"></pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="example-section">
|
|
||||||
<h2>使用示例</h2>
|
|
||||||
<img src="/static/eapi_params.png" alt="请求示例" />
|
|
||||||
<img src="/static/eapi_response.png" alt="响应示例" />
|
|
||||||
|
|
||||||
<div class="example-grid">
|
|
||||||
<div class="example-card">
|
|
||||||
<span class="tag tag-eapi">eapi</span>
|
|
||||||
<h3>请求参数解密</h3>
|
|
||||||
<pre>输入 hex 字符串,解密后得到请求 URL 和参数</pre>
|
|
||||||
<button class="copy-btn" @click="loadExampleData('eapi_req')">加载示例</button>
|
|
||||||
</div>
|
|
||||||
<div class="example-card">
|
|
||||||
<span class="tag tag-linuxapi">linuxapi</span>
|
|
||||||
<h3>请求 eparams 解密</h3>
|
|
||||||
<pre>AES-ECB 解密 linuxapi 的 eparams 参数(hex)</pre>
|
|
||||||
<button class="copy-btn" @click="loadExampleData('linuxapi_req')">加载示例</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios"></script>
|
||||||
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
||||||
|
|||||||
@ -1,221 +1,127 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>听歌识曲 Demo</title>
|
<title>听歌识曲 Demo</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
.subtitle { font-size: 0.8125rem; color: var(--muted-foreground); margin-bottom: 1.5rem; }
|
||||||
padding: 0;
|
.subtitle a { color: var(--info); }
|
||||||
box-sizing: border-box;
|
hr { border: none; border-top: 1px solid var(--border); margin: 1.25rem 0; }
|
||||||
}
|
p { font-size: 0.875rem; color: var(--muted-foreground); line-height: 1.6; margin-bottom: 0.75rem; }
|
||||||
|
a { color: var(--info); }
|
||||||
body {
|
.section { margin-bottom: 1.5rem; }
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #555;
|
|
||||||
line-height: 1.6;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section h3 {
|
.section h3 {
|
||||||
font-size: 16px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
margin-bottom: 0.75rem;
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-group {
|
.control-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 0.75rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:disabled {
|
|
||||||
background: #999;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
padding: 10px 14px;
|
padding: 0.625rem 0.875rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid var(--input);
|
||||||
border-radius: 6px;
|
border-radius: var(--radius);
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
|
background: var(--gray-1);
|
||||||
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group input[type="checkbox"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group label {
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
audio {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0;
|
height: 0;
|
||||||
transition: all linear 0.1s;
|
transition: height linear 0.1s;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
.canvas-active { height: 15vh; }
|
||||||
.canvas-active {
|
|
||||||
height: 15vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-family: 'Courier New', monospace;
|
max-height: 25rem;
|
||||||
font-size: 13px;
|
|
||||||
color: #666;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
max-height: 400px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 16px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
}
|
||||||
|
pre a { color: var(--info); }
|
||||||
.warning {
|
.warning {
|
||||||
padding: 12px 16px;
|
padding: 0.75rem 1rem;
|
||||||
background: #fef3c7;
|
border: 1px solid var(--warning);
|
||||||
border-radius: 6px;
|
background: color-mix(in srgb, var(--warning) 8%, transparent);
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #92400e;
|
color: var(--warning);
|
||||||
margin-bottom: 16px;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
.warning a { color: inherit; }
|
||||||
|
.warning strong { font-weight: 600; }
|
||||||
|
ul.steps { padding-left: 1.25rem; font-size: 0.875rem; color: var(--muted-foreground); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>听歌识曲 Demo</h1>
|
<a class="brand" href="/"><img src="/docs/netease.png" alt="logo">听歌识曲</a>
|
||||||
<p class="subtitle">Credit: <a href="https://github.com/mos9527/ncm-afp" target="_blank">https://github.com/mos9527/ncm-afp</a></p>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<hr>
|
<main class="shell-main">
|
||||||
|
<div class="shell-container shell-narrow" style="max-width:50rem">
|
||||||
|
<header class="shell-header">
|
||||||
|
<h1>听歌识曲 Demo</h1>
|
||||||
|
<p class="subtitle">Credit: <a href="https://github.com/mos9527/ncm-afp" target="_blank">https://github.com/mos9527/ncm-afp</a></p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="warning">
|
<hr>
|
||||||
<strong>免责声明:</strong>本站点使用网易云音乐官方音频识别API(逆向自 <a href="https://fn.music.163.com/g/chrome-extension-home-page-beta/" target="_blank">Chrome 扩展页面</a>),不鼓励版权侵犯或知识产权盗窃。
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
<div class="warning">
|
||||||
<h3>使用说明</h3>
|
<strong>免责声明:</strong>本站点使用网易云音乐官方音频识别API(逆向自 <a href="https://fn.music.163.com/g/chrome-extension-home-page-beta/" target="_blank">Chrome 扩展页面</a>),不鼓励版权侵犯或知识产权盗窃。
|
||||||
<p>在使用本站点之前,您可能需要先访问以下链接:</p>
|
|
||||||
<p><a href="https://cors-anywhere.herokuapp.com/corsdemo" target="_blank">https://cors-anywhere.herokuapp.com/corsdemo</a></p>
|
|
||||||
<p>由于网易云音乐API没有CORS头,这是解决此限制的必要步骤。</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<h3>使用方法</h3>
|
|
||||||
<ul style="padding-left: 20px; font-size: 14px; color: #555;">
|
|
||||||
<li>通过"选择文件"选择您的音频文件</li>
|
|
||||||
<li>点击"识别"按钮并等待结果</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<audio id="audio" controls autoplay></audio>
|
|
||||||
<canvas id="canvas"></canvas>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<button id="invoke">识别</button>
|
|
||||||
<input type="file" name="picker" accept="*" id="file">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<div class="checkbox-group">
|
|
||||||
<input type="checkbox" name="use-mic" id="usemic">
|
|
||||||
<label for="usemic">混合麦克风输入</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section class="shell-card">
|
||||||
|
<div class="section">
|
||||||
|
<h3>使用说明</h3>
|
||||||
|
<p>在使用本站点之前,您可能需要先访问以下链接:</p>
|
||||||
|
<p><a href="https://cors-anywhere.herokuapp.com/corsdemo" target="_blank">https://cors-anywhere.herokuapp.com/corsdemo</a></p>
|
||||||
|
<p>由于网易云音乐API没有CORS头,这是解决此限制的必要步骤。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h3>使用方法</h3>
|
||||||
|
<ul class="steps">
|
||||||
|
<li>通过"选择文件"选择您的音频文件</li>
|
||||||
|
<li>点击"识别"按钮并等待结果</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<audio id="audio" controls autoplay></audio>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
|
||||||
|
<div class="control-group shell-mt-3">
|
||||||
|
<button id="invoke" class="shell-btn shell-btn--default">识别</button>
|
||||||
|
<input type="file" name="picker" accept="*" id="file">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<input type="checkbox" name="use-mic" id="usemic">
|
||||||
|
<label for="usemic">混合麦克风输入</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h3 style="font-family:var(--font-mono);font-size:1rem;font-weight:600;margin-bottom:0.75rem">日志</h3>
|
||||||
|
<pre id="logs"></pre>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h3 style="font-size: 16px; font-weight: 600; color: #333; margin-bottom: 12px;">日志</h3>
|
|
||||||
<pre id="logs"></pre>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script src="./afp.wasm.js"></script>
|
<script src="./afp.wasm.js"></script>
|
||||||
@ -348,7 +254,7 @@
|
|||||||
canvasCtx.fillStyle = 'rgba(0,0,0,0)';
|
canvasCtx.fillStyle = 'rgba(0,0,0,0)';
|
||||||
canvasCtx.fillRect(0, 0, w, h);
|
canvasCtx.fillRect(0, 0, w, h);
|
||||||
if (audioBuffer) {
|
if (audioBuffer) {
|
||||||
canvasCtx.fillStyle = 'black';
|
canvasCtx.fillStyle = '#e4e4e4'; /* 深色主题下用前景色绘制波形 */
|
||||||
for (var x = 0; x < w * bufferHealth; x++) {
|
for (var x = 0; x < w * bufferHealth; x++) {
|
||||||
var y = audioBuffer[Math.ceil((x / w) * audioBuffer.length)]
|
var y = audioBuffer[Math.ceil((x / w) * audioBuffer.length)]
|
||||||
var z = Math.abs(y) * h / 2
|
var z = Math.abs(y) * h / 2
|
||||||
|
|||||||
@ -4,120 +4,73 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>更新头像</title>
|
<title>更新头像</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
}
|
}
|
||||||
|
.shell-header { text-align: center; }
|
||||||
.container {
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 40px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-wrapper {
|
.avatar-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 160px;
|
width: 160px;
|
||||||
height: 160px;
|
height: 160px;
|
||||||
margin: 0 auto 32px;
|
margin: 0 auto 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 3px solid #fff;
|
border: 3px solid var(--gray-5);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
.avatar-wrapper.loading .avatar { opacity: 0.5; }
|
||||||
.avatar-wrapper.loading .avatar {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-overlay {
|
.loading-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: color-mix(in srgb, var(--background) 90%, transparent);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
.avatar-wrapper.loading .loading-overlay { opacity: 1; }
|
||||||
.avatar-wrapper.loading .loading-overlay {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 40px;
|
width: 2.5rem;
|
||||||
height: 40px;
|
height: 2.5rem;
|
||||||
border: 3px solid #e0e0e0;
|
border: 3px solid var(--gray-4);
|
||||||
border-top-color: #333;
|
border-top-color: var(--foreground);
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
animation: spin 0.8s linear infinite;
|
animation: spin 0.8s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to {
|
to { transform: rotate(360deg); }
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-btn {
|
.upload-btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 12px 28px;
|
padding: 0.75rem 1.75rem;
|
||||||
background: #333;
|
background: var(--primary);
|
||||||
color: white;
|
color: var(--primary-foreground);
|
||||||
font-size: 15px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.9375rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s ease;
|
transition: opacity var(--transition);
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.upload-btn:hover { opacity: 0.85; }
|
||||||
.upload-btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn input[type="file"] {
|
.upload-btn input[type="file"] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -127,67 +80,20 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-top: 24px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 24px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) translateY(100px);
|
|
||||||
padding: 12px 20px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
z-index: 1000;
|
|
||||||
max-width: 90%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.show {
|
|
||||||
transform: translateX(-50%) translateY(0);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.success {
|
|
||||||
background: #10b981;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.error {
|
|
||||||
background: #ef4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.info {
|
|
||||||
background: #3b82f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
margin-top: 20px;
|
margin-top: 1.25rem;
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:2.5rem">
|
||||||
<h1>更新头像</h1>
|
<header class="shell-header">
|
||||||
<p class="subtitle">选择一张图片作为您的新头像</p>
|
<h1>更新头像</h1>
|
||||||
|
<p class="sub">选择一张图片作为您的新头像</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="avatar-wrapper" id="avatarWrapper">
|
<div class="avatar-wrapper" id="avatarWrapper">
|
||||||
<img id="avatar" class="avatar" src="" alt="头像" />
|
<img id="avatar" class="avatar" src="" alt="头像" />
|
||||||
@ -196,16 +102,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="upload-btn">
|
<div class="shell-flex shell-flex--center">
|
||||||
选择图片
|
<label class="upload-btn">
|
||||||
<input id="file" type="file" accept="image/*" />
|
选择图片
|
||||||
</label>
|
<input id="file" type="file" accept="image/*" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
还没有登录?点击登录
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">
|
||||||
</a>
|
还没有登录?点击登录
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="hint">支持 JPG、PNG 格式,建议尺寸 200x200</p>
|
<p class="hint">支持 JPG、PNG 格式,建议尺寸 200x200</p>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
|
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast"></div>
|
||||||
|
|||||||
@ -3,276 +3,184 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>云盘上传</title>
|
<title>云盘上传</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-section {
|
.mode-section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 8px;
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-section label {
|
.mode-section label {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-options {
|
.mode-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 1rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-option {
|
.mode-option {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.mode-option input[type="radio"] { margin-top: 3px; }
|
||||||
.mode-option input[type="radio"] {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-option-text {
|
.mode-option-text {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-option-title {
|
.mode-option-title {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-option-desc {
|
.mode-option-desc {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-option input[type="radio"]:checked + .mode-option-text .mode-option-title {
|
.upload-section { margin-bottom: 2rem; }
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-section {
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn {
|
.upload-btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 12px 28px;
|
padding: 0.75rem 1.75rem;
|
||||||
background: #333;
|
background: var(--primary);
|
||||||
color: white;
|
color: var(--primary-foreground);
|
||||||
font-size: 15px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.9375rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s ease;
|
transition: opacity var(--transition);
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.upload-btn:hover { opacity: 0.85; }
|
||||||
.upload-btn:hover {
|
.upload-btn input[type="file"] { display: none; }
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn input[type="file"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn.disabled {
|
.upload-btn.disabled {
|
||||||
background: #ccc;
|
opacity: 0.4;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.songs-list {
|
.songs-list { list-style: none; }
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-item {
|
.song-item {
|
||||||
padding: 12px 16px;
|
padding: 0.75rem 1rem;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid var(--border);
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
.song-item:last-child { border-bottom: none; }
|
||||||
.song-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 40px 20px;
|
padding: 2.5rem 1.25rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
}
|
|
||||||
|
|
||||||
.progress-section {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-section.active {
|
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-section { margin-bottom: 1.5rem; display: none; }
|
||||||
|
.progress-section.active { display: block; }
|
||||||
.progress-item {
|
.progress-item {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
padding: 12px;
|
padding: 0.75rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-item .name {
|
.progress-item .name {
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
color: #333;
|
font-size: 0.875rem;
|
||||||
margin-bottom: 8px;
|
color: var(--foreground);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-item .status {
|
.progress-item .status {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
margin-bottom: 6px;
|
margin-bottom: 0.375rem;
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar {
|
|
||||||
height: 6px;
|
|
||||||
background: #e0e0e0;
|
|
||||||
border-radius: 3px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar .fill {
|
|
||||||
height: 100%;
|
|
||||||
background: #333;
|
|
||||||
border-radius: 3px;
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
width: 0%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-item.success .fill {
|
|
||||||
background: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-item.error .fill {
|
|
||||||
background: #f44336;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-item.error .status {
|
|
||||||
color: #f44336;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-text {
|
.info-text {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
margin-top: 8px;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-text {
|
.warning-text {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #e65100;
|
color: var(--warning);
|
||||||
margin-top: 8px;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>云盘上传</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">云盘上传</a>
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">登录</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="mode-section">
|
<main class="shell-main">
|
||||||
<label>上传模式</label>
|
<div class="shell-container shell-narrow" style="max-width:50rem">
|
||||||
<div class="mode-options">
|
<header class="shell-header">
|
||||||
<label class="mode-option">
|
<h1>云盘上传</h1>
|
||||||
<input type="radio" name="uploadMode" value="direct" checked />
|
<p class="sub">上传本地音频文件到网易云音乐云盘</p>
|
||||||
<span class="mode-option-text">
|
</header>
|
||||||
<span class="mode-option-title">客户端直传</span>
|
|
||||||
<span class="mode-option-desc">文件直接上传到云存储,支持大文件,适合 Vercel 等平台</span>
|
<section class="shell-card">
|
||||||
</span>
|
<div class="mode-section">
|
||||||
</label>
|
<label>上传模式</label>
|
||||||
<label class="mode-option">
|
<div class="mode-options">
|
||||||
<input type="radio" name="uploadMode" value="proxy" />
|
<label class="mode-option">
|
||||||
<span class="mode-option-text">
|
<input type="radio" name="uploadMode" value="direct" checked />
|
||||||
<span class="mode-option-title">后端代理</span>
|
<span class="mode-option-text">
|
||||||
<span class="mode-option-desc">文件通过服务器转发,更简洁,需要服务器支持大文件</span>
|
<span class="mode-option-title">客户端直传</span>
|
||||||
</span>
|
<span class="mode-option-desc">文件直接上传到云存储,支持大文件,适合 Vercel 等平台</span>
|
||||||
</label>
|
</span>
|
||||||
</div>
|
</label>
|
||||||
|
<label class="mode-option">
|
||||||
|
<input type="radio" name="uploadMode" value="proxy" />
|
||||||
|
<span class="mode-option-text">
|
||||||
|
<span class="mode-option-title">后端代理</span>
|
||||||
|
<span class="mode-option-desc">文件通过服务器转发,更简洁,需要服务器支持大文件</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="upload-section">
|
||||||
|
<label class="upload-btn" id="uploadBtn">
|
||||||
|
选择文件(支持多选)
|
||||||
|
<input id="file" type="file" multiple accept="audio/*" />
|
||||||
|
</label>
|
||||||
|
<p class="info-text" id="modeInfo">支持大文件上传,文件将直接传输到云存储服务器</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="progressSection" class="progress-section"></div>
|
||||||
|
|
||||||
|
<div id="app">
|
||||||
|
<div v-if="loading" class="loading">加载中...</div>
|
||||||
|
<ul v-else-if="songs.length > 0" class="songs-list">
|
||||||
|
<li v-for="(item, index) in songs" :key="index" class="song-item">
|
||||||
|
{{ item.songName }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-else class="empty-state">暂无云盘歌曲</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
<div class="upload-section">
|
|
||||||
<label class="upload-btn" id="uploadBtn">
|
|
||||||
选择文件(支持多选)
|
|
||||||
<input id="file" type="file" multiple accept="audio/*" />
|
|
||||||
</label>
|
|
||||||
<p class="info-text" id="modeInfo">支持大文件上传,文件将直接传输到云存储服务器</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="progressSection" class="progress-section"></div>
|
|
||||||
|
|
||||||
<div id="app">
|
|
||||||
<div v-if="loading" class="loading">加载中...</div>
|
|
||||||
<ul v-else-if="songs.length > 0" class="songs-list">
|
|
||||||
<li v-for="(item, index) in songs" :key="index" class="song-item">
|
|
||||||
{{ item.songName }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div v-else class="empty-state">暂无云盘歌曲</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
||||||
|
|||||||
@ -4,196 +4,184 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<link rel="icon" href="docs/netease.png">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>免费听 — 网易云音乐 API Enhanced</title>
|
<title>免费听 — 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
body { padding: 1.25rem; }
|
||||||
--fg: #333;
|
.block { margin-bottom: 1rem; }
|
||||||
--muted: #666;
|
.block h3 { font-size: 0.9375rem; font-weight: 600; margin: 0; }
|
||||||
--border: #ddd;
|
.block .desc { margin: 0.25rem 0 0.75rem; color: var(--muted-foreground); font-size: 0.8125rem; }
|
||||||
--bg: #f5f5f5;
|
pre.data { max-height: 22.5rem; }
|
||||||
--panel: #ffffff;
|
.flex { display: flex; align-items: center; gap: 0.625rem; }
|
||||||
--accent: #333;
|
|
||||||
}
|
|
||||||
* { box-sizing: border-box; }
|
|
||||||
html, body { height: 100%; }
|
|
||||||
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; }
|
|
||||||
.container { max-width: 800px; margin: 40px auto; padding: 0 20px; }
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.container { margin: 20px auto; padding: 0 16px; }
|
|
||||||
header.site-header h1 { font-size: 22px; }
|
|
||||||
.block { padding: 16px; }
|
|
||||||
}
|
|
||||||
header.site-header { margin-bottom: 24px; }
|
|
||||||
header.site-header h1 { font-size: 24px; font-weight: 600; margin: 0; }
|
|
||||||
.sub { margin-top: 4px; color: var(--muted); font-size: 14px; }
|
|
||||||
.block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); }
|
|
||||||
.block h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
|
|
||||||
.block .desc { margin: 0 0 12px; color: var(--muted); font-size: 13px; }
|
|
||||||
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Courier New', monospace; font-size: 13px; max-height: 200px; overflow: auto; }
|
|
||||||
pre.data { max-height: 360px; }
|
|
||||||
.row { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
|
|
||||||
button { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--border); font-size: 14px; font-weight: 500; cursor: pointer; background: var(--panel); color: var(--fg); transition: all .15s; }
|
|
||||||
button:hover { background: #eee; }
|
|
||||||
button.primary { background: var(--fg); color: var(--panel); border-color: var(--fg); }
|
|
||||||
button.primary:hover { opacity: .85; }
|
|
||||||
button:disabled { opacity: .4; cursor: not-allowed; }
|
|
||||||
button.danger { background: #a11; color: #fff; border-color: #a11; }
|
|
||||||
button.danger:hover { opacity: .85; }
|
|
||||||
.tag { display: inline-block; padding: 2px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; border: 1px solid var(--border); }
|
|
||||||
.tag.ok { background: #e6f7e6; color: #1a7a1a; border-color: #b7e6b7; }
|
|
||||||
.tag.no { background: #ffe6e6; color: #a11; border-color: #f5c6c6; }
|
|
||||||
.tag.busy { background: #fff3cd; color: #856404; border-color: #ffeeba; }
|
|
||||||
.flex { display: flex; align-items: center; gap: 10px; }
|
|
||||||
.flex.sb { justify-content: space-between; }
|
.flex.sb { justify-content: space-between; }
|
||||||
.step { display: flex; gap: 12px; align-items: flex-start; }
|
.step { display: flex; gap: 0.75rem; align-items: flex-start; }
|
||||||
.step .num { width: 26px; height: 26px; border-radius: 50%; background: var(--fg); color: var(--panel); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600; flex-shrink: 0; margin-top: 1px; }
|
.step .num {
|
||||||
.step .num.done { background: #1a7a1a; }
|
width: 1.625rem;
|
||||||
|
height: 1.625rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
.step .num.done { background: var(--success); }
|
||||||
.step .body { flex: 1; min-width: 0; }
|
.step .body { flex: 1; min-width: 0; }
|
||||||
input[type=text] { width: 100%; padding: 8px 12px; border-radius: 6px; border: 1px solid var(--border); font-size: 14px; font-family: inherit; margin-top: 6px; }
|
.info-grid { display: grid; grid-template-columns: 5rem 1fr; gap: 0.25rem 0.75rem; font-size: 0.8125rem; margin-top: 0.5rem; }
|
||||||
input[type=text]:focus { outline: 2px solid var(--fg); outline-offset: -1px; border-color: transparent; }
|
.info-grid .lbl { color: var(--muted-foreground); }
|
||||||
footer.site-footer { margin-top: 32px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; font-size: 13px; }
|
|
||||||
footer.site-footer a { color: var(--fg); text-decoration: none; }
|
|
||||||
footer.site-footer a:hover { border-bottom: 1px dotted var(--fg); }
|
|
||||||
.info-grid { display: grid; grid-template-columns: 80px 1fr; gap: 4px 12px; font-size: 13px; margin-top: 8px; }
|
|
||||||
.info-grid .lbl { color: var(--muted); }
|
|
||||||
|
|
||||||
/* ---- 剩余时长计时器 ---- */
|
/* ---- 剩余时长计时器 ---- */
|
||||||
.timer-wrap { text-align: center; padding: 12px 0 4px; }
|
.timer-wrap { text-align: center; padding: 0.75rem 0 0.25rem; }
|
||||||
.timer-row { display: flex; align-items: baseline; justify-content: center; gap: 2px; font-variant-numeric: tabular-nums; }
|
.timer-row { display: flex; align-items: baseline; justify-content: center; gap: 2px; font-variant-numeric: tabular-nums; }
|
||||||
.timer-num { font-size: 36px; font-weight: 700; letter-spacing: 1px; line-height: 1.2; font-family: 'SF Mono', 'Courier New', monospace; min-width: 2ch; text-align: center; }
|
.timer-num { font-size: 2.25rem; font-weight: 700; letter-spacing: 1px; line-height: 1.2; font-family: var(--font-mono); min-width: 2ch; text-align: center; }
|
||||||
.timer-label { font-size: 13px; color: var(--muted); margin-right: 12px; }
|
.timer-label { font-size: 0.8125rem; color: var(--muted-foreground); margin-right: 0.75rem; }
|
||||||
.timer-label:last-child { margin-right: 0; }
|
.timer-label:last-child { margin-right: 0; }
|
||||||
.timer-sep { font-size: 28px; font-weight: 300; color: #bbb; margin: 0 2px; }
|
.timer-sep { font-size: 1.75rem; font-weight: 300; color: var(--gray-5); margin: 0 2px; }
|
||||||
.timer-unit { display: flex; flex-direction: column; align-items: center; }
|
.timer-unit { display: flex; flex-direction: column; align-items: center; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="container">
|
<header class="shell-topbar">
|
||||||
<header class="site-header">
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">免费听</a>
|
||||||
<h1>🎧 免费听 · 看广告获取时长</h1>
|
<nav>
|
||||||
<p class="sub">模拟 App 内看激励视频广告 → 领取免费听权益的完整流程</p>
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
</header>
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<!-- 步骤 1: 注册 Token -->
|
<main class="shell-main">
|
||||||
<div class="block">
|
<div class="shell-container shell-narrow" style="max-width:48rem">
|
||||||
<div class="step">
|
<header class="shell-header">
|
||||||
<div class="num" id="s1num">1</div>
|
<h1>🎧 免费听 · 看广告获取时长</h1>
|
||||||
<div class="body">
|
<p class="sub">模拟 App 内看激励视频广告 → 领取免费听权益的完整流程</p>
|
||||||
<div class="flex sb">
|
</header>
|
||||||
<h3>注册反作弊 Token</h3>
|
|
||||||
<span id="s1tag" class="tag no">未开始</span>
|
<!-- 步骤 1: 注册 Token -->
|
||||||
</div>
|
<section class="shell-card block">
|
||||||
<p class="desc">调用易盾接口获取实时 token,广告接口需要此 token 才能正常响应</p>
|
<div class="step">
|
||||||
<pre id="s1pre">(尚未执行)</pre>
|
<div class="num" id="s1num">1</div>
|
||||||
<div class="row">
|
<div class="body">
|
||||||
<button id="step1" class="primary">① 注册 Token</button>
|
<div class="flex sb">
|
||||||
|
<h3>注册反作弊 Token</h3>
|
||||||
|
<span id="s1tag" class="tag no">未开始</span>
|
||||||
|
</div>
|
||||||
|
<p class="desc">调用易盾接口获取实时 token,广告接口需要此 token 才能正常响应</p>
|
||||||
|
<pre id="s1pre">(尚未执行)</pre>
|
||||||
|
<div class="shell-flex shell-mt-3">
|
||||||
|
<button id="step1" class="shell-btn shell-btn--default">① 注册 Token</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 步骤 2: 获取广告 -->
|
<!-- 步骤 2: 获取广告 -->
|
||||||
<div class="block">
|
<section class="shell-card block">
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<div class="num" id="s2num">2</div>
|
<div class="num" id="s2num">2</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="flex sb">
|
<div class="flex sb">
|
||||||
<h3>获取广告 & 提取 reqId</h3>
|
<h3>获取广告 & 提取 reqId</h3>
|
||||||
<span id="s2tag" class="tag no">未开始</span>
|
<span id="s2tag" class="tag no">未开始</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="desc">请求广告位,从返回的 extJson.contextInfo.req_id 提取用于领取权益的标识</p>
|
<p class="desc">请求广告位,从返回的 extJson.contextInfo.req_id 提取用于领取权益的标识</p>
|
||||||
<pre id="s2pre" class="data">(尚未执行)</pre>
|
<pre id="s2pre" class="data">(尚未执行)</pre>
|
||||||
<div class="row">
|
<div class="shell-flex shell-mt-3">
|
||||||
<button id="step2" class="primary" disabled>② 获取广告</button>
|
<button id="step2" class="shell-btn shell-btn--default" disabled>② 获取广告</button>
|
||||||
<button id="step2b" disabled>换一个广告位</button>
|
<button id="step2b" class="shell-btn shell-btn--outline" disabled>换一个广告位</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-grid" id="s2info" style="display:none">
|
<div class="info-grid" id="s2info" style="display:none">
|
||||||
<div class="lbl">广告 ID</div><div id="s2adId">-</div>
|
<div class="lbl">广告 ID</div><div id="s2adId">-</div>
|
||||||
<div class="lbl">reqId</div><div id="s2reqId">-</div>
|
<div class="lbl">reqId</div><div id="s2reqId">-</div>
|
||||||
<div class="lbl">文案</div><div id="s2text">-</div>
|
<div class="lbl">文案</div><div id="s2text">-</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 步骤 3: 领取权益 -->
|
<!-- 步骤 3: 领取权益 -->
|
||||||
<div class="block">
|
<section class="shell-card block">
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<div class="num" id="s3num">3</div>
|
<div class="num" id="s3num">3</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="flex sb">
|
<div class="flex sb">
|
||||||
<h3>领取免费听权益</h3>
|
<h3>领取免费听权益</h3>
|
||||||
<span id="s3tag" class="tag no">未开始</span>
|
<span id="s3tag" class="tag no">未开始</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="desc">传入 reqId 及相关参数,领取免费听权益(模拟点击广告后的领取请求)</p>
|
<p class="desc">传入 reqId 及相关参数,领取免费听权益(模拟点击广告后的领取请求)</p>
|
||||||
<pre id="s3pre" class="data">(尚未执行)</pre>
|
<pre id="s3pre" class="data">(尚未执行)</pre>
|
||||||
<div class="row">
|
<div class="shell-flex shell-mt-3">
|
||||||
<button id="step3" class="primary" disabled>③ 领取权益</button>
|
<button id="step3" class="shell-btn shell-btn--default" disabled>③ 领取权益</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-grid" id="s3info" style="display:none">
|
<div class="info-grid" id="s3info" style="display:none">
|
||||||
<div class="lbl">结果</div><div id="s3result">-</div>
|
<div class="lbl">结果</div><div id="s3result">-</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 一键全流程 -->
|
<!-- 一键全流程 -->
|
||||||
<div class="block">
|
<section class="shell-card block">
|
||||||
<div class="flex sb">
|
<div class="flex sb">
|
||||||
<h3>一键运行</h3>
|
<h3>一键运行</h3>
|
||||||
<span id="allTag" class="tag no">就绪</span>
|
<span id="allTag" class="tag no">就绪</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="desc" style="margin-top:4px">按顺序自动执行以上三个步骤</p>
|
<p class="desc" style="margin-top:4px">按顺序自动执行以上三个步骤</p>
|
||||||
<div class="row">
|
<div class="shell-flex shell-mt-3">
|
||||||
<button id="runAll" class="primary">▶ 一键获取免费听时长</button>
|
<button id="runAll" class="shell-btn shell-btn--default">▶ 一键获取免费听时长</button>
|
||||||
<button id="clearAll" class="danger">✕ 清空</button>
|
<button id="clearAll" class="shell-btn shell-btn--danger">✕ 清空</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<!-- 剩余时长计时器 -->
|
<!-- 剩余时长计时器 -->
|
||||||
<div class="block" id="rightsBlock">
|
<section class="shell-card block">
|
||||||
<div class="flex sb">
|
<div class="flex sb">
|
||||||
<h3 id="rightsTitleDisplay">🕐 免费听时长剩余</h3>
|
<h3 id="rightsTitleDisplay">🕐 免费听时长剩余</h3>
|
||||||
<span id="rightsTag" class="tag no">未查询</span>
|
<span id="rightsTag" class="tag no">未查询</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="desc" id="rightsSubDesc">查询你的免费听权益剩余时长</p>
|
<p class="desc" id="rightsSubDesc">查询你的免费听权益剩余时长</p>
|
||||||
<div class="timer-wrap">
|
<div class="timer-wrap">
|
||||||
<div class="timer-row" id="timerRow">
|
<div class="timer-row" id="timerRow">
|
||||||
<div class="timer-unit">
|
<div class="timer-unit">
|
||||||
<span class="timer-num" id="timerDays">--</span>
|
<span class="timer-num" id="timerDays">--</span>
|
||||||
<span class="timer-label">天</span>
|
<span class="timer-label">天</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="timer-sep">:</span>
|
<span class="timer-sep">:</span>
|
||||||
<div class="timer-unit">
|
<div class="timer-unit">
|
||||||
<span class="timer-num" id="timerHours">--</span>
|
<span class="timer-num" id="timerHours">--</span>
|
||||||
<span class="timer-label">时</span>
|
<span class="timer-label">时</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="timer-sep">:</span>
|
<span class="timer-sep">:</span>
|
||||||
<div class="timer-unit">
|
<div class="timer-unit">
|
||||||
<span class="timer-num" id="timerMinutes">--</span>
|
<span class="timer-num" id="timerMinutes">--</span>
|
||||||
<span class="timer-label">分</span>
|
<span class="timer-label">分</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="timer-sep">:</span>
|
<span class="timer-sep">:</span>
|
||||||
<div class="timer-unit">
|
<div class="timer-unit">
|
||||||
<span class="timer-num" id="timerSeconds">--</span>
|
<span class="timer-num" id="timerSeconds">--</span>
|
||||||
<span class="timer-label">秒</span>
|
<span class="timer-label">秒</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="shell-flex shell-flex--center shell-mt-3">
|
||||||
<div class="row" style="justify-content:center">
|
<button id="queryRightsBtn" class="shell-btn shell-btn--default">⟳ 查询剩余时长</button>
|
||||||
<button id="queryRightsBtn" class="primary">⟳ 查询剩余时长</button>
|
<button id="claim30Btn" class="shell-btn shell-btn--outline">领取30分钟</button>
|
||||||
<button id="claim30Btn">领取30分钟</button>
|
</div>
|
||||||
</div>
|
<div class="info-grid" id="rightsInfo" style="display:none">
|
||||||
<div class="info-grid" id="rightsInfo" style="display:none">
|
<div class="lbl">状态</div><div id="rightsStatus">-</div>
|
||||||
<div class="lbl">状态</div><div id="rightsStatus">-</div>
|
<div class="lbl">结束时间</div><div id="rightsEndTimeDisplay">-</div>
|
||||||
<div class="lbl">结束时间</div><div id="rightsEndTimeDisplay">-</div>
|
<div class="lbl">今日已覆盖</div><div id="rightsCoverToday">-</div>
|
||||||
<div class="lbl">今日已覆盖</div><div id="rightsCoverToday">-</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="site-footer">
|
<footer class="shell-footer">
|
||||||
<a href="/">← 返回首页</a>
|
<a href="/">← 返回首页</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -3,12 +3,31 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>home</title>
|
<title>home</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
.shell-header { text-align: center; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:2.5rem">
|
||||||
<a href="/qrlogin-nocookie.html"> 如果没登录,请先登录 </a>
|
<header class="shell-header">
|
||||||
|
<h1>首页数据</h1>
|
||||||
|
<p class="sub">获取网易云音乐首页推荐板块数据</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center">
|
||||||
|
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/qrlogin-nocookie.html">
|
||||||
|
如果没登录,请先登录
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -5,108 +5,85 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<link rel="icon" href="docs/netease.png">
|
<link rel="icon" href="docs/netease.png">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>网易云音乐 API Enhanced</title>
|
<title>网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
.kvs { display: grid; grid-template-columns: 7rem 1fr; gap: 0.375rem 0.75rem; align-items: start; font-size: 0.875rem; }
|
||||||
--fg: #333;
|
.kvs .k { color: var(--muted-foreground); }
|
||||||
--muted: #666;
|
.kvs .v { word-break: break-all; overflow-wrap: anywhere; min-width: 0; font-family: var(--font-mono); font-size: 0.8125rem; }
|
||||||
--border: #ddd;
|
ul.links { list-style: none; }
|
||||||
--bg: #f5f5f5;
|
ul.links li { margin: 0.625rem 0; }
|
||||||
--panel: #ffffff;
|
ul.links a { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; border-bottom: 1px solid var(--gray-4); padding-bottom: 0.375rem; }
|
||||||
--accent: #333;
|
ul.links a:hover { text-decoration: none; border-bottom-color: var(--foreground); }
|
||||||
}
|
ul.links code { color: var(--muted-foreground); font-size: 0.8125rem; }
|
||||||
* { box-sizing: border-box; }
|
.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
|
||||||
html, body { height: 100%; }
|
.chips a { border: 1px solid var(--border); padding: 0.25rem 0.625rem; font-size: 0.8125rem; color: var(--foreground); }
|
||||||
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; }
|
.chips a:hover { background: var(--foreground); color: var(--background); text-decoration: none; }
|
||||||
.container { max-width: 960px; margin: 40px auto; padding: 0 20px; }
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.container { margin: 20px auto; padding: 0 16px; }
|
|
||||||
header.site-header h1 { font-size: 22px; }
|
|
||||||
.block { padding: 16px; }
|
|
||||||
}
|
|
||||||
header.site-header { margin-bottom: 24px; }
|
|
||||||
header.site-header h1 { font-size: 28px; font-weight: 600; margin: 0; }
|
|
||||||
.badge { display: inline-block; margin-left: 8px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 12px; font-size: 12px; color: var(--muted); }
|
|
||||||
.sub { margin-top: 8px; color: var(--muted); font-size: 14px; }
|
|
||||||
.block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); }
|
|
||||||
.block h2 { margin: 0 0 12px; font-size: 18px; font-weight: 600; }
|
|
||||||
.kvs { display: grid; grid-template-columns: 100px 1fr; gap: 8px 12px; align-items: start; }
|
|
||||||
.kvs div:first-child { color: var(--muted); flex-shrink: 0; }
|
|
||||||
.kvs div:last-child { word-break: break-all; overflow-wrap: anywhere; min-width: 0; overflow: hidden; }
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.kvs { grid-template-columns: 1fr; gap: 4px 12px; }
|
|
||||||
.kvs div:first-child { font-weight: 500; }
|
|
||||||
}
|
|
||||||
ul.links { list-style: none; padding: 0; margin: 0; }
|
|
||||||
ul.links li { margin: 8px 0; }
|
|
||||||
ul.links a { color: var(--fg); text-decoration: none; border-bottom: 1px dotted var(--border); transition: all 0.2s ease; }
|
|
||||||
ul.links a:hover { color: var(--accent); border-bottom-color: var(--accent); }
|
|
||||||
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; }
|
|
||||||
code { font-family: 'Courier New', monospace; font-size: 13px; }
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
code { font-size: 12px; }
|
|
||||||
}
|
|
||||||
footer.site-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; }
|
|
||||||
footer.site-footer a { color: var(--fg); text-decoration: none; transition: color 0.2s ease; }
|
|
||||||
footer.site-footer a:hover { color: var(--accent); }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="container">
|
<header class="shell-topbar">
|
||||||
<header class="site-header">
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">网易云音乐 API Enhanced</a>
|
||||||
<h1>网易云音乐 API Enhanced <span id="api-version" class="badge"></span></h1>
|
<nav>
|
||||||
<p class="sub">🔍 A revival project for NeteaseCloudMusicApi Node.js Api Services || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护</p>
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/docs" target="_blank">文档</a>
|
||||||
</header>
|
<a class="shell-btn shell-btn--default shell-btn--sm" href="/api.html">API 调试</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<section class="block">
|
<main class="shell-main">
|
||||||
<h2>状态</h2>
|
<div class="shell-container">
|
||||||
<div class="kvs">
|
<header class="shell-header">
|
||||||
<div>Base URL</div><div id="base-url">—</div>
|
<h1>网易云音乐 API Enhanced <span id="api-version" class="shell-badge shell-badge--primary"></span></h1>
|
||||||
<div>当前页</div><div id="current-url">—</div>
|
<p class="sub">🔍 A revival project for NeteaseCloudMusicApi Node.js Api Services || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="shell-grid">
|
||||||
|
<section class="shell-card" style="border:0;">
|
||||||
|
<h3>状态</h3>
|
||||||
|
<div class="kvs">
|
||||||
|
<div class="k">Base URL</div><div class="v" id="base-url">—</div>
|
||||||
|
<div class="k">当前页</div><div class="v" id="current-url">—</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="shell-card" style="border:0;">
|
||||||
|
<h3>常用接口</h3>
|
||||||
|
<ul class="links">
|
||||||
|
<li><a href="/search?keywords=妖精小姐的魔法邀约">搜索音乐<code>GET /search</code></a></li>
|
||||||
|
<li><a href="/song/detail?ids=2756058128">获取音乐详情<code>GET /song/detail</code></a></li>
|
||||||
|
<li><a href="/comment/music?id=2756058128&limit=1">获取音乐评论<code>GET /comment/music</code></a></li>
|
||||||
|
<li><a href="/song/url/v1?id=2756058128&level=exhigh">获取音乐播放链接<code>GET /song/url/v1</code></a></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="block">
|
<section class="shell-card shell-mt-6">
|
||||||
<h2>文档</h2>
|
<h3>调试部分</h3>
|
||||||
<p><a href="/docs" target="_blank">查看在线文档</a></p>
|
<pre><code>curl -s {origin}/inner/version
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="block">
|
|
||||||
<h2>常用接口</h2>
|
|
||||||
<ul class="links">
|
|
||||||
<li><a href="/search?keywords=妖精小姐的魔法邀约">搜索音乐: <code>GET /search</code></a></li>
|
|
||||||
<li><a href="/song/detail?ids=2756058128">获取音乐详情: <code>GET /song/detail</code></a></li>
|
|
||||||
<li><a href="/comment/music?id=2756058128&limit=1">获取音乐评论: <code>GET /comment/music</code></a></li>
|
|
||||||
<li><a href="/song/url/v1?id=2756058128&level=exhigh">获取音乐播放链接: <code>GET /song/url/v1</code></a></li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="block">
|
|
||||||
<h2>调试部分</h2>
|
|
||||||
<pre><code>curl -s {origin}/inner/version
|
|
||||||
curl -s {origin}/search?keywords=网易云</code></pre>
|
curl -s {origin}/search?keywords=网易云</code></pre>
|
||||||
<div style="margin-top:10px; line-height:2;">
|
<div class="chips">
|
||||||
<a href="/api.html">交互式调试</a> ·
|
<a href="/api.html">交互式调试</a>
|
||||||
<a href="/qrlogin.html">二维码登录示例</a> ·
|
<a href="/qrlogin.html">二维码登录示例</a>
|
||||||
<a href="/unblock_test.html">解灰测试</a> ·
|
<a href="/unblock_test.html">解灰测试</a>
|
||||||
<a href="/audio_match_demo/index.html">听歌识曲 Demo</a> ·
|
<a href="/audio_match_demo/index.html">听歌识曲 Demo</a>
|
||||||
<a href="/cloud.html">云盘上传</a> ·
|
<a href="/cloud.html">云盘上传</a>
|
||||||
<a href="/playlist_import.html">歌单导入</a> ·
|
<a href="/playlist_import.html">歌单导入</a>
|
||||||
<a href="/api_decrypt.html">API 解密</a> ·
|
<a href="/api_decrypt.html">API 解密</a>
|
||||||
<a href="/listen_together_host.html">一起听示例</a> ·
|
<a href="/listen_together_host.html">一起听示例</a>
|
||||||
<a href="/playlist_cover_update.html">更新歌单封面示例</a> ·
|
<a href="/playlist_cover_update.html">更新歌单封面示例</a>
|
||||||
<a href="/avatar_update.html">头像更新示例</a> ·
|
<a href="/avatar_update.html">头像更新示例</a>
|
||||||
<a href="/scrobble.html">听歌打卡示例</a> ·
|
<a href="/scrobble.html">听歌打卡示例</a>
|
||||||
<a href="/yidun.html">获取 CheckToken</a> ·
|
<a href="/yidun.html">获取 CheckToken</a>
|
||||||
<a href="/free_listen.html">免费听权益</a> ·
|
<a href="/free_listen.html">免费听权益</a>
|
||||||
<a href="/ugc.html">云小编任务中心</a> ·
|
<a href="/ugc.html">云小编任务中心</a>
|
||||||
<a href="/ugc_lottery.html">云小编每日抽奖</a>
|
<a href="/ugc_lottery.html">云小编每日抽奖</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer class="site-footer">
|
<footer class="shell-footer">
|
||||||
<a href="https://github.com/neteasecloudmusicapienhanced/api-enhanced" target="_blank">GitHub</a>
|
<a href="https://github.com/neteasecloudmusicapienhanced/api-enhanced" target="_blank">GitHub</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -3,317 +3,246 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>一起听 - 主机模式</title>
|
<title>一起听 - 主机模式</title>
|
||||||
<script src="https://unpkg.com/petite-vue"></script>
|
<script src="https://unpkg.com/petite-vue"></script>
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
padding: 12px 16px;
|
padding: 0.75rem 1rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
margin-bottom: 20px;
|
border-left: 3px solid var(--info);
|
||||||
font-size: 14px;
|
margin-bottom: 1.25rem;
|
||||||
color: #555;
|
font-size: 0.875rem;
|
||||||
|
color: var(--foreground);
|
||||||
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
.audio-player { width: 100%; margin-bottom: 1.25rem; }
|
||||||
.audio-player {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
padding: 10px 20px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
margin-right: 8px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section h3 {
|
.section h3 {
|
||||||
font-size: 16px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
margin-bottom: 0.75rem;
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
|
.section h4 {
|
||||||
input[type="text"], input[type="number"] {
|
font-family: var(--font-mono);
|
||||||
padding: 10px 14px;
|
font-size: 0.875rem;
|
||||||
border: 1px solid #ddd;
|
font-weight: 600;
|
||||||
border-radius: 6px;
|
margin: 1rem 0 0.5rem;
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
width: 200px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 0.75rem;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group label {
|
.input-group label {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #555;
|
color: var(--muted-foreground);
|
||||||
min-width: 80px;
|
min-width: 5rem;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.share-link {
|
.share-link {
|
||||||
padding: 12px;
|
padding: 0.75rem;
|
||||||
background: #f0f0f0;
|
background: var(--gray-1);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
font-size: 13px;
|
font-family: var(--font-mono);
|
||||||
color: #666;
|
font-size: 0.8125rem;
|
||||||
|
color: var(--muted-foreground);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-list {
|
.user-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
max-height: 200px;
|
max-height: 12.5rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-item {
|
.user-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 0.75rem;
|
||||||
padding: 10px;
|
padding: 0.625rem;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
.user-item:last-child { border-bottom: none; }
|
||||||
.user-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar {
|
.user-avatar {
|
||||||
width: 40px;
|
width: 2.5rem;
|
||||||
height: 40px;
|
height: 2.5rem;
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-name {
|
.user-name {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-list {
|
.track-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
max-height: 300px;
|
max-height: 18.75rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-item {
|
.track-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 0.75rem;
|
||||||
padding: 10px;
|
padding: 0.625rem;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid var(--border);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s ease;
|
transition: background var(--transition);
|
||||||
}
|
}
|
||||||
|
.track-item:hover { background: var(--gray-4); }
|
||||||
.track-item:hover {
|
.track-item:last-child { border-bottom: none; }
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-cover {
|
.track-cover {
|
||||||
width: 40px;
|
width: 2.5rem;
|
||||||
height: 40px;
|
height: 2.5rem;
|
||||||
border-radius: 4px;
|
border-radius: 2px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-name {
|
.track-name {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
details { cursor: pointer; }
|
||||||
details {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
details summary {
|
details summary {
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #555;
|
color: var(--muted-foreground);
|
||||||
padding: 8px 0;
|
padding: 0.5rem 0;
|
||||||
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
details summary::before { content: '▸ '; }
|
||||||
details summary:hover {
|
details[open] summary::before { content: '▾ '; }
|
||||||
color: #333;
|
details summary:hover { color: var(--foreground); }
|
||||||
}
|
details[open] summary { margin-bottom: 0.75rem; }
|
||||||
|
|
||||||
details[open] summary {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-buttons {
|
.control-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.account-info {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="container">
|
<body>
|
||||||
<a href="/qrlogin.html" class="login-link">还没登录?点击登录</a>
|
<header class="shell-topbar">
|
||||||
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">一起听 · 主机模式</a>
|
||||||
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin.html">登录</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<h1>一起听 - 主机模式</h1>
|
<main class="shell-main">
|
||||||
|
<div class="shell-container shell-narrow" style="max-width:56rem">
|
||||||
|
<header class="shell-header">
|
||||||
|
<h1>一起听 - 主机模式</h1>
|
||||||
|
<p class="sub">创建房间并同步播放,与好友一起听歌</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="message">消息: {{message}}</div>
|
<div class="message">消息: {{message}}</div>
|
||||||
|
|
||||||
<audio id="player" class="audio-player" autoplay controls></audio>
|
<audio id="player" class="audio-player" autoplay controls></audio>
|
||||||
|
|
||||||
<div v-if="!account.login">
|
<div v-if="!account.login">
|
||||||
<button class="btn" @click="login">获取登录状态</button>
|
<button class="shell-btn shell-btn--default" @click="login">获取登录状态</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<section class="section">
|
||||||
<h3>账号信息</h3>
|
<h3>账号信息</h3>
|
||||||
<div>当前登录账号: {{account.nickname}}</div>
|
<div class="account-info">当前登录账号: {{account.nickname}}</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<div v-if="account.login" class="section">
|
<section class="section" v-if="account.login">
|
||||||
<h3>房间管理</h3>
|
<h3>房间管理</h3>
|
||||||
<div class="control-buttons">
|
<div class="control-buttons">
|
||||||
<button v-if="!roomInfo.roomId" class="btn" @click="createRoom">创建房间</button>
|
<button v-if="!roomInfo.roomId" class="shell-btn shell-btn--default" @click="createRoom">创建房间</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>加入房间</summary>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>房间ID:</label>
|
||||||
|
<input v-model="roomInfo.roomId" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>邀请者ID:</label>
|
||||||
|
<input v-model="roomInfo.inviterId" type="text" />
|
||||||
|
</div>
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="joinRoom">加入房间</button>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<div v-if="roomInfo.roomId" style="margin-top:1rem">
|
||||||
|
<h4>分享链接</h4>
|
||||||
|
<div class="share-link">
|
||||||
|
https://st.music.163.com/listen-together/share/?songId=1372188635&roomId={{roomInfo.roomId}}&inviterId={{roomInfo.inviterId}}
|
||||||
|
</div>
|
||||||
|
<div class="control-buttons">
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="refreshRoom">刷新房间状态</button>
|
||||||
|
<button class="shell-btn shell-btn--danger" @click="closeRoom">关闭房间</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>在线用户</h4>
|
||||||
|
<ul class="user-list">
|
||||||
|
<li v-for="user in roomInfo.roomUsers" :key="user.userId" class="user-item">
|
||||||
|
<img :src="user.avatarUrl" class="user-avatar" alt="avatar" />
|
||||||
|
<span class="user-name">{{user.nickname}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<h3>播放控制</h3>
|
||||||
|
<div class="control-buttons">
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="playTrack">播放</button>
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="pauseTrack">暂停</button>
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="seekTrack">同步进度</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>播放列表</summary>
|
||||||
|
<section class="section">
|
||||||
|
<div class="input-group">
|
||||||
|
<label>歌单ID:</label>
|
||||||
|
<input v-model="playlistInfo.playlistId" type="text" />
|
||||||
|
</div>
|
||||||
|
<button class="shell-btn shell-btn--outline" @click="loadPlaylist">加载歌单</button>
|
||||||
|
<div style="margin-top:0.75rem; font-size:0.875rem; color:var(--muted-foreground)">
|
||||||
|
歌单名称: {{playlistInfo.playlistName}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>歌单内容</h4>
|
||||||
|
<ul class="track-list">
|
||||||
|
<li
|
||||||
|
@click="gotoTrack(track.id)"
|
||||||
|
v-for="track in playlistInfo.playlistTracks"
|
||||||
|
:key="track.id"
|
||||||
|
class="track-item"
|
||||||
|
>
|
||||||
|
<img :src="track.al.picUrl" class="track-cover" alt="cover" />
|
||||||
|
<span class="track-name">{{track.name}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
<details>
|
|
||||||
<summary>加入房间</summary>
|
|
||||||
<div class="input-group">
|
|
||||||
<label>房间ID:</label>
|
|
||||||
<input v-model="roomInfo.roomId" type="text" />
|
|
||||||
</div>
|
|
||||||
<div class="input-group">
|
|
||||||
<label>邀请者ID:</label>
|
|
||||||
<input v-model="roomInfo.inviterId" type="text" />
|
|
||||||
</div>
|
|
||||||
<button class="btn" @click="joinRoom">加入房间</button>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<div v-if="roomInfo.roomId" style="margin-top: 16px;">
|
|
||||||
<h4>分享链接</h4>
|
|
||||||
<div class="share-link">
|
|
||||||
https://st.music.163.com/listen-together/share/?songId=1372188635&roomId={{roomInfo.roomId}}&inviterId={{roomInfo.inviterId}}
|
|
||||||
</div>
|
|
||||||
<button class="btn" @click="refreshRoom">刷新房间状态</button>
|
|
||||||
<button class="btn" @click="closeRoom">关闭房间</button>
|
|
||||||
|
|
||||||
<h4 style="margin-top: 16px;">在线用户</h4>
|
|
||||||
<ul class="user-list">
|
|
||||||
<li v-for="user in roomInfo.roomUsers" :key="user.userId" class="user-item">
|
|
||||||
<img :src="user.avatarUrl" class="user-avatar" alt="avatar" />
|
|
||||||
<span class="user-name">{{user.nickname}}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<h3>播放控制</h3>
|
|
||||||
<div class="control-buttons">
|
|
||||||
<button class="btn" @click="playTrack">播放</button>
|
|
||||||
<button class="btn" @click="pauseTrack">暂停</button>
|
|
||||||
<button class="btn" @click="seekTrack">同步进度</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>播放列表</summary>
|
|
||||||
<div class="section">
|
|
||||||
<div class="input-group">
|
|
||||||
<label>歌单ID:</label>
|
|
||||||
<input v-model="playlistInfo.playlistId" type="text" />
|
|
||||||
</div>
|
|
||||||
<button class="btn" @click="loadPlaylist">加载歌单</button>
|
|
||||||
<div style="margin-top: 12px; font-size: 14px; color: #555;">
|
|
||||||
歌单名称: {{playlistInfo.playlistName}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4 style="margin-top: 16px;">歌单内容</h4>
|
|
||||||
<ul class="track-list">
|
|
||||||
<li
|
|
||||||
@click="gotoTrack(track.id)"
|
|
||||||
v-for="track in playlistInfo.playlistTracks"
|
|
||||||
:key="track.id"
|
|
||||||
class="track-item"
|
|
||||||
>
|
|
||||||
<img :src="track.al.picUrl" class="track-cover" alt="cover" />
|
|
||||||
<span class="track-name">{{track.name}}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -4,137 +4,45 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>登录</title>
|
<title>登录</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 40px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 15px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
width: 100%;
|
|
||||||
padding: 14px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:disabled {
|
|
||||||
background: #999;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 24px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #666;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: #ef4444;
|
|
||||||
background: #fef2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
color: #10b981;
|
|
||||||
background: #f0fdf4;
|
|
||||||
}
|
}
|
||||||
|
.shell-header { text-align: center; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:2.5rem">
|
||||||
<h1>登录</h1>
|
<header class="shell-header">
|
||||||
<p class="subtitle">使用手机号和密码登录网易云音乐</p>
|
<h1>登录</h1>
|
||||||
|
<p class="sub">使用手机号和密码登录网易云音乐</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" style="text-align:left">
|
||||||
<label for="phone">手机号</label>
|
<label for="phone">手机号</label>
|
||||||
<input type="tel" id="phone" placeholder="请输入手机号" />
|
<input type="tel" id="phone" placeholder="请输入手机号" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" style="text-align:left">
|
||||||
<label for="password">密码</label>
|
<label for="password">密码</label>
|
||||||
<input type="password" id="password" placeholder="请输入密码" />
|
<input type="password" id="password" placeholder="请输入密码" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="loginBtn" class="btn" onclick="handleLogin()">登录</button>
|
<button id="loginBtn" class="shell-btn shell-btn--default shell-btn--block" onclick="handleLogin()">登录</button>
|
||||||
|
|
||||||
<div id="result" class="result" style="display: none;"></div>
|
<div id="result" class="result" style="display: none;"></div>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
|
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/qrlogin.html">改用二维码登录</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
|
|||||||
@ -4,182 +4,81 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>歌单封面上传</title>
|
<title>歌单封面上传</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; display: flex; align-items: flex-start; justify-content: center; }
|
||||||
margin: 0;
|
.shell-header { text-align: center; }
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-wrapper {
|
.cover-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 180px;
|
height: 180px;
|
||||||
margin: 0 auto 24px;
|
margin: 0 auto 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover {
|
.cover {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 2px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 4px solid #fff;
|
border: 1px solid var(--border);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 12px 28px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn input[type="file"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
background: color-mix(in srgb, var(--background) 85%, transparent);
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
.loading.active { opacity: 1; }
|
||||||
.loading.active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 48px;
|
width: 3rem;
|
||||||
height: 48px;
|
height: 3rem;
|
||||||
border: 4px solid #e0e0e0;
|
border: 4px solid var(--gray-4);
|
||||||
border-top-color: #333;
|
border-top-color: var(--foreground);
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
animation: spin 0.8s linear infinite;
|
animation: spin 0.8s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to {
|
to { transform: rotate(360deg); }
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.upload-btn {
|
||||||
.result {
|
display: inline-block;
|
||||||
margin-top: 20px;
|
padding: 0.75rem 1.75rem;
|
||||||
padding: 12px 16px;
|
background: var(--primary);
|
||||||
border-radius: 6px;
|
color: var(--primary-foreground);
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
text-align: left;
|
font-size: 0.9375rem;
|
||||||
}
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
.result.success {
|
transition: opacity var(--transition);
|
||||||
background: #d1fae5;
|
border: none;
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.error {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
}
|
||||||
|
.upload-btn:hover { opacity: 0.85; }
|
||||||
|
.upload-btn input[type="file"] { display: none; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:2.5rem">
|
||||||
<h1>歌单封面上传</h1>
|
<header class="shell-header">
|
||||||
<p class="subtitle">上传自定义歌单封面图片</p>
|
<h1>歌单封面上传</h1>
|
||||||
|
<p class="sub">上传自定义歌单封面图片</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
|
<div class="shell-flex shell-flex--center shell-mb-4">
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">还没登录?点击登录</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" style="text-align:left">
|
||||||
<label for="playlistId">歌单 ID</label>
|
<label for="playlistId">歌单 ID</label>
|
||||||
<input type="text" id="playlistId" placeholder="请输入歌单ID" />
|
<input type="text" id="playlistId" placeholder="请输入歌单ID" />
|
||||||
</div>
|
</div>
|
||||||
@ -191,12 +90,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="upload-btn">
|
<div class="shell-flex shell-flex--center">
|
||||||
选择封面图片
|
<label class="upload-btn">
|
||||||
<input id="file" type="file" name="filename" accept="image/*" />
|
选择封面图片
|
||||||
</label>
|
<input id="file" type="file" name="filename" accept="image/*" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="result" class="result" style="display: none;"></div>
|
<div id="result" class="result" style="display: none;"></div>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
|
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
|
|||||||
@ -3,266 +3,125 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" href="docs/netease.png">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>歌单导入工具</title>
|
<title>歌单导入工具</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn {
|
|
||||||
padding: 12px 24px;
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #666;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn:hover {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
|
||||||
color: #333;
|
|
||||||
border-bottom-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"], textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus, textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
min-height: 120px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th, table td {
|
|
||||||
padding: 12px;
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #555;
|
|
||||||
background: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
.input-group input { flex: 1; }
|
||||||
.input-group input {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
.checkbox-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-group input[type="checkbox"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group label {
|
.checkbox-group label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
.removeLinkButton { flex-shrink: 0; }
|
||||||
input:disabled {
|
|
||||||
background: #f5f5f5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>歌单导入工具</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">歌单导入</a>
|
||||||
<p class="subtitle">请选择一种导入方式并填写相关信息</p>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<ul class="tabs" id="importTabs" role="tablist">
|
<main class="shell-main">
|
||||||
<li role="presentation">
|
<div class="shell-container shell-narrow" style="max-width:56rem">
|
||||||
<button class="tab-btn active" id="metadata-tab" data-bs-toggle="tab" data-bs-target="#metadata" type="button" role="tab" aria-controls="metadata" aria-selected="true">元数据导入</button>
|
<header class="shell-header">
|
||||||
</li>
|
<h1>歌单导入工具</h1>
|
||||||
<li role="presentation">
|
<p class="subtitle">请选择一种导入方式并填写相关信息</p>
|
||||||
<button class="tab-btn" id="text-tab" data-bs-toggle="tab" data-bs-target="#text" type="button" role="tab" aria-controls="text" aria-selected="false">文字导入</button>
|
</header>
|
||||||
</li>
|
|
||||||
<li role="presentation">
|
|
||||||
<button class="tab-btn" id="link-tab" data-bs-toggle="tab" data-bs-target="#link" type="button" role="tab" aria-controls="link" aria-selected="false">链接导入</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="tab-content active" id="importTabContent">
|
<section class="shell-card">
|
||||||
<!-- 元数据导入 -->
|
<ul class="tabs" id="importTabs" role="tablist">
|
||||||
<div class="tab-content active" id="metadata" role="tabpanel" aria-labelledby="metadata-tab">
|
<li role="presentation" style="list-style:none">
|
||||||
<table>
|
<button class="tab-btn active" id="metadata-tab" data-bs-toggle="tab" data-bs-target="#metadata" type="button" role="tab" aria-controls="metadata" aria-selected="true">元数据导入</button>
|
||||||
<thead>
|
</li>
|
||||||
<tr>
|
<li role="presentation" style="list-style:none">
|
||||||
<th style="width: 33%">歌曲名称</th>
|
<button class="tab-btn" id="text-tab" data-bs-toggle="tab" data-bs-target="#text" type="button" role="tab" aria-controls="text" aria-selected="false">文字导入</button>
|
||||||
<th style="width: 33%">艺术家</th>
|
</li>
|
||||||
<th style="width: 33%">专辑</th>
|
<li role="presentation" style="list-style:none">
|
||||||
</tr>
|
<button class="tab-btn" id="link-tab" data-bs-toggle="tab" data-bs-target="#link" type="button" role="tab" aria-controls="link" aria-selected="false">链接导入</button>
|
||||||
</thead>
|
</li>
|
||||||
<tbody id="metadataTableBody">
|
</ul>
|
||||||
<tr>
|
|
||||||
<td><input type="text" name="name[]" placeholder="歌曲名称"></td>
|
|
||||||
<td><input type="text" name="artist[]" placeholder="艺术家"></td>
|
|
||||||
<td><input type="text" name="album[]" placeholder="专辑"></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<button type="button" class="btn btn-secondary" id="addMetadataRow">增加歌曲</button>
|
|
||||||
</div>
|
|
||||||
<!-- 文字导入 -->
|
|
||||||
<div class="tab-content" id="text" role="tabpanel" aria-labelledby="text-tab">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="textInput">文字内容</label>
|
|
||||||
<textarea id="textInput" name="text" rows="5" placeholder="请输入歌曲信息,每行一首歌曲"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="playlistNameInput">歌单名称</label>
|
|
||||||
<input type="text" id="playlistNameInput" name="playlistName" placeholder="请输入歌单名">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 链接导入 -->
|
|
||||||
<div class="tab-content" id="link" role="tabpanel" aria-labelledby="link-tab">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>链接列表</label>
|
|
||||||
<div id="linkInputsContainer">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" id="linkInput0" name="linkInput0" placeholder="请输入链接">
|
|
||||||
<button type="button" class="btn btn-secondary removeLinkButton" data-index="0">×</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-secondary" id="addLinkButton" style="margin-top: 8px;">增加链接</button>
|
|
||||||
</div>
|
|
||||||
<div class="form-group" style="margin-top: 20px;">
|
|
||||||
<label for="playlistNameLinkInput">歌单名称</label>
|
|
||||||
<input type="text" id="playlistNameLinkInput" name="playlistName" placeholder="请输入歌单名">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="checkbox-group">
|
<div class="tab-content active" id="importTabContent">
|
||||||
<input type="checkbox" value="" id="importStarCheckbox">
|
<!-- 元数据导入 -->
|
||||||
<label for="importStarCheckbox">
|
<div class="tab-content active" id="metadata" role="tabpanel" aria-labelledby="metadata-tab">
|
||||||
导入"我喜欢的音乐"
|
<table>
|
||||||
</label>
|
<thead>
|
||||||
</div>
|
<tr>
|
||||||
|
<th style="width: 33%">歌曲名称</th>
|
||||||
|
<th style="width: 33%">艺术家</th>
|
||||||
|
<th style="width: 33%">专辑</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="metadataTableBody">
|
||||||
|
<tr>
|
||||||
|
<td><input type="text" name="name[]" placeholder="歌曲名称"></td>
|
||||||
|
<td><input type="text" name="artist[]" placeholder="艺术家"></td>
|
||||||
|
<td><input type="text" name="album[]" placeholder="专辑"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button type="button" class="shell-btn shell-btn--outline" id="addMetadataRow">增加歌曲</button>
|
||||||
|
</div>
|
||||||
|
<!-- 文字导入 -->
|
||||||
|
<div class="tab-content" id="text" role="tabpanel" aria-labelledby="text-tab">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="textInput">文字内容</label>
|
||||||
|
<textarea id="textInput" name="text" rows="5" placeholder="请输入歌曲信息,每行一首歌曲"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="playlistNameInput">歌单名称</label>
|
||||||
|
<input type="text" id="playlistNameInput" name="playlistName" placeholder="请输入歌单名">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 链接导入 -->
|
||||||
|
<div class="tab-content" id="link" role="tabpanel" aria-labelledby="link-tab">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>链接列表</label>
|
||||||
|
<div id="linkInputsContainer">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="linkInput0" name="linkInput0" placeholder="请输入链接">
|
||||||
|
<button type="button" class="shell-btn shell-btn--outline shell-btn--sm removeLinkButton" data-index="0">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="shell-btn shell-btn--outline" id="addLinkButton">增加链接</button>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="margin-top:1.25rem">
|
||||||
|
<label for="playlistNameLinkInput">歌单名称</label>
|
||||||
|
<input type="text" id="playlistNameLinkInput" name="playlistName" placeholder="请输入歌单名">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn" id="submitBtn">导入歌曲</button>
|
<div class="checkbox-group">
|
||||||
</div>
|
<input type="checkbox" value="" id="importStarCheckbox">
|
||||||
|
<label for="importStarCheckbox">
|
||||||
|
导入"我喜欢的音乐"
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="shell-btn shell-btn--default" id="submitBtn">导入歌曲</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@ -291,11 +150,11 @@
|
|||||||
newInput.id = `linkInput${newIndex}`;
|
newInput.id = `linkInput${newIndex}`;
|
||||||
newInput.name = `linkInput${newIndex}`;
|
newInput.name = `linkInput${newIndex}`;
|
||||||
newInput.placeholder = '请输入链接';
|
newInput.placeholder = '请输入链接';
|
||||||
newInput.style.cssText = 'flex: 1; padding: 10px 14px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none;';
|
newInput.style.cssText = 'flex: 1; padding: 10px 14px; border: 1px solid #4d4d4d; border-radius: 0; font-size: 14px; outline: none; background:#171717; color:#e4e4e4;';
|
||||||
|
|
||||||
var removeButton = document.createElement('button');
|
var removeButton = document.createElement('button');
|
||||||
removeButton.type = 'button';
|
removeButton.type = 'button';
|
||||||
removeButton.className = 'btn btn-secondary';
|
removeButton.className = 'shell-btn shell-btn--outline shell-btn--sm';
|
||||||
removeButton.textContent = '×';
|
removeButton.textContent = '×';
|
||||||
removeButton.dataset.index = newIndex.toString();
|
removeButton.dataset.index = newIndex.toString();
|
||||||
removeButton.addEventListener('click', function() {
|
removeButton.addEventListener('click', function() {
|
||||||
@ -317,9 +176,9 @@
|
|||||||
var newRow = document.createElement('tr');
|
var newRow = document.createElement('tr');
|
||||||
|
|
||||||
newRow.innerHTML = `
|
newRow.innerHTML = `
|
||||||
<td><input type="text" name="name[]" placeholder="歌曲名称" style="width: 100%; padding: 10px 14px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none;"></td>
|
<td><input type="text" name="name[]" placeholder="歌曲名称"></td>
|
||||||
<td><input type="text" name="artist[]" placeholder="艺术家" style="width: 100%; padding: 10px 14px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none;"></td>
|
<td><input type="text" name="artist[]" placeholder="艺术家"></td>
|
||||||
<td><input type="text" name="album[]" placeholder="专辑" style="width: 100%; padding: 10px 14px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none;"></td>
|
<td><input type="text" name="album[]" placeholder="专辑"></td>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
container.appendChild(newRow);
|
container.appendChild(newRow);
|
||||||
|
|||||||
@ -4,110 +4,67 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>二维码登录</title>
|
<title>二维码登录</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 48px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 450px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-wrapper {
|
.qr-wrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 8px;
|
border: 1px solid var(--border);
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#qrImg {
|
#qrImg {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
font-family: 'Courier New', monospace;
|
font-family: var(--font-mono);
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
max-height: 200px;
|
max-height: 12.5rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
margin-top: 16px;
|
margin-top: 1rem;
|
||||||
padding: 12px;
|
padding: 0.75rem;
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.status.waiting { border-color: var(--warning); color: var(--warning); background: color-mix(in srgb, var(--warning) 8%, transparent); }
|
||||||
.status.waiting {
|
.status.success { border-color: var(--success); color: var(--success); background: color-mix(in srgb, var(--success) 8%, transparent); }
|
||||||
background: #fef3c7;
|
.status.error { border-color: var(--destructive); color: var(--destructive); background: color-mix(in srgb, var(--destructive) 8%, transparent); }
|
||||||
color: #92400e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status.success {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status.error {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
margin-top: 16px;
|
margin-top: 1rem;
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
.shell-header { text-align: center; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:3rem">
|
||||||
<h1>二维码登录</h1>
|
<header class="shell-header">
|
||||||
<p class="subtitle">使用网易云音乐App扫描二维码登录</p>
|
<h1>二维码登录</h1>
|
||||||
|
<p class="sub">使用网易云音乐App扫描二维码登录</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="qr-wrapper">
|
<div class="qr-wrapper">
|
||||||
<img id="qrImg" src="" alt="二维码加载中..." />
|
<img id="qrImg" src="" alt="二维码加载中..." />
|
||||||
@ -115,9 +72,13 @@
|
|||||||
|
|
||||||
<div id="status" class="status waiting">等待扫描...</div>
|
<div id="status" class="status waiting">等待扫描...</div>
|
||||||
|
|
||||||
<div id="info" class="info"></div>
|
<div id="info" class="info shell-mt-3"></div>
|
||||||
|
|
||||||
<p class="hint">请打开网易云音乐App,扫描上方二维码完成登录</p>
|
<p class="hint">请打开网易云音乐App,扫描上方二维码完成登录</p>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
|
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
|
|||||||
@ -4,110 +4,67 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>二维码登录</title>
|
<title>二维码登录</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 48px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 450px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-wrapper {
|
.qr-wrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 8px;
|
border: 1px solid var(--border);
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#qrImg {
|
#qrImg {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
font-family: 'Courier New', monospace;
|
font-family: var(--font-mono);
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
max-height: 200px;
|
max-height: 12.5rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
margin-top: 16px;
|
margin-top: 1rem;
|
||||||
padding: 12px;
|
padding: 0.75rem;
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.status.waiting { border-color: var(--warning); color: var(--warning); background: color-mix(in srgb, var(--warning) 8%, transparent); }
|
||||||
.status.waiting {
|
.status.success { border-color: var(--success); color: var(--success); background: color-mix(in srgb, var(--success) 8%, transparent); }
|
||||||
background: #fef3c7;
|
.status.error { border-color: var(--destructive); color: var(--destructive); background: color-mix(in srgb, var(--destructive) 8%, transparent); }
|
||||||
color: #92400e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status.success {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status.error {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
margin-top: 16px;
|
margin-top: 1rem;
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
.shell-header { text-align: center; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:3rem">
|
||||||
<h1>二维码登录</h1>
|
<header class="shell-header">
|
||||||
<p class="subtitle">使用网易云音乐App扫描二维码登录</p>
|
<h1>二维码登录</h1>
|
||||||
|
<p class="sub">使用网易云音乐App扫描二维码登录</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="qr-wrapper">
|
<div class="qr-wrapper">
|
||||||
<img id="qrImg" src="" alt="二维码加载中..." />
|
<img id="qrImg" src="" alt="二维码加载中..." />
|
||||||
@ -115,9 +72,13 @@
|
|||||||
|
|
||||||
<div id="status" class="status waiting">等待扫描...</div>
|
<div id="status" class="status waiting">等待扫描...</div>
|
||||||
|
|
||||||
<div id="info" class="info"></div>
|
<div id="info" class="info shell-mt-3"></div>
|
||||||
|
|
||||||
<p class="hint">请打开网易云音乐App,扫描上方二维码完成登录</p>
|
<p class="hint">请打开网易云音乐App,扫描上方二维码完成登录</p>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
|
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
|
|||||||
@ -4,346 +4,127 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>听歌打卡 - 网易云音乐 API Enhanced</title>
|
<title>听歌打卡 - 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
.shell-header { text-align: center; }
|
||||||
padding: 0;
|
.btn-shell { display: block; width: 100%; }
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 520px;
|
|
||||||
margin: 40px auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Tabs ---- */
|
|
||||||
.tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 0;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
border-bottom: 2px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn {
|
|
||||||
flex: 1;
|
|
||||||
padding: 10px 0;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
margin-bottom: -2px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #999;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn:hover {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
|
||||||
color: #333;
|
|
||||||
border-bottom-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Form ---- */
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 1px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.r {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.o {
|
|
||||||
background: #fef3c7;
|
|
||||||
color: #92400e;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="number"],
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
font-family: inherit;
|
|
||||||
transition: border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus,
|
|
||||||
input[type="number"]:focus,
|
|
||||||
textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row .form-group {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill {
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span {
|
|
||||||
color: #0066cc;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 16px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.success {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.error {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.info {
|
|
||||||
background: #e0f2fe;
|
|
||||||
color: #0369a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 24px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a {
|
|
||||||
color: #666;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>听歌打卡</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">听歌打卡</a>
|
||||||
<p class="subtitle">同步听歌记录至网易云音乐,增加听歌排行计数</p>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">登录</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
|
<main class="shell-main">
|
||||||
|
<div class="shell-container shell-narrow">
|
||||||
|
<header class="shell-header">
|
||||||
|
<h1>听歌打卡</h1>
|
||||||
|
<p class="sub">同步听歌记录至网易云音乐,增加听歌排行计数</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<!-- Cookie (共享) -->
|
<section class="shell-card">
|
||||||
<div class="form-group">
|
<!-- Cookie (共享) -->
|
||||||
<label for="cookie">Cookie <span class="tag o">可选</span></label>
|
|
||||||
<textarea id="cookie" placeholder="留空则默认读取本地存储的登录态" rows="2"></textarea>
|
|
||||||
<div class="quick-fill">
|
|
||||||
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Tabs -->
|
|
||||||
<div class="tabs">
|
|
||||||
<button class="tab-btn active" data-tab="v0" onclick="switchTab('v0')">原版</button>
|
|
||||||
<button class="tab-btn" data-tab="v1" onclick="switchTab('v1')">V2 (NCBL 加密版)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ============ Tab: v0 (原版 /scrobble) ============ -->
|
|
||||||
<div class="tab-content active" id="tab-v0">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲 ID <span class="tag r">必填</span></label>
|
|
||||||
<input type="text" id="v0-songId" placeholder="例如: 2756058128" value="2756058128" />
|
|
||||||
<div class="quick-fill">
|
|
||||||
示例:
|
|
||||||
<span onclick="fillV0('2756058128', '288651229')">妖精小姐的魔法邀约</span>
|
|
||||||
<span onclick="fillV0('2637402867', '251025018')">Echoes of Memoria</span>
|
|
||||||
<span onclick="fillV0('36307815', '3394198')">Lose Control</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>来源 ID (歌单或专辑 ID) <span class="tag r">必填</span></label>
|
|
||||||
<input type="text" id="v0-sourceId" placeholder="例如: 2756058128" value="2756058128" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>播放时间 (秒) <span class="tag o">可选</span></label>
|
|
||||||
<input type="number" id="v0-time" placeholder="300" value="300" />
|
|
||||||
</div>
|
|
||||||
<button class="btn" onclick="submitV0()">立即打卡 (原版)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ============ Tab: v1 (NCBL 加密版 /scrobble/v1) ============ -->
|
|
||||||
<div class="tab-content" id="tab-v1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲 ID <span class="tag r">必填</span></label>
|
|
||||||
<input type="text" id="v1-songId" placeholder="例如: 518066366" value="518066366" />
|
|
||||||
<div class="quick-fill">
|
|
||||||
示例:
|
|
||||||
<span onclick="fillV1('518066366', '36780169', '291')">默认示例</span>
|
|
||||||
<span onclick="fillV1('2756058128', '288651229', '300')">妖精小姐的魔法邀约</span>
|
|
||||||
<span onclick="fillV1('2637402867', '251025018', '300')">Echoes of Memoria</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>播放时间 (秒) <span class="tag r">必填</span></label>
|
|
||||||
<input type="number" id="v1-time" placeholder="例如: 291" value="291" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>来源 ID <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-sourceId" placeholder="歌单或专辑 ID" value="36780169" />
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>歌曲名 <span class="tag o">可选</span></label>
|
<label for="cookie">Cookie <span class="tag o">可选</span></label>
|
||||||
<input type="text" id="v1-name" placeholder="歌曲名称" />
|
<textarea id="cookie" placeholder="留空则默认读取本地存储的登录态" rows="2"></textarea>
|
||||||
|
<div class="quick-fill">
|
||||||
|
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label>艺术家 <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-artist" placeholder="歌手名" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>码率 <span class="tag o">可选</span></label>
|
|
||||||
<input type="number" id="v1-bitrate" placeholder="默认 320" value="320" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>音质等级 <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-level" placeholder="默认 exhigh" value="exhigh" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲总时长 (秒) <span class="tag o">可选</span></label>
|
|
||||||
<input type="number" id="v1-total" placeholder="留空则与播放时间相同" />
|
|
||||||
</div>
|
|
||||||
<button class="btn" onclick="submitV1()">立即打卡 (NCBL 加密版)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Result -->
|
<!-- Tabs -->
|
||||||
<div id="result" class="result" style="display: none;"></div>
|
<div class="tabs">
|
||||||
|
<button class="tab-btn active" data-tab="v0" onclick="switchTab('v0')">原版</button>
|
||||||
|
<button class="tab-btn" data-tab="v1" onclick="switchTab('v1')">V2 (NCBL 加密版)</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="footer-link">
|
<!-- ============ Tab: v0 (原版 /scrobble) ============ -->
|
||||||
<a href="/">返回首页</a>
|
<div class="tab-content active" id="tab-v0">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>歌曲 ID <span class="tag r">必填</span></label>
|
||||||
|
<input type="text" id="v0-songId" placeholder="例如: 2756058128" value="2756058128" />
|
||||||
|
<div class="quick-fill">
|
||||||
|
示例:
|
||||||
|
<span onclick="fillV0('2756058128', '288651229')">妖精小姐的魔法邀约</span>
|
||||||
|
<span onclick="fillV0('2637402867', '251025018')">Echoes of Memoria</span>
|
||||||
|
<span onclick="fillV0('36307815', '3394198')">Lose Control</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>来源 ID (歌单或专辑 ID) <span class="tag r">必填</span></label>
|
||||||
|
<input type="text" id="v0-sourceId" placeholder="例如: 2756058128" value="2756058128" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>播放时间 (秒) <span class="tag o">可选</span></label>
|
||||||
|
<input type="number" id="v0-time" placeholder="300" value="300" />
|
||||||
|
</div>
|
||||||
|
<button class="btn shell-btn shell-btn--default shell-btn--block" onclick="submitV0()">立即打卡 (原版)</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ============ Tab: v1 (NCBL 加密版 /scrobble/v1) ============ -->
|
||||||
|
<div class="tab-content" id="tab-v1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>歌曲 ID <span class="tag r">必填</span></label>
|
||||||
|
<input type="text" id="v1-songId" placeholder="例如: 518066366" value="518066366" />
|
||||||
|
<div class="quick-fill">
|
||||||
|
示例:
|
||||||
|
<span onclick="fillV1('518066366', '36780169', '291')">默认示例</span>
|
||||||
|
<span onclick="fillV1('2756058128', '288651229', '300')">妖精小姐的魔法邀约</span>
|
||||||
|
<span onclick="fillV1('2637402867', '251025018', '300')">Echoes of Memoria</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>播放时间 (秒) <span class="tag r">必填</span></label>
|
||||||
|
<input type="number" id="v1-time" placeholder="例如: 291" value="291" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>来源 ID <span class="tag o">可选</span></label>
|
||||||
|
<input type="text" id="v1-sourceId" placeholder="歌单或专辑 ID" value="36780169" />
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>歌曲名 <span class="tag o">可选</span></label>
|
||||||
|
<input type="text" id="v1-name" placeholder="歌曲名称" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>艺术家 <span class="tag o">可选</span></label>
|
||||||
|
<input type="text" id="v1-artist" placeholder="歌手名" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>码率 <span class="tag o">可选</span></label>
|
||||||
|
<input type="number" id="v1-bitrate" placeholder="默认 320" value="320" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>音质等级 <span class="tag o">可选</span></label>
|
||||||
|
<input type="text" id="v1-level" placeholder="默认 exhigh" value="exhigh" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>歌曲总时长 (秒) <span class="tag o">可选</span></label>
|
||||||
|
<input type="number" id="v1-total" placeholder="留空则与播放时间相同" />
|
||||||
|
</div>
|
||||||
|
<button class="btn shell-btn shell-btn--default shell-btn--block" onclick="submitV1()">立即打卡 (NCBL 加密版)</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Result -->
|
||||||
|
<div id="result" class="result" style="display: none;"></div>
|
||||||
|
|
||||||
|
<div class="shell-flex shell-flex--center shell-mt-4">
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
901
public/static/shell.css
Normal file
901
public/static/shell.css
Normal file
@ -0,0 +1,901 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
shell.css — 网易云音乐 API Enhanced 统一设计系统(深色 · 直角)
|
||||||
|
技术栈:原生 HTML + CSS 变量设计令牌 / shadcn 风格组件 / 无构建静态托管
|
||||||
|
字体:font-mono 等宽优先(按钮/徽章/代码),正文系统无衬线
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* ============================== 设计令牌 ============================== */
|
||||||
|
:root {
|
||||||
|
--background: #171717; /* 页底 */
|
||||||
|
--foreground: #e4e4e4; /* 正文 */
|
||||||
|
--card: #1f1f1f; /* 卡片 */
|
||||||
|
--card-foreground: #e4e4e4;
|
||||||
|
--popover: #242424; /* 浮层 */
|
||||||
|
--popover-foreground: #e4e4e4;
|
||||||
|
--primary: #e4e4e4; /* 主按钮 = 白底黑字 */
|
||||||
|
--primary-foreground: #171717;
|
||||||
|
--secondary: #a4a4a4;
|
||||||
|
--secondary-foreground: #171717;
|
||||||
|
--muted: #2e2e2e; /* 弱化块 */
|
||||||
|
--muted-foreground: #a1a1a1;
|
||||||
|
--accent: #383838; /* 悬停/高亮块 */
|
||||||
|
--accent-foreground: #e4e4e4;
|
||||||
|
--destructive: #ff544c; /* 危险 */
|
||||||
|
--border: #404040; /* 边框(灰阶递进最高一级) */
|
||||||
|
--input: #4d4d4d;
|
||||||
|
--ring: #e4e4e4;
|
||||||
|
--radius: 0px; /* ← 全站直角的核心 */
|
||||||
|
|
||||||
|
/* 灰阶递进:#171717 → #1f1f1f → #242424 → #2e2e2e → #383838 */
|
||||||
|
--gray-1: #171717;
|
||||||
|
--gray-2: #1f1f1f;
|
||||||
|
--gray-3: #242424;
|
||||||
|
--gray-4: #2e2e2e;
|
||||||
|
--gray-5: #383838;
|
||||||
|
|
||||||
|
/* 点缀色 */
|
||||||
|
--live: #ff2d55; /* 直播在线(三层扩散光环) */
|
||||||
|
--bili: #fb7299; /* B站(闪烁光标) */
|
||||||
|
|
||||||
|
--success: #46c881;
|
||||||
|
--warning: #e8a33d;
|
||||||
|
--info: #5aa7f0;
|
||||||
|
|
||||||
|
/* 等宽字体栈(font-mono) */
|
||||||
|
--font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',
|
||||||
|
Menlo, Consolas, 'Courier New', monospace;
|
||||||
|
/* 系统无衬线 */
|
||||||
|
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC',
|
||||||
|
'Hiragino Sans GB', 'Microsoft YaHei', Roboto, sans-serif;
|
||||||
|
|
||||||
|
--transition: 75ms ease; /* 全站微交互统一 75ms */
|
||||||
|
--topbar-h: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 基础 Reset ============================== */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
line-height: 1.6;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条(深色适配) */
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: var(--gray-1);
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--gray-5);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 布局模式 ============================== */
|
||||||
|
|
||||||
|
/* 顶栏:毛玻璃,固定 */
|
||||||
|
.shell-topbar {
|
||||||
|
position: fixed;
|
||||||
|
inset-inline: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 40;
|
||||||
|
height: var(--topbar-h);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
padding-inline: 1.25rem;
|
||||||
|
border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--background) 80%, transparent);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
}
|
||||||
|
.shell-topbar .brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.shell-topbar .brand img {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
.shell-topbar nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主体:居中大留白 */
|
||||||
|
.shell-main {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: calc(var(--topbar-h) + 3rem) 1.25rem 4rem;
|
||||||
|
}
|
||||||
|
.shell-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 72rem; /* max-w-6xl */
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
.shell-narrow {
|
||||||
|
max-width: 36rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面标题 */
|
||||||
|
.shell-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.shell-header h1 {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
}
|
||||||
|
.shell-header .sub {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片网格:用背景色当分隔线,卡片零间距,hover 反色区分 */
|
||||||
|
.shell-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1px;
|
||||||
|
border-block: 1px solid var(--border);
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.shell-grid {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shell-grid > * {
|
||||||
|
background: var(--background);
|
||||||
|
transition: background var(--transition);
|
||||||
|
}
|
||||||
|
.shell-grid > *:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 组件:Button ============================== */
|
||||||
|
/* 直角,font-mono,h-9 px-4,75ms,hover 前景/背景反转 */
|
||||||
|
.shell-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
height: 2.25rem; /* h-9 */
|
||||||
|
padding-inline: 1rem; /* px-4 */
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem; /* text-sm */
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--foreground);
|
||||||
|
transition: background-color var(--transition), color var(--transition),
|
||||||
|
border-color var(--transition), opacity var(--transition);
|
||||||
|
}
|
||||||
|
.shell-btn:hover {
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
.shell-btn:focus-visible {
|
||||||
|
outline: 2px solid var(--ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.shell-btn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default:实心白 */
|
||||||
|
.shell-btn--default {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
}
|
||||||
|
.shell-btn--default:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
/* outline:透明 + 边框 */
|
||||||
|
.shell-btn--outline {
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
.shell-btn--outline:hover {
|
||||||
|
border-color: var(--foreground);
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
/* ghost:无边框 */
|
||||||
|
.shell-btn--ghost:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
/* danger */
|
||||||
|
.shell-btn--danger {
|
||||||
|
border-color: var(--destructive);
|
||||||
|
color: var(--destructive);
|
||||||
|
}
|
||||||
|
.shell-btn--danger:hover {
|
||||||
|
background: var(--destructive);
|
||||||
|
color: var(--background);
|
||||||
|
border-color: var(--destructive);
|
||||||
|
}
|
||||||
|
/* success */
|
||||||
|
.shell-btn--success {
|
||||||
|
border-color: var(--success);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
.shell-btn--success:hover {
|
||||||
|
background: var(--success);
|
||||||
|
color: var(--background);
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* icon 尺寸变体(宽高互换) */
|
||||||
|
.shell-btn--icon {
|
||||||
|
width: 2.25rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.shell-btn--icon-xs {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
.shell-btn--icon-sm {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
.shell-btn--icon-lg {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
/* sm */
|
||||||
|
.shell-btn--sm {
|
||||||
|
height: 1.875rem;
|
||||||
|
padding-inline: 0.75rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
/* block */
|
||||||
|
.shell-btn--block {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 旧式 .btn / .btn-sm 兼容(保留颜色语义,直角化) */
|
||||||
|
.btn,
|
||||||
|
.btn-sm {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity var(--transition), background-color var(--transition),
|
||||||
|
color var(--transition);
|
||||||
|
}
|
||||||
|
.btn:hover,
|
||||||
|
.btn-sm:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
.btn:disabled,
|
||||||
|
.btn-sm:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.btn-sm {
|
||||||
|
height: 1.875rem;
|
||||||
|
padding-inline: 0.75rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
.btn-sm.green { background: var(--success); color: var(--gray-1); }
|
||||||
|
.btn-sm.red { background: var(--destructive); color: var(--gray-1); }
|
||||||
|
.btn-sm.gray { background: var(--secondary); color: var(--secondary-foreground); }
|
||||||
|
.btn-secondary { background: var(--gray-4); color: var(--foreground); }
|
||||||
|
.btn-success { background: var(--success); color: var(--gray-1); }
|
||||||
|
|
||||||
|
/* ============================== 组件:Badge ============================== */
|
||||||
|
.shell-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.625rem; /* text-[10px] */
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
padding: 1px 0.375rem; /* px-1.5 py-px */
|
||||||
|
line-height: 1.4;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 9999px; /* rounded-full:唯一全圆保留 */
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
.shell-badge--primary {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
.shell-badge--success {
|
||||||
|
background: var(--success);
|
||||||
|
color: var(--gray-1);
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
.shell-badge--danger {
|
||||||
|
background: var(--destructive);
|
||||||
|
color: var(--gray-1);
|
||||||
|
border-color: var(--destructive);
|
||||||
|
}
|
||||||
|
.shell-badge--warn {
|
||||||
|
background: var(--warning);
|
||||||
|
color: var(--gray-1);
|
||||||
|
border-color: var(--warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 组件:Card ============================== */
|
||||||
|
/* 直角细边框,[--card-spacing] 控制间距 */
|
||||||
|
.shell-card {
|
||||||
|
--card-spacing: 1.25rem;
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: var(--card-spacing);
|
||||||
|
}
|
||||||
|
.shell-card--popover {
|
||||||
|
background: var(--popover);
|
||||||
|
}
|
||||||
|
.shell-card--muted {
|
||||||
|
background: var(--muted);
|
||||||
|
}
|
||||||
|
.shell-card h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.shell-card .desc {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 组件:输入控件 ============================== */
|
||||||
|
.shell-input,
|
||||||
|
.shell-textarea,
|
||||||
|
.shell-select,
|
||||||
|
input[type='text'],
|
||||||
|
input[type='number'],
|
||||||
|
input[type='tel'],
|
||||||
|
input[type='password'],
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
background: var(--gray-1);
|
||||||
|
color: var(--foreground);
|
||||||
|
border: 1px solid var(--input);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color var(--transition), background-color var(--transition);
|
||||||
|
}
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: var(--ring);
|
||||||
|
background: var(--gray-2);
|
||||||
|
}
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
input:disabled,
|
||||||
|
textarea:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-label,
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标签内的小 tag(必填/可选) */
|
||||||
|
label .tag,
|
||||||
|
label .tag.r,
|
||||||
|
label .tag.o,
|
||||||
|
label .tag.g {
|
||||||
|
display: inline-block;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.625rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 0 0.375rem;
|
||||||
|
margin-left: 0.375rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
label .tag.r { border-color: var(--destructive); color: var(--destructive); }
|
||||||
|
label .tag.o { border-color: var(--warning); color: var(--warning); }
|
||||||
|
label .tag.g { border-color: var(--success); color: var(--success); }
|
||||||
|
|
||||||
|
/* Checkbox(shadcn 风格,直角) */
|
||||||
|
input[type='checkbox'],
|
||||||
|
input[type='radio'] {
|
||||||
|
accent-color: var(--primary);
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.checkbox-group label {
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 组件:Skeleton / Spinner ============================== */
|
||||||
|
.shell-skeleton {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--gray-4);
|
||||||
|
}
|
||||||
|
.shell-skeleton::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
color-mix(in srgb, var(--gray-5) 70%, transparent),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
animation: shell-scan 1.5s ease-in-out infinite;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 三方块依次呼吸加载 */
|
||||||
|
.shell-spinner {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 0.375rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.shell-spinner-dot {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
background: var(--foreground);
|
||||||
|
animation: shell-spinner-dot 0.9s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.shell-spinner-dot:nth-child(1) { animation-delay: 0s; }
|
||||||
|
.shell-spinner-dot:nth-child(2) { animation-delay: 0.3s; }
|
||||||
|
.shell-spinner-dot:nth-child(3) { animation-delay: 0.6s; }
|
||||||
|
|
||||||
|
/* ============================== 动画系统(shell- 前缀) ============================== */
|
||||||
|
@keyframes shell-blink {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0; }
|
||||||
|
}
|
||||||
|
@keyframes shell-fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
@keyframes shell-fade-out {
|
||||||
|
from { opacity: 1; }
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
@keyframes shell-slide-in-left {
|
||||||
|
from { transform: translateX(-100%); }
|
||||||
|
to { transform: translateX(0); }
|
||||||
|
}
|
||||||
|
@keyframes shell-slide-out-left {
|
||||||
|
from { transform: translateX(0); }
|
||||||
|
to { transform: translateX(-100%); }
|
||||||
|
}
|
||||||
|
@keyframes shell-slide-in-right {
|
||||||
|
from { transform: translateX(100%); }
|
||||||
|
to { transform: translateX(0); }
|
||||||
|
}
|
||||||
|
@keyframes shell-slide-out-right {
|
||||||
|
from { transform: translateX(0); }
|
||||||
|
to { transform: translateX(100%); }
|
||||||
|
}
|
||||||
|
@keyframes shell-slide-in-up {
|
||||||
|
from { transform: translateY(100%); }
|
||||||
|
to { transform: translateY(0); }
|
||||||
|
}
|
||||||
|
@keyframes shell-backdrop-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
@keyframes shell-backdrop-out {
|
||||||
|
from { opacity: 1; }
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
@keyframes shell-scan {
|
||||||
|
to { transform: translateX(100%); }
|
||||||
|
}
|
||||||
|
@keyframes shell-spinner-dot {
|
||||||
|
0%, 100% { opacity: 0.25; transform: scale(0.8); }
|
||||||
|
50% { opacity: 1; transform: scale(1); }
|
||||||
|
}
|
||||||
|
/* 直播在线:三层扩散光环 */
|
||||||
|
@keyframes live-pulse {
|
||||||
|
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--live) 60%, transparent); }
|
||||||
|
100% { box-shadow: 0 0 0 0.75rem transparent; }
|
||||||
|
}
|
||||||
|
@keyframes live-ring {
|
||||||
|
0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--live) 55%, transparent),
|
||||||
|
0 0 0 0.5rem color-mix(in srgb, var(--live) 25%, transparent),
|
||||||
|
0 0 0 1rem color-mix(in srgb, var(--live) 10%, transparent); }
|
||||||
|
70% { box-shadow: 0 0 0 0.5rem color-mix(in srgb, var(--live) 25%, transparent),
|
||||||
|
0 0 0 1rem color-mix(in srgb, var(--live) 10%, transparent),
|
||||||
|
0 0 0 1.5rem transparent; }
|
||||||
|
}
|
||||||
|
/* B站:闪烁光标 */
|
||||||
|
@keyframes bili-blink {
|
||||||
|
0%, 49% { opacity: 1; }
|
||||||
|
50%, 100% { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-anim-blink { animation: shell-blink 1.2s steps(2, start) infinite; }
|
||||||
|
.shell-anim-fade-in { animation: shell-fade-in 0.2s ease both; }
|
||||||
|
|
||||||
|
.shell-live-dot {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: var(--live);
|
||||||
|
animation: live-ring 2s ease-out infinite;
|
||||||
|
}
|
||||||
|
.shell-bili-caret::after {
|
||||||
|
content: '▍';
|
||||||
|
color: var(--bili);
|
||||||
|
animation: bili-blink 1s steps(2, start) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 尊重系统减弱动效 */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 状态结果块 ============================== */
|
||||||
|
.shell-result {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.shell-result--success { border-color: var(--success); color: var(--success); background: color-mix(in srgb, var(--success) 8%, transparent); }
|
||||||
|
.shell-result--error { border-color: var(--destructive); color: var(--destructive); background: color-mix(in srgb, var(--destructive) 8%, transparent); }
|
||||||
|
.shell-result--info { border-color: var(--info); color: var(--info); background: color-mix(in srgb, var(--info) 8%, transparent); }
|
||||||
|
.shell-result--warning { border-color: var(--warning); color: var(--warning); background: color-mix(in srgb, var(--warning) 8%, transparent); }
|
||||||
|
|
||||||
|
/* 旧 .result 兼容 */
|
||||||
|
.result {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.result.success { border-color: var(--success); color: var(--success); background: color-mix(in srgb, var(--success) 8%, transparent); }
|
||||||
|
.result.error { border-color: var(--destructive); color: var(--destructive); background: color-mix(in srgb, var(--destructive) 8%, transparent); }
|
||||||
|
.result.info { border-color: var(--info); color: var(--info); background: color-mix(in srgb, var(--info) 8%, transparent); }
|
||||||
|
.result.warning { border-color: var(--warning); color: var(--warning); background: color-mix(in srgb, var(--warning) 8%, transparent); }
|
||||||
|
|
||||||
|
/* 步骤 tag(旧式,兼容) */
|
||||||
|
.tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.125rem 0.625rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
.tag.ok { border-color: var(--success); color: var(--success); background: color-mix(in srgb, var(--success) 8%, transparent); }
|
||||||
|
.tag.no { border-color: var(--destructive); color: var(--destructive); background: color-mix(in srgb, var(--destructive) 8%, transparent); }
|
||||||
|
.tag.busy { border-color: var(--warning); color: var(--warning); background: color-mix(in srgb, var(--warning) 8%, transparent); }
|
||||||
|
|
||||||
|
/* ============================== 代码块 ============================== */
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: var(--gray-2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 链接 ============================== */
|
||||||
|
a {
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color var(--transition), border-color var(--transition);
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a.shell-link-muted {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
a.shell-link-muted:hover {
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 页脚 ============================== */
|
||||||
|
.shell-footer {
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 通用工具 ============================== */
|
||||||
|
.shell-flex { display: flex; align-items: center; gap: 0.625rem; }
|
||||||
|
.shell-flex--sb { justify-content: space-between; }
|
||||||
|
.shell-flex--wrap { flex-wrap: wrap; }
|
||||||
|
.shell-flex--center { justify-content: center; }
|
||||||
|
.shell-mt-1 { margin-top: 0.25rem; }
|
||||||
|
.shell-mt-2 { margin-top: 0.5rem; }
|
||||||
|
.shell-mt-3 { margin-top: 0.75rem; }
|
||||||
|
.shell-mt-4 { margin-top: 1rem; }
|
||||||
|
.shell-mt-6 { margin-top: 1.5rem; }
|
||||||
|
.shell-mb-2 { margin-bottom: 0.5rem; }
|
||||||
|
.shell-mb-4 { margin-bottom: 1rem; }
|
||||||
|
.shell-gap-2 { gap: 0.5rem; }
|
||||||
|
.shell-gap-3 { gap: 0.75rem; }
|
||||||
|
.shell-hidden { display: none; }
|
||||||
|
.shell-mono { font-family: var(--font-mono); }
|
||||||
|
.shell-text-sm { font-size: 0.8125rem; }
|
||||||
|
.shell-text-xs { font-size: 0.75rem; }
|
||||||
|
.shell-muted { color: var(--muted-foreground); }
|
||||||
|
.shell-fade { transition: opacity var(--transition); }
|
||||||
|
|
||||||
|
/* 表单行 */
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.row .form-group {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.quick-fill {
|
||||||
|
margin-top: 0.375rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
.quick-fill span {
|
||||||
|
color: var(--info);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
.quick-fill span:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 表格 ============================== */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
padding: 0.625rem 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
table th {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
background: var(--gray-2);
|
||||||
|
}
|
||||||
|
table td input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== Tabs(直角) ============================== */
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.tab-btn {
|
||||||
|
padding: 0.625rem 1rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color var(--transition), border-color var(--transition);
|
||||||
|
}
|
||||||
|
.tab-btn:hover {
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
.tab-btn.active {
|
||||||
|
color: var(--foreground);
|
||||||
|
border-bottom-color: var(--foreground);
|
||||||
|
}
|
||||||
|
.tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.tab-content.active {
|
||||||
|
display: block;
|
||||||
|
animation: shell-fade-in 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================== 音频 / 进度条 ============================== */
|
||||||
|
audio {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
audio::-webkit-media-controls-panel {
|
||||||
|
background-color: var(--gray-2);
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
height: 0.375rem;
|
||||||
|
background: var(--gray-4);
|
||||||
|
border-radius: 2px; /* rounded-[2px]:唯一 2px 微圆角 */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.progress-bar .fill {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--foreground);
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
.progress-item.success .fill { background: var(--success); }
|
||||||
|
.progress-item.error .fill { background: var(--destructive); }
|
||||||
|
.progress-item.error .status { color: var(--destructive); }
|
||||||
|
|
||||||
|
/* 头像(rounded-full 保留) */
|
||||||
|
.avatar,
|
||||||
|
.shell-avatar {
|
||||||
|
border-radius: 9999px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toast(深色) */
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1.5rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(6.25rem);
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--gray-1);
|
||||||
|
background: var(--foreground);
|
||||||
|
box-shadow: 0 0.25rem 1.5rem rgba(0, 0, 0, 0.5);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 1000;
|
||||||
|
max-width: 90%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.toast.show {
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.toast.success { background: var(--success); }
|
||||||
|
.toast.error { background: var(--destructive); }
|
||||||
|
.toast.info { background: var(--info); color: var(--gray-1); }
|
||||||
|
|
||||||
|
/* ============================== 响应式 ============================== */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.shell-main {
|
||||||
|
padding-top: calc(var(--topbar-h) + 1.5rem);
|
||||||
|
}
|
||||||
|
.shell-header h1 {
|
||||||
|
font-size: 1.375rem;
|
||||||
|
}
|
||||||
|
.shell-topbar {
|
||||||
|
padding-inline: 0.875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
534
public/ugc.html
534
public/ugc.html
@ -6,472 +6,204 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="referrer" content="no-referrer" />
|
<meta name="referrer" content="no-referrer" />
|
||||||
<link rel="icon" href="docs/netease.png" />
|
<link rel="icon" href="docs/netease.png" />
|
||||||
<meta name="referrer" content="no-referrer" />
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<link rel="icon" href="docs/netease.png" />
|
|
||||||
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 40px auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Section ---- */
|
|
||||||
.section {
|
.section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 1.5rem;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 1.25rem;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section:last-of-type {
|
.section:last-of-type {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 16px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Form ---- */
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 1px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.r {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.o {
|
|
||||||
background: #fef3c7;
|
|
||||||
color: #92400e;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.g {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="number"],
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
font-family: inherit;
|
|
||||||
transition: border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus,
|
|
||||||
input[type="number"]:focus,
|
|
||||||
textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row .form-group {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill {
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span {
|
|
||||||
color: #0066cc;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm {
|
|
||||||
display: inline-block;
|
|
||||||
width: auto;
|
|
||||||
padding: 6px 16px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.green {
|
|
||||||
background: #059669;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.green:hover {
|
|
||||||
background: #047857;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.green:disabled {
|
|
||||||
background: #a7f3d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.red {
|
|
||||||
background: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.red:hover {
|
|
||||||
background: #b91c1c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.red:disabled {
|
|
||||||
background: #fca5a5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.gray {
|
|
||||||
background: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.gray:hover {
|
|
||||||
background: #4b5563;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.gray:disabled {
|
|
||||||
background: #d1d5db;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 16px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.success {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.error {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.info {
|
|
||||||
background: #e0f2fe;
|
|
||||||
color: #0369a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 24px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a {
|
|
||||||
color: #666;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Info Card ---- */
|
|
||||||
.info-card {
|
.info-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 0.75rem;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9fafb;
|
background: var(--gray-2);
|
||||||
border-radius: 8px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card .avatar {
|
.info-card .avatar {
|
||||||
width: 50px;
|
width: 3.125rem;
|
||||||
height: 50px;
|
height: 3.125rem;
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
background: #e5e7eb;
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card .info-text {
|
.info-card .info-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
.info-card .btn-sm { flex-shrink: 0; }
|
||||||
.info-card .btn-sm {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#userPoint {
|
#userPoint {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: var(--success);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Song Review ---- */
|
/* ---- Song Review ---- */
|
||||||
.song-review {
|
.song-review {
|
||||||
margin-top: 16px;
|
margin-top: 1rem;
|
||||||
padding: 16px;
|
padding: 1rem;
|
||||||
background: #f9fafb;
|
background: var(--gray-2);
|
||||||
border-radius: 8px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-review .cover {
|
.song-review .cover {
|
||||||
width: 100px;
|
width: 6.25rem;
|
||||||
height: 100px;
|
height: 6.25rem;
|
||||||
border-radius: 6px;
|
border-radius: 2px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
background: #e5e7eb;
|
background: var(--gray-4);
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-review .song-title {
|
.song-review .song-title {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
.song-review audio { width: 100%; margin-bottom: 0.5rem; }
|
||||||
.song-review audio {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-review .song-tag {
|
.song-review .song-tag {
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-review .btn-group {
|
.song-review .btn-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0.625rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-review .lyric {
|
.song-review .lyric {
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
color: #555;
|
color: var(--muted-foreground);
|
||||||
background: #fff;
|
background: var(--gray-1);
|
||||||
padding: 10px 12px;
|
padding: 0.625rem 0.75rem;
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
max-height: 160px;
|
max-height: 10rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
font-family: monospace;
|
font-family: var(--font-mono);
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-text {
|
.status-text { font-size: 0.875rem; color: var(--muted-foreground); }
|
||||||
font-size: 14px;
|
.footer-link {
|
||||||
color: #888;
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
}
|
}
|
||||||
|
.footer-link a { color: var(--muted-foreground); }
|
||||||
|
.footer-link a:hover { color: var(--foreground); text-decoration: underline; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>云小编任务中心</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">云小编任务中心</a>
|
||||||
<p class="subtitle">管理云音乐审核任务、签到与会员权益领取</p>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">登录</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
|
<main class="shell-main">
|
||||||
|
<div class="shell-container shell-narrow">
|
||||||
|
<header class="shell-header">
|
||||||
|
<h1>云小编任务中心</h1>
|
||||||
|
<p class="sub">管理云音乐审核任务、签到与会员权益领取</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<!-- Cookie -->
|
<section class="shell-card">
|
||||||
<div class="form-group">
|
<!-- Cookie -->
|
||||||
<label for="cookie">Cookie <span class="tag o">可选</span></label>
|
<div class="form-group">
|
||||||
<textarea id="cookie" placeholder="留空则默认读取本地存储的登录态" rows="2"></textarea>
|
<label for="cookie">Cookie <span class="tag o">可选</span></label>
|
||||||
<div class="quick-fill">
|
<textarea id="cookie" placeholder="留空则默认读取本地存储的登录态" rows="2"></textarea>
|
||||||
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
<div class="quick-fill">
|
||||||
<span onclick="clearCookie()">清除 Cookie</span>
|
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
||||||
<span onclick="saveCookie()">保存 Cookie</span>
|
<span onclick="clearCookie()">清除 Cookie</span>
|
||||||
<span onclick="loadAll()">一键载入</span>
|
<span onclick="saveCookie()">保存 Cookie</span>
|
||||||
</div>
|
<span onclick="loadAll()">一键载入</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-header">
|
|
||||||
<span class="section-title">用户信息</span>
|
|
||||||
<button class="btn-sm" id="userGet">获取用户详情</button>
|
|
||||||
</div>
|
|
||||||
<div class="info-card" id="user">
|
|
||||||
<img src="docs/netease.png" alt="头像" class="avatar" id="userAvatar" />
|
|
||||||
<span class="info-text" id="userName">未登录</span>
|
|
||||||
<span id="userPoint">0</span>
|
|
||||||
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-header">
|
|
||||||
<span class="section-title">会员领取</span>
|
|
||||||
<button class="btn-sm" id="vipGet">获取会员详情</button>
|
|
||||||
</div>
|
|
||||||
<div class="info-card" id="vip">
|
|
||||||
<span class="info-text" id="vipStatus">未载入</span>
|
|
||||||
<button class="btn-sm" id="vipCollect" disabled>领取</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-header">
|
|
||||||
<span class="section-title">审核任务</span>
|
|
||||||
<button class="btn-sm" id="taskGet">获取任务详情</button>
|
|
||||||
</div>
|
|
||||||
<div class="info-card" id="task">
|
|
||||||
<span class="info-text" id="taskInfo">[<span id="taskCount">0</span>/<span id="taskGoal">0</span>] 审核歌曲赚贡献分 (+<span id="taskPoint">0</span>)</span>
|
|
||||||
<button class="btn-sm" id="taskStart" disabled>去完成</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Song Review -->
|
|
||||||
<div class="song-review" id="song" style="display: none;">
|
|
||||||
<img alt="封面" class="cover" id="songCover" />
|
|
||||||
<div class="song-title" id="songTitle">未载入</div>
|
|
||||||
<audio controls id="songAudio"></audio>
|
|
||||||
<div class="song-tag" id="songTag"></div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn-sm green" id="songApprove" disabled>同意</button>
|
|
||||||
<button class="btn-sm red" id="songReject" disabled>否决</button>
|
|
||||||
<button class="btn-sm gray" id="songSkip" disabled>跳过 (不计次数)</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="lyric" id="songLyric">[00:00.00] 暂无歌词</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="result" class="result" style="display: none;"></div>
|
<div class="section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title">用户信息</span>
|
||||||
|
<button class="btn-sm" id="userGet">获取用户详情</button>
|
||||||
|
</div>
|
||||||
|
<div class="info-card" id="user">
|
||||||
|
<img src="docs/netease.png" alt="头像" class="avatar" id="userAvatar" />
|
||||||
|
<span class="info-text" id="userName">未登录</span>
|
||||||
|
<span id="userPoint">0</span>
|
||||||
|
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="footer-link">
|
<div class="section">
|
||||||
<a href="/">返回首页</a>
|
<div class="section-header">
|
||||||
|
<span class="section-title">会员领取</span>
|
||||||
|
<button class="btn-sm" id="vipGet">获取会员详情</button>
|
||||||
|
</div>
|
||||||
|
<div class="info-card" id="vip">
|
||||||
|
<span class="info-text" id="vipStatus">未载入</span>
|
||||||
|
<button class="btn-sm" id="vipCollect" disabled>领取</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title">审核任务</span>
|
||||||
|
<button class="btn-sm" id="taskGet">获取任务详情</button>
|
||||||
|
</div>
|
||||||
|
<div class="info-card" id="task">
|
||||||
|
<span class="info-text" id="taskInfo">[<span id="taskCount">0</span>/<span id="taskGoal">0</span>] 审核歌曲赚贡献分 (+<span id="taskPoint">0</span>)</span>
|
||||||
|
<button class="btn-sm" id="taskStart" disabled>去完成</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Song Review -->
|
||||||
|
<div class="song-review" id="song" style="display: none;">
|
||||||
|
<img alt="封面" class="cover" id="songCover" />
|
||||||
|
<div class="song-title" id="songTitle">未载入</div>
|
||||||
|
<audio controls id="songAudio"></audio>
|
||||||
|
<div class="song-tag" id="songTag"></div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn-sm green" id="songApprove" disabled>同意</button>
|
||||||
|
<button class="btn-sm red" id="songReject" disabled>否决</button>
|
||||||
|
<button class="btn-sm gray" id="songSkip" disabled>跳过 (不计次数)</button>
|
||||||
|
</div>
|
||||||
|
<div class="lyric" id="songLyric">[00:00.00] 暂无歌词</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="result" class="result" style="display: none;"></div>
|
||||||
|
|
||||||
|
<div class="footer-link">
|
||||||
|
<a href="/">返回首页</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
|||||||
@ -6,230 +6,26 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="referrer" content="no-referrer" />
|
<meta name="referrer" content="no-referrer" />
|
||||||
<link rel="icon" href="docs/netease.png" />
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>云小编每日抽奖 - 网易云音乐 API Enhanced</title>
|
<title>云小编每日抽奖 - 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
.shell-header { text-align: center; }
|
||||||
padding: 0;
|
.subtitle { text-align: center; }
|
||||||
box-sizing: border-box;
|
.subtitle b { color: var(--destructive); }
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 520px;
|
|
||||||
margin: 40px auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle b {
|
|
||||||
color: #d63031;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Section ---- */
|
|
||||||
.section {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section:last-of-type {
|
|
||||||
border-bottom: none;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Form Elements ---- */
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 1px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.o {
|
|
||||||
background: #fef3c7;
|
|
||||||
color: #92400e;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="number"],
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
font-family: inherit;
|
|
||||||
transition: border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus,
|
|
||||||
input[type="number"]:focus,
|
|
||||||
textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm {
|
|
||||||
display: inline-block;
|
|
||||||
width: auto;
|
|
||||||
padding: 6px 16px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.gray {
|
|
||||||
background: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.gray:hover {
|
|
||||||
background: #4b5563;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.gray:disabled {
|
|
||||||
background: #d1d5db;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.green {
|
|
||||||
background: #059669;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.green:hover {
|
|
||||||
background: #047857;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-sm.green:disabled {
|
|
||||||
background: #a7f3d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill {
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span {
|
|
||||||
color: #0066cc;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Lottery Display ---- */
|
/* ---- Lottery Display ---- */
|
||||||
.lottery-display {
|
.lottery-display {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 24px 16px;
|
padding: 1.5rem 1rem;
|
||||||
background: #f9fafb;
|
background: var(--gray-2);
|
||||||
border-radius: 8px;
|
border: 1px solid var(--border);
|
||||||
border: 1px solid #eee;
|
margin-bottom: 1rem;
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rolling-display {
|
.rolling-display {
|
||||||
font-size: 2.4rem;
|
font-size: 2.4rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
min-height: 4rem;
|
min-height: 4rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -237,200 +33,155 @@
|
|||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rolling-display.idle {
|
.rolling-display.idle {
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Winner Result ---- */
|
/* ---- Winner Result ---- */
|
||||||
.winner-display {
|
.winner-display {
|
||||||
background: #f0fdf4;
|
background: color-mix(in srgb, var(--success) 8%, var(--gray-2));
|
||||||
border: 1px solid #86efac;
|
border: 1px solid var(--success);
|
||||||
border-radius: 8px;
|
padding: 1.25rem 1rem;
|
||||||
padding: 20px 16px;
|
margin-top: 1rem;
|
||||||
margin-top: 16px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.winner-label {
|
.winner-label {
|
||||||
font-size: 14px;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #065f46;
|
color: var(--success);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.winner-name {
|
.winner-name {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: #dc2626;
|
color: var(--foreground);
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confetti-row {
|
.confetti-row {
|
||||||
margin-top: 8px;
|
margin-top: 0.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confetti-emoji {
|
.confetti-emoji {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
animation: bounce 0.6s ease infinite alternate;
|
animation: bounce 0.6s ease infinite alternate;
|
||||||
}
|
}
|
||||||
|
.confetti-emoji:nth-child(2) { animation-delay: 0.15s; }
|
||||||
.confetti-emoji:nth-child(2) {
|
.confetti-emoji:nth-child(3) { animation-delay: 0.3s; }
|
||||||
animation-delay: 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.confetti-emoji:nth-child(3) {
|
|
||||||
animation-delay: 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bounce {
|
@keyframes bounce {
|
||||||
from {
|
from { transform: translateY(0); }
|
||||||
transform: translateY(0);
|
to { transform: translateY(-8px); }
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: translateY(-8px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Status ---- */
|
/* ---- Status ---- */
|
||||||
.status-bar {
|
.status-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 0.5rem;
|
||||||
padding: 10px 14px;
|
padding: 0.625rem 0.875rem;
|
||||||
background: #f9fafb;
|
background: var(--gray-2);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--border);
|
||||||
margin-top: 16px;
|
margin-top: 1rem;
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot {
|
.status-dot {
|
||||||
width: 8px;
|
width: 0.5rem;
|
||||||
height: 8px;
|
height: 0.5rem;
|
||||||
border-radius: 50%;
|
border-radius: 9999px;
|
||||||
background: #059669;
|
background: var(--success);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
animation: pulse 1.5s ease infinite;
|
animation: pulse 1.5s ease infinite;
|
||||||
}
|
}
|
||||||
|
.status-dot.error { background: var(--destructive); }
|
||||||
.status-dot.error {
|
.status-dot.idle { background: var(--gray-5); animation: none; }
|
||||||
background: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-dot.idle {
|
|
||||||
background: #9ca3af;
|
|
||||||
animation: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
0%,
|
50% { opacity: 0.4; }
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-text {
|
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
.status-text { flex: 1; }
|
||||||
|
|
||||||
/* ---- Footer ---- */
|
/* ---- Footer ---- */
|
||||||
.footer-link {
|
.footer-link {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 24px;
|
margin-top: 1.5rem;
|
||||||
padding-top: 16px;
|
padding-top: 1rem;
|
||||||
border-top: 1px solid #eee;
|
border-top: 1px solid var(--border);
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
}
|
}
|
||||||
|
.footer-link a { color: var(--muted-foreground); }
|
||||||
|
.footer-link a:hover { color: var(--foreground); text-decoration: underline; }
|
||||||
|
|
||||||
.footer-link a {
|
|
||||||
color: #666;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Responsive ---- */
|
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
.container {
|
.rolling-display { font-size: 1.8rem; min-height: 3rem; }
|
||||||
padding: 20px 16px;
|
.winner-name { font-size: 1.5rem; }
|
||||||
margin: 20px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rolling-display {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
min-height: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.winner-name {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>云小编每日抽奖</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">云小编抽奖</a>
|
||||||
<p class="subtitle">每次抽奖消耗 <b>200 个曲库编辑部积分</b>,每日最多可抽奖 3 次</p>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/ugc.html">任务中心</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<!-- Cookie 管理 -->
|
<main class="shell-main">
|
||||||
<div class="form-group">
|
<div class="shell-container shell-narrow" style="max-width:32rem">
|
||||||
<label for="cookieInput">令牌 <span class="tag o">可选</span></label>
|
<header class="shell-header">
|
||||||
<input type="text" id="cookieInput" placeholder="留空则默认读取本地存储的令牌" autocomplete="off" />
|
<h1>云小编每日抽奖</h1>
|
||||||
<div class="quick-fill">
|
<p class="subtitle">每次抽奖消耗 <b>200 个曲库编辑部积分</b>,每日最多可抽奖 3 次</p>
|
||||||
<span onclick="readLocalCookie()">读取本地令牌</span>
|
</header>
|
||||||
<span onclick="clearCookie()">清除令牌</span>
|
|
||||||
<span onclick="saveCookie()">保存令牌</span>
|
<section class="shell-card">
|
||||||
</div>
|
<!-- Cookie 管理 -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cookieInput">令牌 <span class="tag o">可选</span></label>
|
||||||
|
<input type="text" id="cookieInput" placeholder="留空则默认读取本地存储的令牌" autocomplete="off" />
|
||||||
|
<div class="quick-fill">
|
||||||
|
<span onclick="readLocalCookie()">读取本地令牌</span>
|
||||||
|
<span onclick="clearCookie()">清除令牌</span>
|
||||||
|
<span onclick="saveCookie()">保存令牌</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 抽奖区域 -->
|
||||||
|
<div class="section" style="border-bottom:none;padding-bottom:0;margin-bottom:0">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title" style="font-family:var(--font-mono);font-size:1rem;font-weight:600">每日抽奖</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lottery-display">
|
||||||
|
<div class="rolling-display idle" id="rollingDisplay">等待抽奖</div>
|
||||||
|
<div id="resultContainer"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="shell-btn shell-btn--default shell-btn--block" id="lotteryBtn">
|
||||||
|
🎲 开始抽奖
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="status-bar" id="statusBar">
|
||||||
|
<span class="status-dot idle" id="statusDot"></span>
|
||||||
|
<span class="status-text" id="statusText">就绪,点击按钮开始抽奖</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 页脚 -->
|
||||||
|
<div class="footer-link">
|
||||||
|
<a href="/">← 返回首页</a>
|
||||||
|
<span style="margin: 0 0.5rem; color: var(--border);">·</span>
|
||||||
|
<a href="/ugc.html">云小编任务中心</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
<!-- 抽奖区域 -->
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-header">
|
|
||||||
<span class="section-title">每日抽奖</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="lottery-display">
|
|
||||||
<div class="rolling-display idle" id="rollingDisplay">等待抽奖</div>
|
|
||||||
<div id="resultContainer"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn" id="lotteryBtn">
|
|
||||||
🎲 开始抽奖
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="status-bar" id="statusBar">
|
|
||||||
<span class="status-dot idle" id="statusDot"></span>
|
|
||||||
<span class="status-text" id="statusText">就绪,点击按钮开始抽奖</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 页脚 -->
|
|
||||||
<div class="footer-link">
|
|
||||||
<a href="/">← 返回首页</a>
|
|
||||||
<span style="margin: 0 8px; color: #ddd;">·</span>
|
|
||||||
<a href="/ugc.html">云小编任务中心</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
|||||||
@ -3,123 +3,52 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" href="docs/netease.png">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>音乐解灰测试</title>
|
<title>音乐解灰测试</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 15px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
padding: 14px 28px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:disabled {
|
|
||||||
background: #999;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result {
|
|
||||||
margin-top: 24px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
min-height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
margin-top: 4px;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>音乐解灰测试</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">解灰测试</a>
|
||||||
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="form-group">
|
<main class="shell-main">
|
||||||
<label for="songId">音乐 ID</label>
|
<div class="shell-container shell-narrow" style="max-width:50rem">
|
||||||
<input type="number" id="songId" placeholder="请输入音乐ID" />
|
<header class="shell-header">
|
||||||
<div class="hint">例如: 1372188635</div>
|
<h1>音乐解灰测试</h1>
|
||||||
</div>
|
<p class="sub">测试多音源替换匹配,解除灰色歌曲限制</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="form-group">
|
<section class="shell-card">
|
||||||
<label for="sources">音源列表(可选)</label>
|
<div class="form-group">
|
||||||
<input type="text" id="sources" placeholder="请输入音源" />
|
<label for="songId">音乐 ID</label>
|
||||||
<div class="hint">例如: kuwo, kugou, migu</div>
|
<input type="number" id="songId" placeholder="请输入音乐ID" />
|
||||||
</div>
|
<div class="hint">例如: 1372188635</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="testBtn" onclick="testSong()">开始测试</button>
|
<div class="form-group">
|
||||||
|
<label for="sources">音源列表(可选)</label>
|
||||||
|
<input type="text" id="sources" placeholder="请输入音源" />
|
||||||
|
<div class="hint">例如: kuwo, kugou, migu</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="result"></div>
|
<button id="testBtn" class="shell-btn shell-btn--default" onclick="testSong()">开始测试</button>
|
||||||
</div>
|
|
||||||
|
<pre id="result" style="margin-top:1.5rem;min-height:6.25rem"></pre>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function testSong() {
|
async function testSong() {
|
||||||
|
|||||||
@ -3,233 +3,154 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<link rel="stylesheet" href="/static/shell.css" />
|
||||||
<title>播客上传声音</title>
|
<title>播客上传声音</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
body { padding: 1.25rem; }
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 1.5rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
.voice-list { flex: 1; min-width: 300px; }
|
||||||
.voice-list {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.voice-item {
|
.voice-item {
|
||||||
padding: 12px 16px;
|
padding: 0.75rem 1rem;
|
||||||
border: 1px solid #eee;
|
border: 1px solid var(--border);
|
||||||
border-radius: 6px;
|
margin-bottom: 0.5rem;
|
||||||
margin-bottom: 8px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all var(--transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-item:hover {
|
.voice-item:hover {
|
||||||
border-color: #333;
|
border-color: var(--foreground);
|
||||||
background: #f9f9f9;
|
background: var(--gray-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-item.active {
|
.voice-item.active {
|
||||||
border-color: #333;
|
border-color: var(--foreground);
|
||||||
background: #f0f0f0;
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-header {
|
.voice-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-cover {
|
.voice-cover {
|
||||||
width: 50px;
|
width: 3.125rem;
|
||||||
height: 50px;
|
height: 3.125rem;
|
||||||
border-radius: 4px;
|
border-radius: 2px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
background: var(--gray-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-name {
|
.voice-name {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-tracks {
|
.voice-tracks {
|
||||||
margin-top: 8px;
|
margin-top: 0.5rem;
|
||||||
padding-left: 62px;
|
padding-left: 3.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.voice-track {
|
.voice-track {
|
||||||
font-size: 13px;
|
font-size: 0.8125rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
padding: 4px 0;
|
padding: 0.25rem 0;
|
||||||
}
|
}
|
||||||
|
.upload-section { flex: 1; min-width: 300px; }
|
||||||
.upload-section {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"], input[type="file"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 40px 20px;
|
padding: 2.5rem 1.25rem;
|
||||||
color: #999;
|
color: var(--muted-foreground);
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px;
|
padding: 1.25rem;
|
||||||
color: #666;
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
.section-h3 {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header class="shell-topbar">
|
||||||
<h1>播客上传声音</h1>
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">播客上传声音</a>
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
|
<nav>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">登录</a>
|
||||||
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="content">
|
<main class="shell-main">
|
||||||
<div class="voice-list">
|
<div class="shell-container shell-narrow" style="max-width:56rem">
|
||||||
<h3 style="font-size: 16px; font-weight: 600; color: #333; margin-bottom: 16px;">选择播客列表</h3>
|
<header class="shell-header">
|
||||||
<div v-if="loading" class="loading">加载中...</div>
|
<h1>播客上传声音</h1>
|
||||||
<div v-else-if="voicelist.length > 0">
|
<p class="sub">上传音频到你的播客列表</p>
|
||||||
<div
|
</header>
|
||||||
v-for="(item, index) in voicelist"
|
|
||||||
:key="index"
|
<section class="shell-card">
|
||||||
@click="currentVoiceIndex = index"
|
<div class="content">
|
||||||
:class="{ active: currentVoiceIndex === index }"
|
<div class="voice-list">
|
||||||
class="voice-item"
|
<h3 class="section-h3">选择播客列表</h3>
|
||||||
>
|
<div v-if="loading" class="loading">加载中...</div>
|
||||||
<div class="voice-header">
|
<div v-else-if="voicelist.length > 0">
|
||||||
<img :src="item.coverUrl" class="voice-cover" alt="cover" />
|
|
||||||
<span class="voice-name">{{ item.voiceListName }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="voice-tracks" v-if="item.voiceListData">
|
|
||||||
<div
|
<div
|
||||||
v-for="(item2, index2) in item.voiceListData"
|
v-for="(item, index) in voicelist"
|
||||||
:key="index2"
|
:key="index"
|
||||||
class="voice-track"
|
@click="currentVoiceIndex = index"
|
||||||
|
:class="{ active: currentVoiceIndex === index }"
|
||||||
|
class="voice-item"
|
||||||
>
|
>
|
||||||
{{ item2.voiceName }}
|
<div class="voice-header">
|
||||||
|
<img :src="item.coverUrl" class="voice-cover" alt="cover" />
|
||||||
|
<span class="voice-name">{{ item.voiceListName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="voice-tracks" v-if="item.voiceListData">
|
||||||
|
<div
|
||||||
|
v-for="(item2, index2) in item.voiceListData"
|
||||||
|
:key="index2"
|
||||||
|
class="voice-track"
|
||||||
|
>
|
||||||
|
{{ item2.voiceName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="empty-state">暂无播客列表</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="upload-section">
|
||||||
|
<h3 class="section-h3">上传声音</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="songName">声音名称</label>
|
||||||
|
<input id="songName" v-model="songName" placeholder="请输入声音名称" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="description">介绍</label>
|
||||||
|
<input id="description" v-model="description" placeholder="请输入介绍" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="songFile">选择声音文件</label>
|
||||||
|
<input id="songFile" type="file" name="songFile" accept="audio/*" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="imgFile">选择声音封面(可选)</label>
|
||||||
|
<input id="imgFile" type="file" name="imgFile" accept="image/*" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="shell-btn shell-btn--default shell-btn--block" @click="submit">上传</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="empty-state">暂无播客列表</div>
|
</section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="upload-section">
|
|
||||||
<h3 style="font-size: 16px; font-weight: 600; color: #333; margin-bottom: 16px;">上传声音</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="songName">声音名称</label>
|
|
||||||
<input id="songName" v-model="songName" placeholder="请输入声音名称" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="description">介绍</label>
|
|
||||||
<input id="description" v-model="description" placeholder="请输入介绍" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="songFile">选择声音文件</label>
|
|
||||||
<input id="songFile" type="file" name="songFile" accept="audio/*" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="imgFile">选择声音封面(可选)</label>
|
|
||||||
<input id="imgFile" type="file" name="imgFile" accept="image/*" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn" @click="submit">上传</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||||
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
||||||
|
|||||||
@ -4,74 +4,57 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<link rel="icon" href="docs/netease.png">
|
||||||
|
<link rel="stylesheet" href="/static/shell.css">
|
||||||
<title>Token 注册器 — 网易云音乐 API Enhanced</title>
|
<title>Token 注册器 — 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
body { padding: 1.25rem; }
|
||||||
--fg: #333;
|
.flex { display: flex; align-items: center; gap: 0.625rem; }
|
||||||
--muted: #666;
|
|
||||||
--border: #ddd;
|
|
||||||
--bg: #f5f5f5;
|
|
||||||
--panel: #ffffff;
|
|
||||||
--accent: #333;
|
|
||||||
}
|
|
||||||
* { box-sizing: border-box; }
|
|
||||||
html, body { height: 100%; }
|
|
||||||
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; }
|
|
||||||
.container { max-width: 720px; margin: 40px auto; padding: 0 20px; }
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.container { margin: 20px auto; padding: 0 16px; }
|
|
||||||
header.site-header h1 { font-size: 22px; }
|
|
||||||
.block { padding: 16px; }
|
|
||||||
}
|
|
||||||
header.site-header { margin-bottom: 24px; }
|
|
||||||
header.site-header h1 { font-size: 24px; font-weight: 600; margin: 0; }
|
|
||||||
.sub { margin-top: 4px; color: var(--muted); font-size: 14px; }
|
|
||||||
.block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); }
|
|
||||||
.block h3 { margin: 0 0 12px; font-size: 15px; font-weight: 600; }
|
|
||||||
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Courier New', monospace; font-size: 13px; max-height: 160px; overflow: auto; }
|
|
||||||
.row { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
|
|
||||||
button { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--border); font-size: 14px; font-weight: 500; cursor: pointer; background: var(--panel); color: var(--fg); transition: all .15s; }
|
|
||||||
button:hover { background: #eee; }
|
|
||||||
button.primary { background: var(--fg); color: var(--panel); border-color: var(--fg); }
|
|
||||||
button.primary:hover { opacity: .85; }
|
|
||||||
button:disabled { opacity: .4; cursor: not-allowed; }
|
|
||||||
.tag { display: inline-block; padding: 2px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; border: 1px solid var(--border); }
|
|
||||||
.tag.ok { background: #e6f7e6; color: #1a7a1a; border-color: #b7e6b7; }
|
|
||||||
.tag.no { background: #ffe6e6; color: #a11; border-color: #f5c6c6; }
|
|
||||||
.tag.busy { background: #fff3cd; color: #856404; border-color: #ffeeba; }
|
|
||||||
.flex { display: flex; align-items: center; gap: 10px; }
|
|
||||||
.flex.sb { justify-content: space-between; }
|
.flex.sb { justify-content: space-between; }
|
||||||
.mt-2 { margin-top: 14px; }
|
.token-display {
|
||||||
footer.site-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; font-size: 13px; }
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
color: var(--success);
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="container">
|
<header class="shell-topbar">
|
||||||
<header class="site-header">
|
<a class="brand" href="/"><img src="docs/netease.png" alt="logo">Token 注册器</a>
|
||||||
<h1>🔑 Token 注册器</h1>
|
<nav>
|
||||||
<p class="sub">从易盾获取反作弊 token 并注册到服务器,供广告等需要 checkToken 的接口使用</p>
|
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/">返回首页</a>
|
||||||
</header>
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="block">
|
<main class="shell-main">
|
||||||
<div class="flex sb">
|
<div class="shell-container shell-narrow" style="max-width:45rem">
|
||||||
<h3>当前 Token</h3>
|
<header class="shell-header">
|
||||||
<span id="statusTag" class="tag no">未注册</span>
|
<h1>🔑 Token 注册器</h1>
|
||||||
</div>
|
<p class="sub">从易盾获取反作弊 token 并注册到服务器,供广告等需要 checkToken 的接口使用</p>
|
||||||
<pre id="tokenDisplay">(空)</pre>
|
</header>
|
||||||
<div class="row">
|
|
||||||
<button id="btnGet" class="primary">获取 Token</button>
|
<section class="shell-card">
|
||||||
<button id="btnRefresh">强制刷新</button>
|
<div class="flex sb" style="margin-bottom:0.625rem">
|
||||||
</div>
|
<h3>当前 Token</h3>
|
||||||
|
<span id="statusTag" class="tag no">未注册</span>
|
||||||
|
</div>
|
||||||
|
<pre id="tokenDisplay">(空)</pre>
|
||||||
|
<div class="shell-flex shell-mt-3">
|
||||||
|
<button id="btnGet" class="shell-btn shell-btn--default">获取 Token</button>
|
||||||
|
<button id="btnRefresh" class="shell-btn shell-btn--outline">强制刷新</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="shell-card shell-mt-6">
|
||||||
|
<h3>服务器响应</h3>
|
||||||
|
<pre id="respDisplay">{}</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="shell-footer">
|
||||||
|
<a href="/">← 返回首页</a>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="block">
|
|
||||||
<h3>服务器响应</h3>
|
|
||||||
<pre id="respDisplay">{}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="site-footer">
|
|
||||||
<a href="/">← 返回首页</a>
|
|
||||||
</footer>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user