2 Commits 09f6ce8163 ... e527993a38

Author SHA1 Message Date
  xiang e527993a38 artlist发布页设计前 1 month ago
  xiang b6656fbdfa 两个音乐播放器 1 month ago
96 changed files with 4735 additions and 1174 deletions
  1. 7 0
      package.json
  2. 162 0
      pnpm-lock.yaml
  3. 9 0
      src/apis/musician.ts
  4. 22 0
      src/apis/upload.ts
  5. 232 0
      src/pages/layout/components/FloatingPlayerBar/index.css
  6. 265 0
      src/pages/layout/components/FloatingPlayerBar/index.less
  7. 339 0
      src/pages/layout/components/FloatingPlayerBar/index.tsx
  8. 154 0
      src/pages/layout/components/FloatingPlayerBar2/index.tsx
  9. 0 0
      src/pages/layout/components/LoginContent/components/AuthCallback.tsx
  10. 0 0
      src/pages/layout/components/LoginContent/components/GiteeLogin.tsx
  11. 112 0
      src/pages/layout/components/LoginContent/index.css
  12. 127 0
      src/pages/layout/components/LoginContent/index.less
  13. 480 0
      src/pages/layout/components/LoginContent/index.tsx
  14. 18 0
      src/pages/layout/components/MusicListPage/index.css
  15. 24 0
      src/pages/layout/components/MusicListPage/index.less
  16. 50 0
      src/pages/layout/components/MusicListPage/index.tsx
  17. 0 0
      src/pages/layout/components/Wy_Header/components/CreatorCenter/index.css
  18. 2 2
      src/pages/layout/components/Wy_Header/components/CreatorCenter/index.tsx
  19. 0 93
      src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.css
  20. 0 103
      src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.less
  21. 56 498
      src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.tsx
  22. 13 4
      src/pages/layout/components/Wy_Header/index.tsx
  23. 12 0
      src/pages/layout/index.css
  24. 6 4
      src/pages/layout/index.tsx
  25. 22 0
      src/pages/layout/pages/LoginPage/index.css
  26. 26 0
      src/pages/layout/pages/LoginPage/index.less
  27. 28 0
      src/pages/layout/pages/LoginPage/index.tsx
  28. 16 0
      src/pages/layout/pages/Search/components/SearchTabs/index.css
  29. 30 0
      src/pages/layout/pages/Search/components/SearchTabs/index.tsx
  30. 48 0
      src/pages/layout/pages/Search/index.css
  31. 53 0
      src/pages/layout/pages/Search/index.tsx
  32. 106 0
      src/pages/layout/pages/SongDetail/components/SongDetailPage.css
  33. 125 0
      src/pages/layout/pages/SongDetail/components/SongDetailPage.less
  34. 95 0
      src/pages/layout/pages/SongDetail/components/SongDetailPage.tsx
  35. 6 0
      src/pages/layout/pages/SongDetail/index.css
  36. 7 0
      src/pages/layout/pages/SongDetail/index.less
  37. 27 0
      src/pages/layout/pages/SongDetail/index.tsx
  38. 0 0
      src/pages/layout/pages/Video/Video_player/index.css
  39. 0 0
      src/pages/layout/pages/Video/Video_player/index.less
  40. 58 0
      src/pages/layout/pages/Video/Video_player/index.tsx
  41. 6 0
      src/pages/layout/pages/Video/index.css
  42. 6 0
      src/pages/layout/pages/Video/index.less
  43. 14 0
      src/pages/layout/pages/Video/index.tsx
  44. 21 0
      src/pages/layout/pages/artistinfo/Albums/Albums.tsx
  45. 23 0
      src/pages/layout/pages/artistinfo/Bio/Bio.tsx
  46. 35 0
      src/pages/layout/pages/artistinfo/HotWorks/HotWorks.tsx
  47. 23 0
      src/pages/layout/pages/artistinfo/MVs/MVs.tsx
  48. 52 0
      src/pages/layout/pages/artistinfo/index.css
  49. 62 0
      src/pages/layout/pages/artistinfo/index.less
  50. 63 0
      src/pages/layout/pages/artistinfo/index.tsx
  51. 4 0
      src/pages/layout/pages/find/album/index.css
  52. 4 0
      src/pages/layout/pages/find/album/index.less
  53. 4 3
      src/pages/layout/pages/find/album/index.tsx
  54. 0 12
      src/pages/layout/pages/find/playlist/index.css
  55. 0 12
      src/pages/layout/pages/find/playlist/index.less
  56. 0 12
      src/pages/layout/pages/find/playlist/index.tsx
  57. 18 0
      src/pages/layout/pages/find/rank/compomemts/RankPlaylist/index.tsx
  58. 2 0
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment/index.css
  59. 2 0
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment/index.less
  60. 1 1
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment_list/indes.css
  61. 1 1
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment_list/indes.less
  62. 3 1
      src/pages/layout/pages/find/rank/index.css
  63. 3 1
      src/pages/layout/pages/find/rank/index.less
  64. 6 8
      src/pages/layout/pages/find/rank/index.tsx
  65. 10 0
      src/pages/layout/pages/musician/ApplyMusicianPage/index.css
  66. 11 0
      src/pages/layout/pages/musician/ApplyMusicianPage/index.less
  67. 421 0
      src/pages/layout/pages/musician/ApplyMusicianPage/index.tsx
  68. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/Home/index.css
  69. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/Home/index.less
  70. 5 0
      src/pages/layout/pages/musician/MusicianDashboardPage/Home/index.tsx
  71. 4 0
      src/pages/layout/pages/musician/MusicianDashboardPage/components/artInfo/index.css
  72. 4 0
      src/pages/layout/pages/musician/MusicianDashboardPage/components/artInfo/index.less
  73. 13 0
      src/pages/layout/pages/musician/MusicianDashboardPage/components/artInfo/index.tsx
  74. 13 0
      src/pages/layout/pages/musician/MusicianDashboardPage/index.css
  75. 15 0
      src/pages/layout/pages/musician/MusicianDashboardPage/index.less
  76. 111 0
      src/pages/layout/pages/musician/MusicianDashboardPage/index.tsx
  77. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements/index.css
  78. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements/index.less
  79. 5 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements/index.tsx
  80. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics/index.css
  81. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics/index.less
  82. 5 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics/index.tsx
  83. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs/index.css
  84. 0 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs/index.less
  85. 5 0
      src/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs/index.tsx
  86. 70 3
      src/pages/layout/pages/musician/index.css
  87. 84 6
      src/pages/layout/pages/musician/index.less
  88. 126 384
      src/pages/layout/pages/musician/index.tsx
  89. 6 0
      src/pages/layout/pages/playlist/index.css
  90. 6 0
      src/pages/layout/pages/playlist/index.less
  91. 10 0
      src/pages/layout/pages/playlist/index.tsx
  92. 206 0
      src/pages/layout/pages/testPage/index.css
  93. 239 0
      src/pages/layout/pages/testPage/index.less
  94. 185 0
      src/pages/layout/pages/testPage/index.tsx
  95. 63 3
      src/router/index.tsx
  96. 28 23
      src/utils/request.ts

+ 7 - 0
package.json

@@ -11,19 +11,26 @@
   },
   "dependencies": {
     "@ant-design/icons": "~5.6.1",
+    "@types/dplayer": "^1.25.5",
     "antd": "^5.26.1",
     "axios": "^1.10.0",
+    "classnames": "^2.5.1",
+    "dplayer": "^1.27.1",
     "normalize.css": "^8.0.1",
     "react": "^19.1.0",
+    "react-audio-player": "^0.17.0",
     "react-dom": "^19.1.0",
+    "react-h5-audio-player": "^3.10.1",
     "react-router-dom": "^7.6.2",
     "zustand": "^5.0.5"
   },
   "devDependencies": {
     "@eslint/js": "^9.25.0",
+    "@types/antd": "^1.0.4",
     "@types/node": "^24.0.3",
     "@types/react": "^19.1.2",
     "@types/react-dom": "^19.1.2",
+    "@types/react-router-dom": "^5.3.3",
     "@vitejs/plugin-react": "^4.4.1",
     "eslint": "^9.25.0",
     "eslint-plugin-react-hooks": "^5.2.0",

+ 162 - 0
pnpm-lock.yaml

@@ -11,21 +11,36 @@ importers:
       '@ant-design/icons':
         specifier: ~5.6.1
         version: 5.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@types/dplayer':
+        specifier: ^1.25.5
+        version: 1.25.5
       antd:
         specifier: ^5.26.1
         version: 5.26.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
       axios:
         specifier: ^1.10.0
         version: 1.10.0
+      classnames:
+        specifier: ^2.5.1
+        version: 2.5.1
+      dplayer:
+        specifier: ^1.27.1
+        version: 1.27.1
       normalize.css:
         specifier: ^8.0.1
         version: 8.0.1
       react:
         specifier: ^19.1.0
         version: 19.1.0
+      react-audio-player:
+        specifier: ^0.17.0
+        version: 0.17.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
       react-dom:
         specifier: ^19.1.0
         version: 19.1.0(react@19.1.0)
+      react-h5-audio-player:
+        specifier: ^3.10.1
+        version: 3.10.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
       react-router-dom:
         specifier: ^7.6.2
         version: 7.6.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -36,6 +51,9 @@ importers:
       '@eslint/js':
         specifier: ^9.25.0
         version: 9.29.0
+      '@types/antd':
+        specifier: ^1.0.4
+        version: 1.0.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
       '@types/node':
         specifier: ^24.0.3
         version: 24.0.3
@@ -45,6 +63,9 @@ importers:
       '@types/react-dom':
         specifier: ^19.1.2
         version: 19.1.6(@types/react@19.1.8)
+      '@types/react-router-dom':
+        specifier: ^5.3.3
+        version: 5.3.3
       '@vitejs/plugin-react':
         specifier: ^4.4.1
         version: 4.5.2(vite@6.3.5(@types/node@24.0.3))
@@ -411,6 +432,14 @@ packages:
     resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
     engines: {node: '>=18.18'}
 
+  '@iconify/react@5.2.1':
+    resolution: {integrity: sha512-37GDR3fYDZmnmUn9RagyaX+zca24jfVOMY8E1IXTqJuE8pxNtN51KWPQe3VODOWvuUurq7q9uUu3CFrpqj5Iqg==}
+    peerDependencies:
+      react: '>=16'
+
+  '@iconify/types@2.0.0':
+    resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
   '@jridgewell/gen-mapping@0.3.8':
     resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
     engines: {node: '>=6.0.0'}
@@ -599,6 +628,10 @@ packages:
     cpu: [x64]
     os: [win32]
 
+  '@types/antd@1.0.4':
+    resolution: {integrity: sha512-gp4PGQckP1kNjj2H6juhjKIVwkpXwCIyIvOlwp2DC6geuhVpDHEEB5gwH4hJabVgBAFtrjBPJ58VIRV9VV9W2g==}
+    deprecated: This is a stub types definition. antd provides its own type definitions, so you do not need this installed.
+
   '@types/babel__core@7.20.5':
     resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
 
@@ -611,12 +644,18 @@ packages:
   '@types/babel__traverse@7.20.7':
     resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
 
+  '@types/dplayer@1.25.5':
+    resolution: {integrity: sha512-p/7O94dHDo0Irn2KWIqFE+fBCA4DS7QL3jfCOjCUPBAOgppyyTjmNZjKEfiJa1M3n1oVQqG7xnPwhiIuCqOzkQ==}
+
   '@types/estree@1.0.7':
     resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
 
   '@types/estree@1.0.8':
     resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
 
+  '@types/history@4.7.11':
+    resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
+
   '@types/json-schema@7.0.15':
     resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
 
@@ -628,6 +667,12 @@ packages:
     peerDependencies:
       '@types/react': ^19.0.0
 
+  '@types/react-router-dom@5.3.3':
+    resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
+
+  '@types/react-router@5.1.20':
+    resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
+
   '@types/react@19.1.8':
     resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==}
 
@@ -728,9 +773,15 @@ packages:
   axios@1.10.0:
     resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==}
 
+  axios@1.2.3:
+    resolution: {integrity: sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==}
+
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
+  balloon-css@1.2.0:
+    resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==}
+
   brace-expansion@1.1.12:
     resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
 
@@ -817,6 +868,9 @@ packages:
     resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
     engines: {node: '>=0.4.0'}
 
+  dplayer@1.27.1:
+    resolution: {integrity: sha512-2laBMXs5V1B9zPwJ7eAIw/OBo+Xjvy03i4GHTk3Cg+IWbrq8rKMFO0fFr6ClAYotYOCcFGOvaJDkOZcgKllsCA==}
+
   dunder-proto@1.0.1:
     resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
     engines: {node: '>= 0.4'}
@@ -1099,6 +1153,10 @@ packages:
   lodash.merge@4.6.2:
     resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
 
+  loose-envify@1.4.0:
+    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+
   lru-cache@5.1.1:
     resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
 
@@ -1146,6 +1204,10 @@ packages:
   normalize.css@8.0.1:
     resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==}
 
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
   optionator@0.9.4:
     resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
     engines: {node: '>= 0.8.0'}
@@ -1189,6 +1251,12 @@ packages:
     resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
     engines: {node: '>= 0.8.0'}
 
+  promise-polyfill@8.3.0:
+    resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
+
+  prop-types@15.8.1:
+    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
   proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
 
@@ -1427,11 +1495,26 @@ packages:
       react: '>=16.9.0'
       react-dom: '>=16.9.0'
 
+  react-audio-player@0.17.0:
+    resolution: {integrity: sha512-aCZgusPxA9HK7rLZcTdhTbBH9l6do9vn3NorgoDZRxRxJlOy9uZWzPaKjd7QdcuP2vXpxGA/61JMnnOEY7NXeA==}
+    peerDependencies:
+      react: '>=16'
+      react-dom: '>=16'
+
   react-dom@19.1.0:
     resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
     peerDependencies:
       react: ^19.1.0
 
+  react-h5-audio-player@3.10.1:
+    resolution: {integrity: sha512-r6fSj9WXR6af1kxH5qQ/tawwDK4KrMfayiVCUettLYGX/KZ3BH8OGuaZP4O5KD0AxwsKAXtBv4kVQCWFzaIrUA==}
+    peerDependencies:
+      react: ^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+      react-dom: ^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+  react-is@16.13.1:
+    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
   react-is@18.3.1:
     resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
 
@@ -1954,6 +2037,13 @@ snapshots:
 
   '@humanwhocodes/retry@0.4.3': {}
 
+  '@iconify/react@5.2.1(react@19.1.0)':
+    dependencies:
+      '@iconify/types': 2.0.0
+      react: 19.1.0
+
+  '@iconify/types@2.0.0': {}
+
   '@jridgewell/gen-mapping@0.3.8':
     dependencies:
       '@jridgewell/set-array': 1.2.1
@@ -2114,6 +2204,16 @@ snapshots:
   '@rollup/rollup-win32-x64-msvc@4.43.0':
     optional: true
 
+  '@types/antd@1.0.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+    dependencies:
+      antd: 5.26.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+    transitivePeerDependencies:
+      - date-fns
+      - luxon
+      - moment
+      - react
+      - react-dom
+
   '@types/babel__core@7.20.5':
     dependencies:
       '@babel/parser': 7.27.5
@@ -2135,10 +2235,14 @@ snapshots:
     dependencies:
       '@babel/types': 7.27.6
 
+  '@types/dplayer@1.25.5': {}
+
   '@types/estree@1.0.7': {}
 
   '@types/estree@1.0.8': {}
 
+  '@types/history@4.7.11': {}
+
   '@types/json-schema@7.0.15': {}
 
   '@types/node@24.0.3':
@@ -2149,6 +2253,17 @@ snapshots:
     dependencies:
       '@types/react': 19.1.8
 
+  '@types/react-router-dom@5.3.3':
+    dependencies:
+      '@types/history': 4.7.11
+      '@types/react': 19.1.8
+      '@types/react-router': 5.1.20
+
+  '@types/react-router@5.1.20':
+    dependencies:
+      '@types/history': 4.7.11
+      '@types/react': 19.1.8
+
   '@types/react@19.1.8':
     dependencies:
       csstype: 3.1.3
@@ -2344,8 +2459,18 @@ snapshots:
     transitivePeerDependencies:
       - debug
 
+  axios@1.2.3:
+    dependencies:
+      follow-redirects: 1.15.9
+      form-data: 4.0.3
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+
   balanced-match@1.0.2: {}
 
+  balloon-css@1.2.0: {}
+
   brace-expansion@1.1.12:
     dependencies:
       balanced-match: 1.0.2
@@ -2422,6 +2547,14 @@ snapshots:
 
   delayed-stream@1.0.0: {}
 
+  dplayer@1.27.1:
+    dependencies:
+      axios: 1.2.3
+      balloon-css: 1.2.0
+      promise-polyfill: 8.3.0
+    transitivePeerDependencies:
+      - debug
+
   dunder-proto@1.0.1:
     dependencies:
       call-bind-apply-helpers: 1.0.2
@@ -2715,6 +2848,10 @@ snapshots:
 
   lodash.merge@4.6.2: {}
 
+  loose-envify@1.4.0:
+    dependencies:
+      js-tokens: 4.0.0
+
   lru-cache@5.1.1:
     dependencies:
       yallist: 3.1.1
@@ -2752,6 +2889,8 @@ snapshots:
 
   normalize.css@8.0.1: {}
 
+  object-assign@4.1.1: {}
+
   optionator@0.9.4:
     dependencies:
       deep-is: 0.1.4
@@ -2791,6 +2930,14 @@ snapshots:
 
   prelude-ls@1.2.1: {}
 
+  promise-polyfill@8.3.0: {}
+
+  prop-types@15.8.1:
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+      react-is: 16.13.1
+
   proxy-from-env@1.1.0: {}
 
   punycode@2.3.1: {}
@@ -3116,11 +3263,26 @@ snapshots:
       react: 19.1.0
       react-dom: 19.1.0(react@19.1.0)
 
+  react-audio-player@0.17.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+    dependencies:
+      prop-types: 15.8.1
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+
   react-dom@19.1.0(react@19.1.0):
     dependencies:
       react: 19.1.0
       scheduler: 0.26.0
 
+  react-h5-audio-player@3.10.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+    dependencies:
+      '@babel/runtime': 7.27.6
+      '@iconify/react': 5.2.1(react@19.1.0)
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+
+  react-is@16.13.1: {}
+
   react-is@18.3.1: {}
 
   react-refresh@0.17.0: {}

+ 9 - 0
src/apis/musician.ts

@@ -0,0 +1,9 @@
+import { request } from "@/utils"
+export const applyMusicianApis = (data:any) => {
+  return request.post('/artist/apply', data)
+}
+
+
+export const applyMusicianStatusApis = () => {
+  return request.get('/artist/status')
+}

+ 22 - 0
src/apis/upload.ts

@@ -0,0 +1,22 @@
+// src/apis/upload.ts
+import { request } from '../utils/request';
+
+export const uploadFile = (bucket: string, file: File, onProgress?: (percent: number) => void) => {
+  const formData = new FormData();
+  formData.append('bucket', bucket);
+  formData.append('file', file);
+
+  return request('/media/upload', {
+    method: 'POST',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    },
+    onUploadProgress: (progressEvent: ProgressEvent) => {
+      if (onProgress) {
+        const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
+        onProgress(percent);
+      }
+    },
+  });
+};

+ 232 - 0
src/pages/layout/components/FloatingPlayerBar/index.css

@@ -0,0 +1,232 @@
+/* src/components/FloatingPlayerBar/index.less */
+.floating-player-bar {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 80px;
+  background: rgba(36, 36, 36, 0.95);
+  backdrop-filter: blur(10px);
+  z-index: 1000;
+  transition: transform 0.3s ease;
+  border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+.floating-player-bar.hide {
+  transform: translateY(100%);
+}
+.floating-player-bar.show {
+  transform: translateY(0);
+}
+.player-container {
+  display: flex;
+  align-items: center;
+  height: 100%;
+  padding: 0 20px;
+  justify-content: center;
+}
+.player-controls {
+  display: flex;
+  align-items: center;
+  color: white;
+}
+/* 播放控制按钮 */
+.play-btn {
+  background: none;
+  border: none;
+  color: white;
+  font-size: 16px;
+  cursor: pointer;
+  margin-right: 10px;
+  padding: 5px;
+  border-radius: 50%;
+  transition: all 0.2s ease;
+}
+.play-btn:hover {
+  background-color: rgba(255, 255, 255, 0.1);
+}
+/* 当前歌曲信息 */
+.current-song {
+  width: 100px;
+  /* 设置宽度为 100px */
+  display: flex;
+  align-items: center;
+  margin-left: 20px;
+  margin-right: 20px;
+  overflow: hidden;
+}
+.current-song img {
+  width: 50px;
+  height: 50px;
+  border-radius: 5px;
+  margin-right: 15px;
+}
+.current-song span {
+  color: white;
+  font-size: 14px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+/* 进度条 */
+.progress-bar {
+  width: 300px;
+  height: 6px;
+  background-color: rgba(255, 255, 255, 0.2);
+  border-radius: 3px;
+  margin-right: 15px;
+  cursor: pointer;
+}
+.progress-bar::-webkit-slider-thumb {
+  appearance: none;
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  background-color: white;
+  cursor: pointer;
+  border: 2px solid #fff;
+}
+.progress-bar::-moz-range-thumb {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  background-color: white;
+  cursor: pointer;
+  border: 2px solid #fff;
+}
+/* 时间显示 */
+.time {
+  color: white;
+  font-size: 12px;
+  margin-right: 20px;
+}
+/* 右侧功能按钮 */
+.player-actions {
+  display: flex;
+  align-items: center;
+  /* 优化后的音量控制样式 */
+}
+.player-actions .action-btn {
+  background: none;
+  border: none;
+  color: white;
+  font-size: 16px;
+  cursor: pointer;
+  margin-left: 15px;
+  padding: 5px;
+  border-radius: 50%;
+  transition: all 0.2s ease;
+}
+.player-actions .action-btn:hover {
+  background-color: rgba(255, 255, 255, 0.1);
+}
+.player-actions .volume-control {
+  position: relative;
+  display: flex;
+  align-items: center;
+  /* 音量图标样式 */
+}
+.player-actions .volume-control .volume-slider {
+  position: absolute;
+  bottom: 100%;
+  right: 0;
+  height: 100px;
+  margin-bottom: 10px;
+  background: rgba(36, 36, 36, 0.95);
+  padding: 10px 5px;
+  border-radius: 5px;
+  backdrop-filter: blur(10px);
+  border: 1px solid rgba(255, 255, 255, 0.1);
+}
+.player-actions .volume-control .volume-slider.ant-slider-vertical .ant-slider-handle {
+  margin-left: -5px;
+  border: 2px solid #fff;
+  background-color: #fff;
+  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2);
+}
+.player-actions .volume-control .volume-slider.ant-slider-vertical .ant-slider-track {
+  background-color: #ff4d4f;
+}
+.player-actions .volume-control .volume-slider.ant-slider-vertical .ant-slider-rail {
+  background-color: rgba(255, 255, 255, 0.2);
+}
+.player-actions .volume-control .volume-icon {
+  position: relative;
+  cursor: pointer;
+}
+.player-actions .volume-control .volume-icon:hover {
+  opacity: 0.8;
+}
+.player-actions .volume-control .volume-icon .volume-value {
+  position: absolute;
+  top: -30px;
+  right: 0;
+  background: rgba(0, 0, 0, 0.9);
+  color: white;
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  opacity: 0;
+  transition: opacity 0.2s ease;
+}
+.player-actions .volume-control .volume-icon:hover .volume-value {
+  opacity: 1;
+}
+/* 优化后的进度条样式 */
+.progress-container {
+  display: flex;
+  align-items: center;
+  margin-right: 20px;
+  /* 进度条轨道样式 */
+  /* 时间显示 */
+}
+.progress-container .progress-bar {
+  width: 300px;
+  height: 6px;
+  background-color: rgba(255, 255, 255, 0.1);
+  border-radius: 3px;
+  cursor: pointer;
+}
+.progress-container .progress-bar::-webkit-slider-thumb {
+  appearance: none;
+  width: 14px;
+  height: 14px;
+  border-radius: 50%;
+  background-color: #fff;
+  cursor: pointer;
+  border: 2px solid #fff;
+  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2);
+}
+.progress-container .progress-bar::-moz-range-thumb {
+  width: 14px;
+  height: 14px;
+  border-radius: 50%;
+  background-color: #fff;
+  cursor: pointer;
+  border: 2px solid #fff;
+}
+.progress-container .progress-track {
+  position: relative;
+  width: 300px;
+  height: 6px;
+  background-color: rgba(255, 255, 255, 0.1);
+  border-radius: 3px;
+  overflow: hidden;
+}
+.progress-container .progress-track::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  width: 0%;
+  background-color: #ff4d4f;
+  transition: width 0.2s ease;
+}
+.progress-container .time-display {
+  color: white;
+  font-size: 12px;
+  margin-left: 10px;
+}
+.progress-container .time-display .current-time {
+  margin-right: 5px;
+}

+ 265 - 0
src/pages/layout/components/FloatingPlayerBar/index.less

@@ -0,0 +1,265 @@
+/* src/components/FloatingPlayerBar/index.less */
+.floating-player-bar {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 80px;
+  background: rgba(36, 36, 36, 0.95);
+  backdrop-filter: blur(10px);
+  z-index: 1000;
+  transition: transform 0.3s ease;
+  border-top: 1px solid rgba(255, 255, 255, 0.1);
+  
+  &.hide {
+    transform: translateY(100%);
+  }
+  
+  &.show {
+    transform: translateY(0);
+  }
+}
+
+.player-container {
+  display: flex;
+  align-items: center;
+  height: 100%;
+  padding: 0 20px;
+  justify-content: center;
+}
+
+.player-controls {
+  display: flex;
+  align-items: center;
+  color: white;
+}
+
+/* 播放控制按钮 */
+.play-btn {
+  background: none;
+  border: none;
+  color: white;
+  font-size: 16px;
+  cursor: pointer;
+  margin-right: 10px;
+  padding: 5px;
+  border-radius: 50%;
+  transition: all 0.2s ease;
+  
+  &:hover {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+}
+
+/* 当前歌曲信息 */
+.current-song {
+  width: 100px; /* 设置宽度为 100px */
+  display: flex;
+  align-items: center;
+  margin-left: 20px;
+  margin-right: 20px;
+  overflow: hidden;
+  
+  img {
+    width: 50px;
+    height: 50px;
+    border-radius: 5px;
+    margin-right: 15px;
+  }
+  
+  span {
+    color: white;
+    font-size: 14px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+}
+
+/* 进度条 */
+.progress-bar {
+  width: 300px;
+  height: 6px;
+  background-color: rgba(255, 255, 255, 0.2);
+  border-radius: 3px;
+  margin-right: 15px;
+  cursor: pointer;
+  
+  &::-webkit-slider-thumb {
+    appearance: none;
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+    background-color: white;
+    cursor: pointer;
+    border: 2px solid #fff;
+  }
+  
+  &::-moz-range-thumb {
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+    background-color: white;
+    cursor: pointer;
+    border: 2px solid #fff;
+  }
+}
+
+/* 时间显示 */
+.time {
+  color: white;
+  font-size: 12px;
+  margin-right: 20px;
+}
+
+/* 右侧功能按钮 */
+.player-actions {
+  display: flex;
+  align-items: center;
+
+  .action-btn {
+    background: none;
+    border: none;
+    color: white;
+    font-size: 16px;
+    cursor: pointer;
+    margin-left: 15px;
+    padding: 5px;
+    border-radius: 50%;
+    transition: all 0.2s ease;
+    
+    &:hover {
+      background-color: rgba(255, 255, 255, 0.1);
+    }
+  }
+  
+/* 优化后的音量控制样式 */
+.volume-control {
+  position: relative;
+  display: flex;
+  align-items: center;
+  
+  .volume-slider {
+    position: absolute;
+    bottom: 100%;
+    right: 0;
+    height: 100px;
+    margin-bottom: 10px;
+    background: rgba(36, 36, 36, 0.95);
+    padding: 10px 5px;
+    border-radius: 5px;
+    backdrop-filter: blur(10px);
+    border: 1px solid rgba(255, 255, 255, 0.1);
+    
+    &.ant-slider-vertical {
+      .ant-slider-handle {
+        margin-left: -5px;
+        border: 2px solid #fff;
+        background-color: #fff;
+        box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2);
+      }
+      
+      .ant-slider-track {
+        background-color: #ff4d4f;
+      }
+      
+      .ant-slider-rail {
+        background-color: rgba(255, 255, 255, 0.2);
+      }
+    }
+  }
+  
+  /* 音量图标样式 */
+  .volume-icon {
+    position: relative;
+    cursor: pointer;
+    
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    .volume-value {
+      position: absolute;
+      top: -30px;
+      right: 0;
+      background: rgba(0, 0, 0, 0.9);
+      color: white;
+      padding: 2px 8px;
+      border-radius: 4px;
+      font-size: 12px;
+      opacity: 0;
+      transition: opacity 0.2s ease;
+    }
+    
+    &:hover .volume-value {
+      opacity: 1;
+    }
+  }
+}
+}
+/* 优化后的进度条样式 */
+.progress-container {
+  display: flex;
+  align-items: center;
+  margin-right: 20px;
+  
+  .progress-bar {
+    width: 300px;
+    height: 6px;
+    background-color: rgba(255, 255, 255, 0.1);
+    border-radius: 3px;
+    cursor: pointer;
+    
+    &::-webkit-slider-thumb {
+      appearance: none;
+      width: 14px;
+      height: 14px;
+      border-radius: 50%;
+      background-color: #fff;
+      cursor: pointer;
+      border: 2px solid #fff;
+      box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2);
+    }
+    
+    &::-moz-range-thumb {
+      width: 14px;
+      height: 14px;
+      border-radius: 50%;
+      background-color: #fff;
+      cursor: pointer;
+      border: 2px solid #fff;
+    }
+  }
+  
+  /* 进度条轨道样式 */
+  .progress-track {
+    position: relative;
+    width: 300px;
+    height: 6px;
+    background-color: rgba(255, 255, 255, 0.1);
+    border-radius: 3px;
+    overflow: hidden;
+    
+    &::before {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      height: 100%;
+      width: 0%;
+      background-color: #ff4d4f;
+      transition: width 0.2s ease;
+    }
+  }
+  
+  /* 时间显示 */
+  .time-display {
+    color: white;
+    font-size: 12px;
+    margin-left: 10px;
+    
+    .current-time {
+      margin-right: 5px;
+    }
+  }
+}

+ 339 - 0
src/pages/layout/components/FloatingPlayerBar/index.tsx

@@ -0,0 +1,339 @@
+// src/pages/layout/components/FloatingPlayerBar/index.tsx
+import React, { useState, useEffect, useRef } from 'react';
+import { Button, Slider, Space, Modal, List } from 'antd';
+import {
+  StepBackwardOutlined,
+  StepForwardOutlined,
+  PlayCircleOutlined,
+  PauseCircleOutlined,
+  SoundOutlined,
+  RetweetOutlined,
+  MenuOutlined,
+  HeartOutlined,
+  ShareAltOutlined
+} from '@ant-design/icons';
+import './index.css';
+
+// 定义歌曲类型
+interface Song {
+  id: number;
+  title: string;
+  artist: string;
+  url: string;
+  cover: string;
+  duration: string;
+}
+
+const FloatingPlayerBar = () => {
+  // 播放列表数据
+  const playlist: Song[] = [
+    {
+      id: 1,
+      title: '放生',
+      artist: '陈旧',
+      url: 'http://117.72.120.45:9000/testbucket/1232e131.mp3',
+      cover: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+      duration: '04:24'
+    },
+    {
+      id: 2,
+      title: '夜曲',
+      artist: '周杰伦',
+      url: 'http://music.163.com/song/media/outer/url?id=123456.mp3',
+      cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+      duration: '03:45'
+    },
+    {
+      id: 3,
+      title: '青花瓷',
+      artist: '周杰伦',
+      url: 'http://music.163.com/song/media/outer/url?id=789012.mp3',
+      cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+      duration: '03:58'
+    }
+  ];
+
+  const [isVisible, setIsVisible] = useState(true);
+  const [isPlaying, setIsPlaying] = useState(false);
+  const [progress, setProgress] = useState(0);
+  const [currentTime, setCurrentTime] = useState(0);
+  const [currentSongIndex, setCurrentSongIndex] = useState(0);
+  const [isPlaylistVisible, setIsPlaylistVisible] = useState(false);
+  const [volume, setVolume] = useState(100); // 音量控制
+  const [isRepeat, setIsRepeat] = useState(false); // 重复播放
+  const audioRef = useRef<HTMLAudioElement>(null);
+  const progressInterval = useRef<NodeJS.Timeout | null>(null);
+  // 在组件中添加音量值显示功能
+  const [showVolumeValue, setShowVolumeValue] = useState(false);
+  // 当前播放的歌曲
+  const currentSong = playlist[currentSongIndex];
+
+  // 监听鼠标是否在窗口底部区域
+  useEffect(() => {
+    let timer: NodeJS.Timeout;
+    const handleMouseMove = (e: MouseEvent) => {
+      const windowBottom = window.innerHeight;
+      const mouseY = e.clientY;
+      if (mouseY > windowBottom - 100) {
+        if (timer) clearTimeout(timer);
+        setIsVisible(true);
+      } else {
+        timer = setTimeout(() => setIsVisible(false), 200);
+      }
+    };
+
+    window.addEventListener('mousemove', handleMouseMove);
+    return () => {
+      window.removeEventListener('mousemove', handleMouseMove);
+      if (timer) clearTimeout(timer);
+    };
+  }, []);
+
+  // 处理音频时间更新
+  useEffect(() => {
+    const audio = audioRef.current;
+    if (!audio) return;
+
+    const updateTime = () => {
+      setCurrentTime(audio.currentTime);
+      if (audio.duration) {
+        setProgress((audio.currentTime / audio.duration) * 100);
+      }
+    };
+
+    audio.addEventListener('timeupdate', updateTime);
+    return () => {
+      audio.removeEventListener('timeupdate', updateTime);
+    };
+  }, [currentSongIndex]);
+
+  // 设置音量
+  useEffect(() => {
+    if (audioRef.current) {
+      audioRef.current.volume = volume / 100;
+    }
+  }, [volume]);
+
+  // 添加播放控制函数
+  const handlePlayPause = () => {
+    if (audioRef.current) {
+      if (isPlaying) {
+        audioRef.current.pause();
+      } else {
+        audioRef.current.play();
+      }
+      setIsPlaying(!isPlaying);
+    }
+  };
+
+  // 上一首
+  const handlePrev = () => {
+    setCurrentSongIndex(prevIndex => {
+      return prevIndex === 0 ? playlist.length - 1 : prevIndex - 1;
+    });
+    // 切换歌曲后自动播放
+    setTimeout(() => {
+      if (audioRef.current) {
+        audioRef.current.play();
+        setIsPlaying(true);
+      }
+    }, 0);
+  };
+
+  // 下一首
+  const handleNext = () => {
+    setCurrentSongIndex(prevIndex => {
+      return prevIndex === playlist.length - 1 ? 0 : prevIndex + 1;
+    });
+    // 切换歌曲后自动播放
+    setTimeout(() => {
+      if (audioRef.current) {
+        audioRef.current.play();
+        setIsPlaying(true);
+      }
+    }, 0);
+  };
+
+  // 处理音频播放结束事件
+  const handleEnded = () => {
+    if (isRepeat) {
+      // 如果是重复播放,重新播放当前歌曲
+      if (audioRef.current) {
+        audioRef.current.currentTime = 0;
+        audioRef.current.play();
+      }
+    } else {
+      // 否则播放下一首
+      handleNext();
+    }
+  };
+
+  // 处理进度条变化
+  const handleProgressChange = (value: number) => {
+    setProgress(value);
+    if (audioRef.current && audioRef.current.duration) {
+      audioRef.current.currentTime = (value / 100) * audioRef.current.duration;
+    }
+  };
+
+  // 格式化时间显示
+  const formatTime = (seconds: number) => {
+    const mins = Math.floor(seconds / 60);
+    const secs = Math.floor(seconds % 60);
+    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
+  };
+
+  // 选择播放列表中的歌曲
+  const handleSelectSong = (index: number) => {
+    setCurrentSongIndex(index);
+    setIsPlaylistVisible(false);
+    // 切换歌曲后自动播放
+    setTimeout(() => {
+      if (audioRef.current) {
+        audioRef.current.play();
+        setIsPlaying(true);
+      }
+    }, 0);
+  };
+
+  // 切换重复播放模式
+  const toggleRepeat = () => {
+    setIsRepeat(!isRepeat);
+  };
+
+  // 处理音量变化
+
+  // 处理音量变化时显示数值
+  const handleVolumeChange = (value: number) => {
+    setVolume(value);
+    setShowVolumeValue(true);
+    setTimeout(() => setShowVolumeValue(false), 200);
+  };
+
+  return (
+    <>
+      {/* 音频元素 */}
+      <audio
+        ref={audioRef}
+        src={currentSong.url}
+        onEnded={handleEnded}
+      />
+
+      <div className={`floating-player-bar ${isVisible ? 'show' : 'hide'}`}>
+        <div className="player-container">
+          {/* 播放控制区 */}
+          <div className="player-controls">
+            <Space size="small">
+              <Button
+                type="text"
+                icon={<StepBackwardOutlined />}
+                onClick={handlePrev}
+                className="play-btn"
+              />
+              <Button
+                type="text"
+                icon={isPlaying ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
+                onClick={handlePlayPause}
+                className="play-btn"
+              />
+              <Button
+                type="text"
+                icon={<StepForwardOutlined />}
+                onClick={handleNext}
+                className="play-btn"
+              />
+            </Space>
+
+            <div className="current-song">
+              <img
+                src={currentSong.cover}
+                alt="封面"
+              />
+              <div className="song-info">
+                <div className="song-title">{currentSong.title}</div>
+                <div className="song-artist">{currentSong.artist}</div>
+              </div>
+            </div>
+
+            <div className="progress-container">
+              <Slider
+                min={0}
+                max={100}
+                value={progress}
+                onChange={handleProgressChange}
+                className="progress-bar"
+              />
+              <div className="time-display">
+                <span className="current-time">{formatTime(currentTime)}</span>
+                <span className="duration"> / {currentSong.duration}</span>
+              </div>
+            </div>
+          </div>
+
+          {/* 右侧功能按钮 */}
+          <div className="player-actions">
+            <Space size="small">
+              <Button type="text" icon={<HeartOutlined />} />
+              <Button
+                type="text"
+                icon={<SoundOutlined />}
+                onClick={() => { }}
+              >
+                <Slider
+                  vertical
+                  defaultValue={100}
+                  value={volume}
+                  onChange={handleVolumeChange}
+                  className="volume-slider"
+                />
+              </Button>
+              <Button
+                type="text"
+                icon={<RetweetOutlined />}
+                onClick={toggleRepeat}
+                className={isRepeat ? 'repeat-active' : ''}
+              />
+              <Button
+                type="text"
+                icon={<MenuOutlined />}
+                onClick={() => setIsPlaylistVisible(true)}
+              />
+              <Button type="text" icon={<ShareAltOutlined />} />
+            </Space>
+          </div>
+        </div>
+      </div>
+
+      {/* 播放列表模态框 */}
+      <Modal
+        title="播放列表"
+        open={isPlaylistVisible}
+        onCancel={() => setIsPlaylistVisible(false)}
+        footer={null}
+        width={400}
+      >
+        <List
+          dataSource={playlist}
+          renderItem={(song, index) => (
+            <List.Item
+              onClick={() => handleSelectSong(index)}
+              className={index === currentSongIndex ? 'active-song' : ''}
+              style={{ cursor: 'pointer' }}
+            >
+              <List.Item.Meta
+                avatar={<img src={song.cover} alt={song.title} width={40} height={40} />}
+                title={song.title}
+                description={`${song.artist} - ${song.duration}`}
+              />
+              {index === currentSongIndex && (
+                <span>{isPlaying ? '播放中' : '已暂停'}</span>
+              )}
+            </List.Item>
+          )}
+        />
+      </Modal>
+    </>
+  );
+};
+
+export default FloatingPlayerBar;

+ 154 - 0
src/pages/layout/components/FloatingPlayerBar2/index.tsx

@@ -0,0 +1,154 @@
+import React, { useState } from 'react';
+import AudioPlayer from 'react-h5-audio-player';
+import 'react-h5-audio-player/lib/styles.css';
+
+interface Song {
+  id: number;
+  title: string;
+  artist: string;
+  url: string;
+  cover: string;
+  duration: string;
+}
+
+const MusicPlayer: React.FC = () => {
+  const playlist: Song[] = [
+    {
+      id: 1,
+      title: '放生',
+      artist: '陈旧',
+      url: 'http://117.72.120.45:9000/testbucket/1232e131.mp3',
+      cover: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+      duration: '04:24'
+    },
+    {
+      id: 2,
+      title: '夜曲',
+      artist: '周杰伦',
+      url: 'http://music.163.com/song/media/outer/url?id=123456.mp3',
+      cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+      duration: '03:45'
+    },
+    {
+      id: 3,
+      title: '青花瓷',
+      artist: '周杰伦',
+      url: 'http://music.163.com/song/media/outer/url?id=789012.mp3',
+      cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+      duration: '03:58'
+    }
+  ];
+
+  const [currentTrackIndex, setCurrentTrackIndex] = useState(0);
+  const [isPlaying, setIsPlaying] = useState(false);
+  const [audioError, setAudioError] = useState<string | null>(null);
+
+  const currentTrack = playlist[currentTrackIndex];
+
+  // 上一首逻辑
+  const handlePrevious = () => {
+    setCurrentTrackIndex(prev => (prev === 0 ? playlist.length - 1 : prev - 1));
+    setAudioError(null);
+    if (isPlaying) setIsPlaying(true); // 切换后继续播放
+  };
+
+  // 下一首逻辑
+  const handleNext = () => {
+    setCurrentTrackIndex(prev => (prev === playlist.length - 1 ? 0 : prev + 1));
+    setAudioError(null);
+    if (isPlaying) setIsPlaying(true); // 切换后继续播放
+  };
+
+  // 播放列表项点击
+  const handleTrackSelect = (index: number) => {
+    setCurrentTrackIndex(index);
+    setIsPlaying(true);
+    setAudioError(null);
+  };
+
+  return (
+    <div style={{ padding: '20px' }}>
+      {/* 错误提示 */}
+      {audioError && (
+        <div style={{ color: 'red', padding: '10px', backgroundColor: '#ffe6e6', borderRadius: '4px', marginBottom: '10px' }}>
+          {audioError}
+        </div>
+      )}
+
+      {/* 播放器:适配 3.10.1 版本的核心 Props */}
+      <AudioPlayer
+        key={currentTrackIndex} // 切换歌曲时强制刷新组件
+        src={currentTrack.url}
+        onPlay={() => setIsPlaying(true)}
+        onPause={() => setIsPlaying(false)}
+        onClickPrevious={handlePrevious} // 绑定上一首(3.10.1 支持)
+        onClickNext={handleNext} // 绑定下一首(3.10.1 支持)
+        onEnded={handleNext} // 播放结束自动下一首
+        onError={(e) => setAudioError('音频加载失败,请检查网络')}
+        showSkipControls={true} // 显示上一首/下一首按钮
+        header={
+          // 手动渲染封面图和歌曲信息(替代 3.10.1 不支持的 cover 属性)
+          <div style={{ textAlign: 'center', marginBottom: '10px' }}>
+            <img
+              src={currentTrack.cover}
+              alt={currentTrack.title}
+              style={{ width: '80px', height: '80px', borderRadius: '8px', marginBottom: '8px' }}
+            />
+            <h3 style={{ margin: '0', fontSize: '16px' }}>{currentTrack.title}</h3>
+            <p style={{ margin: '0', fontSize: '14px', color: '#666' }}>{currentTrack.artist}</p>
+          </div>
+        }
+        autoPlay={false}
+        style={{
+          // 自定义播放器样式(适配深色/浅色主题)
+          backgroundColor: '#f5f5f5',
+          padding: '10px',
+          borderRadius: '8px'
+        }}
+      />
+
+      {/* 播放列表 */}
+      <div style={{ marginTop: '20px' }}>
+        <h3 style={{ fontSize: '16px' }}>播放列表</h3>
+        <ul style={{ listStyle: 'none', padding: '0', border: '1px solid #eee', borderRadius: '8px' }}>
+          {playlist.map((song, index) => (
+            <li
+              key={song.id}
+              onClick={() => handleTrackSelect(index)}
+              style={{
+                padding: '12px',
+                backgroundColor: index === currentTrackIndex ? '#e6f7ff' : '#fff',
+                borderBottom: index !== playlist.length - 1 ? '1px solid #eee' : 'none',
+                cursor: 'pointer',
+                display: 'flex',
+                alignItems: 'center'
+              }}
+            >
+              {/* 播放状态指示器 */}
+              {index === currentTrackIndex && isPlaying && (
+                <span style={{ marginRight: '10px', color: '#1890ff' }}>▶</span>
+              )}
+              {/* 列表项封面图 */}
+              <img
+                src={song.cover}
+                alt={song.title}
+                style={{ width: '36px', height: '36px', borderRadius: '4px', marginRight: '10px' }}
+              />
+              {/* 歌曲信息 */}
+              <div style={{ flex: 1 }}>
+                <div style={{ fontSize: '14px', fontWeight: index === currentTrackIndex ? 'bold' : 'normal' }}>
+                  {song.title}
+                </div>
+                <div style={{ fontSize: '12px', color: '#666' }}>
+                  {song.artist} · {song.duration}
+                </div>
+              </div>
+            </li>
+          ))}
+        </ul>
+      </div>
+    </div>
+  );
+};
+
+export default MusicPlayer;

+ 0 - 0
src/pages/layout/components/Wy_Header/components/AuthCallback.tsx → src/pages/layout/components/LoginContent/components/AuthCallback.tsx


+ 0 - 0
src/pages/layout/components/Wy_Header/components/GiteeLogin.tsx → src/pages/layout/components/LoginContent/components/GiteeLogin.tsx


+ 112 - 0
src/pages/layout/components/LoginContent/index.css

@@ -0,0 +1,112 @@
+.yzm_login_content {
+  height: 230px;
+  text-align: center;
+}
+.yzm_login_content .yzm_login_content_form {
+  width: 320px;
+  margin: 0 auto;
+}
+.login_content {
+  height: 320px;
+  position: relative;
+  /* 添加相对定位,用于三角形按钮定位 */
+}
+.login_content .login_content_item {
+  height: 230px;
+  display: flex;
+}
+.login_content .login_content_item .login_content_left {
+  width: 300px;
+}
+.login_content .login_content_item .login_content_left .login_content_left_top {
+  text-align: center;
+}
+.login_content .login_content_item .login_content_left .login_content_left_bottom {
+  text-align: center;
+  height: 30px;
+  line-height: 30px;
+  background-color: aqua;
+  border-radius: 5px;
+  color: aliceblue;
+  width: 224px;
+  margin: 50px auto 0 auto;
+  background: linear-gradient(to right, #2078cb, #4c95da);
+  border: 1px solid #428dd6;
+}
+.login_content .login_content_item .login_content_left .login_content_left_bottom:hover {
+  background: linear-gradient(to right, #5a98d1, #4c95da);
+}
+.login_content .login_content_item .login_content_right {
+  flex: 1;
+  padding-left: 40px;
+  border-left: 1px solid #7e7e7e;
+  font-size: 10px;
+}
+.login_content .login_content_item .login_content_right .login_content_right_item {
+  display: flex;
+  height: 38px;
+  margin-bottom: 15px;
+}
+.login_content .login_content_item .login_content_right .login_content_right_item .login_content_right_item_icon {
+  border-radius: 50%;
+  text-align: center;
+  width: 40px;
+  border: 1px solid #7e7e7e;
+  font-size: 25px;
+  height: 38px;
+}
+.login_content .login_content_item .login_content_right .login_content_right_item .login_content_right_item_text {
+  line-height: 38px;
+  margin-left: 20px;
+}
+.login_content .login_protocol {
+  margin-top: 40px;
+}
+.login_content .login_protocol .protocol_span {
+  color: #1d80e3;
+}
+/* 添加三角形按钮样式 */
+.qr-trigger {
+  position: absolute;
+  bottom: 0px;
+  right: 0px;
+  cursor: pointer;
+}
+/* 二维码登录相关样式 */
+.qr-code-section {
+  display: flex;
+  justify-content: space-around;
+}
+.qr-code-section .qr-code {
+  height: 220px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: space-between;
+}
+.custom-alert {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 300px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 9999;
+  /* 关键属性:确保它在其他组件之上 */
+}
+.custom-alert-content {
+  width: 300px;
+  height: 120px;
+  background-color: rgba(0, 0, 0, 0.7);
+  /* 灰色 + 30% 透明度 */
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+  font-size: 16px;
+  text-align: center;
+  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
+}

+ 127 - 0
src/pages/layout/components/LoginContent/index.less

@@ -0,0 +1,127 @@
+.yzm_login_content {
+  height: 230px;
+  text-align: center;
+  .yzm_login_content_form {
+    width:320px;
+    margin: 0 auto;
+    
+  }
+}
+.login_content {
+  height: 320px;
+  position: relative; /* 添加相对定位,用于三角形按钮定位 */
+
+  .login_content_item {
+    height: 230px;
+
+    display: flex;
+    .login_content_left {
+      width: 300px;
+
+      .login_content_left_top {
+        text-align: center;
+      }
+      .login_content_left_bottom {
+        text-align: center;
+
+        height: 30px;
+        line-height: 30px;
+        background-color: aqua;
+        border-radius: 5px;
+        color: aliceblue;
+        width: 224px;
+        margin: 50px auto 0 auto;
+
+        background: linear-gradient(
+          to right,
+          rgb(32, 120, 203),
+          rgb(76, 149, 218)
+        );
+        border: 1px solid rgb(66, 141, 214);
+      }
+      .login_content_left_bottom:hover {
+        background: linear-gradient(
+          to right,
+          rgb(90, 152, 209),
+          rgb(76, 149, 218)
+        );
+      }
+    }
+    .login_content_right {
+      flex: 1;
+      padding-left: 40px;
+      border-left: 1px solid rgb(126, 126, 126);
+      font-size: 10px;
+      .login_content_right_item {
+        display: flex;
+        height: 38px;
+        margin-bottom: 15px;
+        .login_content_right_item_icon {
+          border-radius: 50%;
+          text-align: center;
+          width: 40px;
+          border: 1px solid rgb(126, 126, 126);
+          font-size: 25px;
+          height: 38px;
+        }
+        .login_content_right_item_text {
+          line-height: 38px;
+          margin-left: 20px;
+        }
+      }
+    }
+  }
+  .login_protocol {
+    margin-top: 40px;
+    .protocol_span {
+      color: rgb(29, 128, 227);
+    }
+  }
+}
+
+/* 添加三角形按钮样式 */
+.qr-trigger {
+  position: absolute;
+  bottom: 0px;
+  right: 0px;
+  cursor: pointer;
+}
+
+/* 二维码登录相关样式 */
+.qr-code-section {
+  display: flex;
+  justify-content: space-around;
+  
+  .qr-code {
+    height: 220px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: space-between;
+  }
+}
+
+.custom-alert {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 300px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 9999; /* 关键属性:确保它在其他组件之上 */
+}
+.custom-alert-content {
+  width: 300px;
+  height: 120px;
+  background-color: rgba(0, 0, 0, 0.7); /* 灰色 + 30% 透明度 */
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+  font-size: 16px;
+  text-align: center;
+  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
+}

+ 480 - 0
src/pages/layout/components/LoginContent/index.tsx

@@ -0,0 +1,480 @@
+// src/components/LoginContent/index.tsx
+import React, { useState, useEffect } from 'react';
+import {
+  WechatOutlined,
+  QqOutlined,
+  WeiboCircleOutlined,
+  GithubOutlined,
+} from '@ant-design/icons';
+import {
+  Checkbox,
+  Image,
+  Button,
+  Form,
+  Input,
+  Space,
+  Select,
+} from 'antd';
+import type { CheckboxProps } from 'antd';
+import type { FormProps } from 'antd';
+import { useNavigate } from 'react-router-dom';
+import { loginByPasswordApi, type loginResType } from '@/apis/login';
+import type { loginType } from '@/type/login';
+import { loginByWechat } from '@/apis/login';
+import { setToken, setUserinfo as setUserInfoUtil } from '@/utils';
+import GiteeLogin from '@/pages/layout/components/LoginContent/components/GiteeLogin';
+import './index.css'
+interface LoginContentProps {
+  onLoginSuccess?: () => void;
+  showBackButton?: boolean;
+  onBack?: () => void;
+}
+
+const LoginContent: React.FC<LoginContentProps> = ({
+  onLoginSuccess,
+}) => {
+  const [type, setType] = useState(0); // 默认为主登录页面
+  const [isChecked, setIsChecked] = useState(false);
+  const [showCustomAlert, setShowCustomAlert] = useState(false);
+
+  const onChange: CheckboxProps['onChange'] = (e) => {
+    setIsChecked(e.target.checked);
+  };
+
+  const CustomAlert = () => {
+    return (
+      <div
+        className="custom-alert"
+        onClick={() => setShowCustomAlert(false)}
+      >
+        <div className="custom-alert-content">
+          <p>请先同意服务条款和隐私政策</p>
+        </div>
+      </div>
+    );
+  };
+
+  type FieldType = {
+    username?: string;
+    password?: string;
+    remember?: string;
+  };
+
+  const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
+    console.log('Success:', values);
+  };
+
+  const LoginByPassword: FormProps<loginType>['onFinish'] = async (values) => {
+    try {
+      const res = await loginByPasswordApi(values);
+      if (res && res.access_token) {
+        setToken(res);
+        setUserInfoUtil(res.access_token);
+        onLoginSuccess?.();
+      } else {
+        console.log('登录失败');
+      }
+    } catch (error) {
+      console.error('登录请求失败:', error);
+    }
+  };
+
+  const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (
+    errorInfo
+  ) => {
+    console.log('Failed:', errorInfo);
+  };
+
+  const options = [
+    { value: '+86', label: '中国大陆+86' },
+    { value: '+852', label: '香港+852' },
+    { value: '+853', label: '澳门+853' },
+    { value: '+886', label: '台湾+886' },
+    { value: '+82', label: '韩国+82' },
+    { value: '+81', label: '日本+81' },
+    { value: '+65', label: '新加坡+65' },
+    { value: '+91', label: '印度+91' },
+    { value: '+60', label: '马来西亚+60' },
+    { value: '+44', label: '英国+44' },
+    { value: '+33', label: '法国+33' },
+    { value: '+49', label: '德国+49' },
+    { value: '+39', label: '意大利+39' },
+    { value: '+34', label: '西班牙+34' },
+  ];
+
+  const navigator = useNavigate();
+
+  const handleWechatLogin = () => {
+    setType(3);
+    initWechatLogin();
+  };
+
+  const handleQrCodeLogin = () => {
+    setType(4);
+  };
+
+  const backToMain = () => {
+    setType(0);
+  };
+
+  const goToPhoneLogin = () => {
+    if (!isChecked) {
+      setShowCustomAlert(true);
+      return;
+    }
+    setType(1);
+  };
+
+  const goToPasswordLogin = () => {
+    setType(2);
+  };
+
+  const initWechatLogin = () => {
+    const script = document.createElement('script');
+    script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js';
+    script.onload = () => {
+      new (window as any).WxLogin({
+        self_redirect: false,
+        id: 'wechat_login_container',
+        appid: 'wxbdc5610cc59c1631',
+        scope: 'snsapi_login',
+        redirect_uri: encodeURIComponent('http://baidu.com'),
+        state: generateState(),
+        style: 'black',
+        href: '',
+      });
+    };
+    document.body.appendChild(script);
+  };
+
+  const generateState = () => {
+    return (
+      Math.random().toString(36).substring(2, 15) +
+      Math.random().toString(36).substring(2, 15)
+    );
+  };
+
+  // 处理微信登录回调
+  useEffect(() => {
+    const handleMessage = (event: MessageEvent) => {
+      if (event.data && event.data.type === 'wechat_login') {
+        handleWechatCode(event.data.code, event.data.state);
+      }
+    };
+
+    window.addEventListener('message', handleMessage);
+    return () => {
+      window.removeEventListener('message', handleMessage);
+    };
+  }, []);
+
+  const handleWechatCode = async (code: string, state: string) => {
+    try {
+      const response = await loginByWechat({ code, state });
+      const res: loginResType = response.data;
+
+      if (res && res.access_token) {
+        setToken(res);
+        setUserInfoUtil(res.access_token);
+        setType(0);
+        onLoginSuccess?.();
+        window.dispatchEvent(new CustomEvent('user-info-updated'));
+      } else {
+        console.error('微信登录失败: 响应数据不完整');
+      }
+    } catch (error) {
+      console.error('微信登录错误:', error);
+    }
+  };
+  // 返回按钮组件 - 始终显示并返回主登录页面
+  const BackButton = () => (
+    <div
+      style={{
+        color: 'rgb(56, 157, 252)',
+        cursor: 'pointer',
+        textAlign: 'center',
+        marginTop: '20px',
+      }}
+      onClick={backToMain} // 始终返回主登录页面
+    >
+      {'<'}返回
+    </div>
+  );
+  return (
+    <div>
+      {showCustomAlert && <CustomAlert />}
+
+      {/* 主登录页面 */}
+      {type === 0 && (
+        <div className="login_content">
+          <div className="login_content_item">
+            <div className="login_content_left">
+              <div className="login_content_left_top">
+                <Image
+                  width={224}
+                  src={'https://web-asd-asd.oss-cn-beijing.aliyuncs.com/login.png'}
+                />
+              </div>
+              <div
+                className="login_content_left_bottom"
+                style={{ cursor: 'pointer' }}
+              >
+                <div onClick={goToPhoneLogin}>手机号登录/注册</div>
+              </div>
+            </div>
+            <div className="login_content_right">
+              <div
+                className="login_content_right_item"
+                onClick={handleWechatLogin}
+              >
+                <div
+                  className="login_content_right_item_icon"
+                  style={{ color: 'rgb(105, 183, 53)' }}
+                >
+                  <WechatOutlined />
+                </div>
+                <div className="login_content_right_item_text">微信登陆</div>
+              </div>
+              <div className="login_content_right_item">
+                <div
+                  className="login_content_right_item_icon"
+                  style={{ color: 'rgb(52, 161, 222)' }}
+                >
+                  <QqOutlined />
+                </div>
+                <div className="login_content_right_item_text">QQ登陆</div>
+              </div>
+              <div className="login_content_right_item">
+                <div className="login_content_right_item_icon">
+                  <WeiboCircleOutlined style={{ color: 'rgb(233, 77, 69)' }} />
+                </div>
+                <div className="login_content_right_item_text">微博登录</div>
+              </div>
+              <div className="login_content_right_item">
+                <div
+                  className="login_content_right_item_icon"
+                  style={{ color: 'rgb(213, 71, 63)', fontSize: '20px' }}
+                >
+                  易
+                </div>
+                <div className="login_content_right_item_text">
+                  网易邮箱账号登录
+                </div>
+              </div>
+              <div className="login_content_right_item">
+                <div
+                  className="login_content_right_item_icon"
+                  style={{ color: 'rgb(51, 51, 51)', fontSize: '20px' }}
+                >
+                  <GithubOutlined />
+                </div>
+                <div className="login_content_right_item_text">
+                  <GiteeLogin
+                    onSuccess={(userInfo) => {
+                      console.log('Gitee 登录成功:', userInfo);
+                      onLoginSuccess?.();
+                    }}
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+          <div className="login_protocol">
+            <Checkbox onChange={onChange} checked={isChecked}>
+              同意
+            </Checkbox>
+            <span className="protocol_span">
+              《服务条款》《隐私政策》《儿童隐私政策》
+            </span>
+          </div>
+
+          <div className="qr-trigger" onClick={handleQrCodeLogin}>
+            <Image src='http://117.72.120.45:9000/testbucket/erweima.png' width={60} preview={false} />
+          </div>
+        </div>
+      )}
+
+      {/* 手机号登录页面 */}
+      {type === 1 && (
+        <div className="yzm_login_content">
+          <div style={{ marginTop: '60px' }}>
+            <Form
+              className="yzm_login_content_form"
+              name="basic"
+              initialValues={{ remember: true }}
+              onFinish={onFinish}
+              onFinishFailed={onFinishFailed}
+              autoComplete="off"
+            >
+              <Form.Item<FieldType>
+                name="username"
+                rules={[{ required: true, message: '请输入手机号!' }]}
+              >
+                <Space.Compact>
+                  <Select
+                    style={{ width: '180px' }}
+                    defaultValue="+86"
+                    options={options}
+                  />
+                  <Input placeholder="请输入手机号"></Input>
+                </Space.Compact>
+              </Form.Item>
+
+              <Form.Item<FieldType>
+                name="password"
+                rules={[{ required: true, message: '请输入验证码!' }]}
+              >
+                <div style={{ display: 'flex' }}>
+                  <Input placeholder="请输入验证码"></Input>
+                  <Button type="primary" shape="round" danger>
+                    获取验证码
+                  </Button>
+                </div>
+              </Form.Item>
+
+              <Form.Item>
+                <Button
+                  type="primary"
+                  block
+                  shape="round"
+                  danger
+                  htmlType="submit"
+                >
+                  登录
+                </Button>
+              </Form.Item>
+              <div
+                style={{
+                  textAlign: 'left',
+                  color: 'rgb(145, 142, 142)',
+                  cursor: 'pointer',
+                }}
+                onClick={goToPasswordLogin}
+              >
+                密码登录
+              </div>
+            </Form>
+          </div>
+          <div
+            style={{ color: 'rgb(56, 157, 252)', cursor: 'pointer' }}
+            onClick={backToMain}
+          >
+            {'<'}其他登录方式
+          </div>
+        </div>
+      )}
+
+      {/* 密码登录页面 */}
+      {type === 2 && (
+        <div className="yzm_login_content">
+          <div style={{ marginTop: '60px' }}>
+            <Form
+              className="yzm_login_content_form"
+              name="basic"
+              initialValues={{ remember: true }}
+              onFinish={LoginByPassword}
+              onFinishFailed={onFinishFailed}
+              autoComplete="off"
+            >
+              <Form.Item<FieldType>
+                name="username"
+                rules={[{ required: true, message: '请输入手机号!' }]}
+              >
+                <Space.Compact>
+                  <Select
+                    style={{ width: '180px' }}
+                    defaultValue="+86"
+                    options={options}
+                  />
+                  <Input placeholder="请输入手机号"></Input>
+                </Space.Compact>
+              </Form.Item>
+
+              <Form.Item<FieldType>
+                name="password"
+                rules={[{ required: true, message: '请输入密码!' }]}
+              >
+                <div style={{ display: 'flex' }}>
+                  <Input
+                    placeholder="请输入密码"
+                    suffix={
+                      <span
+                        style={{
+                          color: 'rgb(56, 157, 252)',
+                          cursor: 'pointer',
+                        }}
+                        onClick={() => {
+                          navigator('/login/yzm');
+                        }}
+                      >
+                        忘记密码?
+                      </span>
+                    }
+                  />
+                </div>
+              </Form.Item>
+
+              <Form.Item>
+                <Button
+                  type="primary"
+                  block
+                  shape="round"
+                  danger
+                  htmlType="submit"
+                >
+                  登录
+                </Button>
+              </Form.Item>
+              <div
+                style={{
+                  color: 'rgb(56, 157, 252)',
+                  cursor: 'pointer',
+                  textAlign: 'left',
+                }}
+                onClick={backToMain}
+              >
+                {'<'}其他登录方式
+              </div>
+            </Form>
+          </div>
+        </div>
+      )}
+
+      {/* 微信扫码登录页面 */}
+      {type === 3 && (
+        <div style={{ textAlign: 'center', padding: '20px 0' }}>
+          <div id="wechat_login_container"></div>
+          <p style={{ marginTop: '20px', color: '#999' }}>
+            请使用微信扫描二维码登录
+          </p>
+          <BackButton />
+        </div>
+      )}
+
+      {/* 二维码登录页面 */}
+      {type === 4 && (
+        <div style={{ textAlign: 'center', padding: '20px 0' }}>
+          <div className="qr-code-section">
+            <Image
+              src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/34905050930/6160/8991/0f0f/85d4094a013dc7c0709fd198152ee9f7.png"
+              width={125}
+            />
+            <div className="qr-code">
+              <p>扫码登录</p>
+              <Image
+                src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
+                width={128}
+              />
+              <p>
+                使用 <a onClick={() => navigator('/download')}>网易云音乐APP</a> 扫码登录
+              </p>
+            </div>
+          </div>
+          <BackButton />
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default LoginContent;

+ 18 - 0
src/pages/layout/components/MusicListPage/index.css

@@ -0,0 +1,18 @@
+.page-container {
+  padding: 20px;
+}
+.music-item {
+  padding: 15px;
+  border-bottom: 1px solid #eee;
+  cursor: pointer;
+}
+.music-item:hover {
+  background-color: #f5f5f5;
+}
+.error-tip {
+  color: red;
+  padding: 10px;
+  background-color: #ffe6e6;
+  margin-bottom: 10px;
+  border-radius: 4px;
+}

+ 24 - 0
src/pages/layout/components/MusicListPage/index.less

@@ -0,0 +1,24 @@
+// src/styles/index.less
+// 页面布局
+.page-container {
+  padding: 20px;
+}
+
+// 音乐列表项
+.music-item {
+  padding: 15px;
+  border-bottom: 1px solid #eee;
+  cursor: pointer;
+  &:hover {
+    background-color: #f5f5f5;
+  }
+}
+
+// 错误提示
+.error-tip {
+  color: red;
+  padding: 10px;
+  background-color: #ffe6e6;
+  margin-bottom: 10px;
+  border-radius: 4px;
+}

+ 50 - 0
src/pages/layout/components/MusicListPage/index.tsx

@@ -0,0 +1,50 @@
+// src/pages/MusicListPage.tsx
+import React from 'react';
+import { useMusic, Song } from '@/utils/MusicContext';
+import '../styles/index.less'; // 引入全局Less
+
+const sampleSongs: Song[] = [
+  {
+    id: 1,
+    title: '放生',
+    artist: '陈旧',
+    url: 'http://117.72.120.45:9000/testbucket/1232e131.mp3',
+    cover: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    duration: '04:24',
+  },
+  {
+    id: 2,
+    title: '夜曲',
+    artist: '周杰伦',
+    url: 'http://music.163.com/song/media/outer/url?id=123456.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '03:45',
+  },
+];
+
+const MusicListPage: React.FC = () => {
+  const { play } = useMusic();
+
+  const handlePlaySong = (song: Song) => {
+    play(song, sampleSongs);
+  };
+
+  return (
+    <div className="page-container">
+      <h2>音乐列表</h2>
+      <ul style={{ listStyle: 'none', padding: 0 }}>
+        {sampleSongs.map((song) => (
+          <li
+            key={song.id}
+            className="music-item"
+            onClick={() => handlePlaySong(song)}
+          >
+            <div>{song.title} - {song.artist}</div>
+          </li>
+        ))}
+      </ul>
+    </div>
+  );
+};
+
+export default MusicListPage;

+ 0 - 0
src/pages/layout/components/Wy_Header/components/Wy_Search/index.css → src/pages/layout/components/Wy_Header/components/CreatorCenter/index.css


+ 2 - 2
src/pages/layout/components/Wy_Header/components/Wy_Search/index.tsx → src/pages/layout/components/Wy_Header/components/CreatorCenter/index.tsx

@@ -3,7 +3,7 @@ import React from 'react'
 import { Button, ConfigProvider } from 'antd'
 import { useNavigate } from 'react-router-dom' // 引入路由跳转钩子
 
-const Wy_Search: React.FC = () => {
+const CreatorCenter: React.FC = () => {
   const navigate = useNavigate() // 获取导航函数
 
   const handleCreatorCenterClick = () => {
@@ -38,4 +38,4 @@ const Wy_Search: React.FC = () => {
   )
 }
 
-export default Wy_Search
+export default CreatorCenter

+ 0 - 93
src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.css

@@ -32,96 +32,3 @@
 .login:hover {
   color: #ffffff;
 }
-.yzm_login_content {
-  height: 230px;
-  text-align: center;
-}
-.yzm_login_content .yzm_login_content_form {
-  width: 320px;
-  margin: 0 auto;
-}
-.login_content {
-  height: 320px;
-}
-.login_content .login_content_item {
-  height: 230px;
-  margin-top: 50px;
-  display: flex;
-}
-.login_content .login_content_item .login_content_left {
-  width: 300px;
-}
-.login_content .login_content_item .login_content_left .login_content_left_top {
-  text-align: center;
-}
-.login_content .login_content_item .login_content_left .login_content_left_bottom {
-  text-align: center;
-  margin-top: 50px;
-  height: 30px;
-  line-height: 30px;
-  background-color: aqua;
-  border-radius: 5px;
-  color: aliceblue;
-  width: 224px;
-  margin: 50px auto 0 auto;
-  background: linear-gradient(to right, #2078cb, #4c95da);
-  border: 1px solid #428dd6;
-}
-.login_content .login_content_item .login_content_left .login_content_left_bottom:hover {
-  background: linear-gradient(to right, #5a98d1, #4c95da);
-}
-.login_content .login_content_item .login_content_right {
-  flex: 1;
-  padding-left: 40px;
-  border-left: 1px solid #7e7e7e;
-  font-size: 10px;
-}
-.login_content .login_content_item .login_content_right .login_content_right_item {
-  display: flex;
-  height: 38px;
-  margin-bottom: 15px;
-}
-.login_content .login_content_item .login_content_right .login_content_right_item .login_content_right_item_icon {
-  border-radius: 50%;
-  text-align: center;
-  width: 40px;
-  border: 1px solid #7e7e7e;
-  font-size: 25px;
-  height: 38px;
-}
-.login_content .login_content_item .login_content_right .login_content_right_item .login_content_right_item_text {
-  line-height: 38px;
-  margin-left: 20px;
-}
-.login_content .login_protocol {
-  margin-top: 40px;
-}
-.login_content .login_protocol .protocol_span {
-  color: #1d80e3;
-}
-.custom-alert {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 300px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 9999;
-  /* 关键属性:确保它在其他组件之上 */
-}
-.custom-alert-content {
-  width: 300px;
-  height: 120px;
-  background-color: rgba(0, 0, 0, 0.7);
-  /* 灰色 + 30% 透明度 */
-  border-radius: 8px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: white;
-  font-size: 16px;
-  text-align: center;
-  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
-}

+ 0 - 103
src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.less

@@ -30,106 +30,3 @@
     color: rgb(255, 255, 255);
   }
 }
-.yzm_login_content {
-  height: 230px;
-  text-align: center;
-  .yzm_login_content_form {
-    width:320px;
-    margin: 0 auto;
-    
-  }
-}
-.login_content {
-  height: 320px;
-
-  .login_content_item {
-    height: 230px;
-    margin-top: 50px;
-    display: flex;
-    .login_content_left {
-      width: 300px;
-
-      .login_content_left_top {
-        text-align: center;
-      }
-      .login_content_left_bottom {
-        text-align: center;
-        margin-top: 50px;
-        height: 30px;
-        line-height: 30px;
-        background-color: aqua;
-        border-radius: 5px;
-        color: aliceblue;
-        width: 224px;
-        margin: 50px auto 0 auto;
-
-        background: linear-gradient(
-          to right,
-          rgb(32, 120, 203),
-          rgb(76, 149, 218)
-        );
-        border: 1px solid rgb(66, 141, 214);
-      }
-      .login_content_left_bottom:hover {
-        background: linear-gradient(
-          to right,
-          rgb(90, 152, 209),
-          rgb(76, 149, 218)
-        );
-      }
-    }
-    .login_content_right {
-      flex: 1;
-      padding-left: 40px;
-      border-left: 1px solid rgb(126, 126, 126);
-      font-size: 10px;
-      .login_content_right_item {
-        display: flex;
-        height: 38px;
-        margin-bottom: 15px;
-        .login_content_right_item_icon {
-          border-radius: 50%;
-          text-align: center;
-          width: 40px;
-          border: 1px solid rgb(126, 126, 126);
-          font-size: 25px;
-          height: 38px;
-        }
-        .login_content_right_item_text {
-          line-height: 38px;
-          margin-left: 20px;
-        }
-      }
-    }
-  }
-  .login_protocol {
-    margin-top: 40px;
-    .protocol_span {
-      color: rgb(29, 128, 227);
-    }
-  }
-}
-.custom-alert {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 300px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 9999; /* 关键属性:确保它在其他组件之上 */
-}
-.custom-alert-content {
-  width: 300px;
-  height: 120px;
-  background-color: rgba(0, 0, 0, 0.7); /* 灰色 + 30% 透明度 */
-  border-radius: 8px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: white;
-  font-size: 16px;
-  text-align: center;
-  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
-}

+ 56 - 498
src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.tsx

@@ -1,46 +1,34 @@
+// src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.tsx
 import {
   CrownOutlined,
-  GithubOutlined,
   IdcardOutlined,
   LogoutOutlined,
   MessageOutlined,
-  QqOutlined,
   SettingOutlined,
   StarOutlined,
   UserOutlined,
-  WechatOutlined,
-  WeiboCircleOutlined,
-} from '@ant-design/icons'
-import {
-  Avatar,
-  Badge,
-  Checkbox,
-  Image,
-  Modal,
-  Popover,
-  type CheckboxProps,
-  Button,
-  Form,
-  Input,
-  Space,
-  Select,
-} from 'antd'
-import './index.css'
-import { getToken, removeToken, removeUserinfo, setToken, setUserinfo, getUserinfo } from '@/utils'
-import { useEffect, useState } from 'react'
-import type { FormProps } from 'antd'
-import { useNavigate } from 'react-router-dom'
-import { loginByPasswordApi, type loginResType } from '@/apis/login'
-import type { loginType } from '@/type/login'
-import { useMessageStore } from '@/store/useMessageStore'
-import { loginByWechat } from '@/apis/login';
-import GiteeLogin from '../GiteeLogin'
+} from '@ant-design/icons';
+import { Avatar, Badge, Modal, Popover } from 'antd';
+import './index.css';
+import { getToken, removeToken, removeUserinfo, getUserinfo } from '@/utils';
+import { useEffect, useState } from 'react';
+import { useMessageStore } from '@/store/useMessageStore';
+import LoginContent from '../../../LoginContent';
+
 const Wy_Avatar = () => {
-  const [userinfo, setUserinfoFn] = useState<any>(null); // 明确指定状态类型
+  const [userinfo, setUserinfoFn] = useState<any>(null);
   const token = getToken();
-  // 在 Wy_Avatar 组件中添加事件监听
+  // 初始化时检查用户信息
+  const initUserInfo = () => {
+    const currentToken = getToken();
+    if (currentToken) {
+      setUserinfoFn(getUserinfo());
+    } else {
+      setUserinfoFn(null);
+    }
+  };
   useEffect(() => {
-    // 监听自定义事件,当其他组件更新了用户信息时触发
+    initUserInfo();
     const handleUserUpdate = () => {
       const currentToken = getToken();
       if (currentToken) {
@@ -58,98 +46,30 @@ const Wy_Avatar = () => {
       window.removeEventListener('user-info-updated', handleUserUpdate);
     };
   }, []);
-  const [isModalOpen, setIsModalOpen] = useState(false)
-  const message = useMessageStore((state) => state.message)
-  const setMessage = useMessageStore((state) => state.setMessage)
+
+  const [isModalOpen, setIsModalOpen] = useState(false);
+  const message = useMessageStore((state) => state.message);
+  const setMessage = useMessageStore((state) => state.setMessage);
+
   useEffect(() => {
     if (message) {
-      showModal()
-      setMessage(false)
+      showModal();
+      setMessage(false);
     }
-  }, [message])
+  }, [message]);
 
   const showModal = () => {
-    setIsModalOpen(true)
-  }
+    setIsModalOpen(true);
+  };
 
   const handleOk = () => {
-    setIsModalOpen(false)
-  }
-
-  const handleCancel = () => {
-    setIsModalOpen(false)
-  }
-
-  // 添加微信登录相关状态
-  const [isWechatLogin, setIsWechatLogin] = useState(false);
-
-  // 微信登录处理函数
-  const handleWechatLogin = () => {
-    setIsWechatLogin(true);
-    // 初始化微信登录
-    initWechatLogin();
+    setIsModalOpen(false);
   };
-  // 初始化微信登录
-  const initWechatLogin = () => {
-    // 动态加载微信登录JS
-    const script = document.createElement('script');
-    script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js';
-    script.onload = () => {
-      new WxLogin({
-        self_redirect: false,
-        id: 'wechat_login_container',
-        appid: 'wxbdc5610cc59c1631',
-        scope: 'snsapi_login',
-        redirect_uri: encodeURIComponent('http://baidu.com'),
-        state: generateState(),
-        style: 'black',
-        href: ''
-      })
-    }
-    document.body.appendChild(script);
-  }
 
-  // 生成随机state参数
-  const generateState = () => {
-    return Math.random().toString(36).substring(2, 15) +
-      Math.random().toString(36).substring(2, 15);
+  const handleCancel = () => {
+    setIsModalOpen(false);
   };
 
-  // 处理微信登录回调
-  useEffect(() => {
-    const handleMessage = (event: MessageEvent) => {
-      if (event.data && event.data.type === 'wechat_login') {
-        // 处理微信登录返回的code
-        handleWechatCode(event.data.code, event.data.state);
-      }
-    };
-
-    window.addEventListener('message', handleMessage);
-    return () => {
-      window.removeEventListener('message', handleMessage);
-    };
-  }, []);
-
-  const handleWechatCode = async (code: string, state: string) => {
-    try {
-      // 使用封装的 request 工具调用后端API处理微信登录
-      const response = await loginByWechat({ code, state });
-      const res: loginResType = response.data;
-
-      if (res && res.access_token) {
-        // 登录成功,保存token
-        setToken(res);
-        setUserinfo(response.access_token)
-        setIsWechatLogin(false);
-        setIsModalOpen(false);
-        window.dispatchEvent(new CustomEvent('user-info-updated'));
-      } else {
-        console.error('微信登录失败: 响应数据不完整');
-      }
-    } catch (error) {
-      console.error('微信登录错误:', error);
-    }
-  };
   const content = () => {
     return (
       <div className="Avatar_content">
@@ -176,403 +96,29 @@ const Wy_Avatar = () => {
         </div>
         <div
           onClick={() => {
-            removeUserinfo()
-            removeToken()
-            window.location.reload()
+            removeUserinfo();
+            removeToken();
+            window.location.reload();
           }}
         >
           <LogoutOutlined /> 退出
         </div>
       </div>
-    )
-  }
-
-  const LoginModal = () => {
-    const [isChecked, setIsChecked] = useState(false)
-    const [showCustomAlert, setShowCustomAlert] = useState(false)
-    const [type, setType] = useState(0)
-    const onChange: CheckboxProps['onChange'] = (e) => {
-      setIsChecked(e.target.checked)
-    }
-    const handleLogin = () => {
-      if (!isChecked) {
-        setShowCustomAlert(true)
-        return
-      }
-      setType(1)
-    }
-    const CustomAlert = () => {
-      return (
-        <div className="custom-alert" onClick={() => setShowCustomAlert(false)}>
-          <div className="custom-alert-content">
-            <p>请先同意服务条款和隐私政策</p>
-          </div>
-        </div>
-      )
-    }
-    type FieldType = {
-      username?: string
-      password?: string
-      remember?: string
-    }
-
-    const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
-      console.log('Success:', values)
-    }
-    const LoginByPassword: FormProps<loginType>['onFinish'] = async (
-      values
-    ) => {
-      try {
-        const res = await loginByPasswordApi(values);
-        // 判断登录是否成功
-        if (res && res.access_token) {
-          // 登录成功处理
-          console.log(res);
-          // 保存 token 和用户信息
-          setToken(res)
-          setUserinfo(res.access_token)
-          setIsModalOpen(false)
-          // 刷新页面
-          // window.location.reload()
-        } else {
-          // 登录失败处理
-          console.log('登录失败');
-        }
-      } catch (error) {
-        // 网络错误或异常处理
-        console.error('登录请求失败:', error);
-      }
-    }
-
-    const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (
-      errorInfo
-    ) => {
-      console.log('Failed:', errorInfo)
-    }
-    const options = [
-      { value: '+86', label: '中国大陆+86' },
-      { value: '+852', label: '香港+852' },
-      { value: '+853', label: '澳门+853' },
-      { value: '+886', label: '台湾+886' },
-      { value: '+82', label: '韩国+82' },
-      { value: '+81', label: '日本+81' },
-      { value: '+65', label: '新加坡+65' },
-      { value: '+91', label: '印度+91' },
-      { value: '+60', label: '马来西亚+60' },
-      { value: '+44', label: '英国+44' },
-      { value: '+33', label: '法国+33' },
-      { value: '+49', label: '德国+49' },
-      { value: '+39', label: '意大利+39' },
-      { value: '+34', label: '西班牙+34' },
-    ]
-    const navigator = useNavigate()
-    return (
-      <div>
-        {showCustomAlert && <CustomAlert />}
-        {isWechatLogin ? (
-          <Modal
-            title="微信扫码登录"
-            open={isModalOpen}
-            onCancel={() => {
-              setIsWechatLogin(false);
-              setIsModalOpen(false);
-            }}
-            footer={null}
-            width={530}
-          >
-            <div style={{ textAlign: 'center', padding: '20px 0' }}>
-              <div id="wechat_login_container"></div>
-              <p style={{ marginTop: '20px', color: '#999' }}>请使用微信扫描二维码登录</p>
-            </div>
-            <div
-              style={{
-                color: 'rgb(56, 157, 252)',
-                cursor: 'pointer',
-                textAlign: 'center',
-                marginTop: '20px'
-              }}
-              onClick={() => {
-                setIsWechatLogin(false);
-              }}
-            >
-              {'<'}其他登录方式
-            </div>
-          </Modal>
-        ) : type === 0 ? (
-          <Modal
-            title="登录"
-            open={isModalOpen}
-            onOk={handleOk}
-            onCancel={handleCancel}
-            footer={null}
-            width={530}
-          >
-            <div className="login_content">
-              <div className="login_content_item">
-                <div className="login_content_left">
-                  <div className="login_content_left_top">
-                    <Image
-                      width={224}
-                      src={
-                        'https://web-asd-asd.oss-cn-beijing.aliyuncs.com/login.png'
-                      }
-                    />
-                  </div>
-                  <div
-                    className="login_content_left_bottom"
-                    style={{ cursor: 'pointer' }}
-                  >
-                    <div onClick={handleLogin}>手机号登录/注册</div>
-                  </div>
-                </div>
-                <div className="login_content_right">
-                  <div className="login_content_right_item" onClick={handleWechatLogin}>
-                    <div
-                      className="login_content_right_item_icon"
-                      style={{ color: 'rgb(105, 183, 53)' }}
-                    >
-                      <WechatOutlined />
-                    </div>
-                    <div className="login_content_right_item_text"
-
-                    >
-                      微信登陆
-                    </div>
-                  </div>
-                  <div className="login_content_right_item">
-                    <div
-                      className="login_content_right_item_icon"
-                      style={{ color: 'rgb(52, 161, 222)' }}
-                    >
-                      <QqOutlined />
-                    </div>
-                    <div className="login_content_right_item_text">QQ登陆</div>
-                  </div>
-                  <div className="login_content_right_item">
-                    <div className="login_content_right_item_icon">
-                      <WeiboCircleOutlined
-                        style={{ color: 'rgb(233, 77, 69)' }}
-                      />
-                    </div>
-                    <div className="login_content_right_item_text">
-                      微博登录
-                    </div>
-                  </div>
-                  <div className="login_content_right_item">
-                    <div
-                      className="login_content_right_item_icon"
-                      style={{ color: 'rgb(213, 71, 63)', fontSize: '20px' }}
-                    >
-                      易
-                    </div>
-                    <div className="login_content_right_item_text">
-                      网易邮箱账号登录
-                    </div>
-                  </div>
-                  <div className="login_content_right_item">
-                    <div
-                      className="login_content_right_item_icon"
-                      style={{ color: 'rgb(51, 51, 51)', fontSize: '20px' }}
-                    >
-                      <GithubOutlined />
-                    </div>
-                    <div className="login_content_right_item_text">
-                      <GiteeLogin onSuccess={(userInfo) => {
-                        console.log('Gitee 登录成功:', userInfo)
-                        // 处理登录成功的逻辑
-                        setIsModalOpen(false)
-                      }} />
-                    </div>
-                  </div>
-                </div>
-              </div>
-              <div className="login_protocol">
-                <Checkbox onChange={onChange} checked={isChecked}>
-                  同意{' '}
-                </Checkbox>
-                <span className="protocol_span">
-                  《服务条款》《隐私政策》《儿童隐私政策》
-                </span>
-              </div>
-            </div>
-          </Modal>
-        ) : type === 1 ? (
-          <Modal
-            title="手机号登录"
-            open={isModalOpen}
-            onOk={handleOk}
-            onCancel={handleCancel}
-            footer={null}
-            width={530}
-          >
-            <div className="yzm_login_content">
-              <div style={{ marginTop: '60px' }}>
-                <Form
-                  className="yzm_login_content_form"
-                  name="basic"
-                  initialValues={{ remember: true }}
-                  onFinish={onFinish}
-                  onFinishFailed={onFinishFailed}
-                  autoComplete="off"
-                >
-                  <Form.Item<FieldType>
-                    name="username"
-                    rules={[{ required: true, message: '请输入手机号!' }]}
-                  >
-                    <Space.Compact>
-                      <Select
-                        style={{ width: '180px' }}
-                        defaultValue="+86"
-                        options={options}
-                      />
-                      <Input placeholder="请输入手机号"></Input>
-                    </Space.Compact>
-                  </Form.Item>
-
-                  <Form.Item<FieldType>
-                    name="password"
-                    rules={[{ required: true, message: '请输入验证码!' }]}
-                  >
-                    <div style={{ display: 'flex' }}>
-                      <Input placeholder="请输入验证码"></Input>
-                      <Button type="primary" shape="round" danger>
-                        获取验证码
-                      </Button>
-                    </div>
-                  </Form.Item>
-
-                  <Form.Item>
-                    <Button
-                      type="primary"
-                      block
-                      shape="round"
-                      danger
-                      htmlType="submit"
-                    >
-                      登录
-                    </Button>
-                  </Form.Item>
-                  <div
-                    style={{
-                      textAlign: 'left',
-                      color: 'rgb(145, 142, 142)',
-                      cursor: 'pointer',
-                    }}
-                    onClick={() => {
-                      setType(2)
-                    }}
-                  >
-                    密码登录
-                  </div>
-                </Form>
-              </div>
-            </div>
-            <div
-              style={{ color: 'rgb(56, 157, 252)', cursor: 'pointer' }}
-              onClick={() => setType(0)}
-            >
-              {'<'}其他登录方式
-            </div>
-          </Modal>
-        ) : (
-          <Modal
-            title="密码登录"
-            open={isModalOpen}
-            onOk={handleOk}
-            onCancel={handleCancel}
-            footer={null}
-            width={530}
-          >
-            <div className="yzm_login_content">
-              <div style={{ marginTop: '60px' }}>
-                <Form
-                  className="yzm_login_content_form"
-                  name="basic"
-                  initialValues={{ remember: true }}
-                  onFinish={LoginByPassword}
-                  onFinishFailed={onFinishFailed}
-                  autoComplete="off"
-                >
-                  <Form.Item<FieldType>
-                    name="username"
-                    rules={[{ required: true, message: '请输入手机号!' }]}
-                  >
-                    <Space.Compact>
-                      <Select
-                        style={{ width: '180px' }}
-                        defaultValue="+86"
-                        options={options}
-                      />
-                      <Input placeholder="请输入手机号"></Input>
-                    </Space.Compact>
-                  </Form.Item>
-
-                  <Form.Item<FieldType>
-                    name="password"
-                    rules={[{ required: true, message: '请输入密码!' }]}
-                  >
-                    <div style={{ display: 'flex' }}>
-                      <Input
-                        placeholder="请输入密码"
-                        suffix={
-                          <span
-                            style={{
-                              color: 'rgb(56, 157, 252)',
-                              cursor: 'pointer',
-                            }}
-                            onClick={() => {
-                              navigator('/login/yzm')
-                            }}
-                          >
-                            忘记密码?
-                          </span>
-                        }
-                      />
-                    </div>
-                  </Form.Item>
-
-                  <Form.Item>
-                    <Button
-                      type="primary"
-                      block
-                      shape="round"
-                      danger
-                      htmlType="submit"
-                    >
-                      登录
-                    </Button>
-                  </Form.Item>
-                  <div
-                    style={{
-                      color: 'rgb(56, 157, 252)',
-                      cursor: 'pointer',
-                      textAlign: 'left',
-                    }}
-                    onClick={() => setType(0)}
-                  >
-                    {'<'}其他登录方式
-                  </div>
-                </Form>
-              </div>
-            </div>
-          </Modal>
-        )}
-      </div>
-    )
-  }
+    );
+  };
 
   return (
     <>
       {token ? (
         <div>
-          <Popover content={content} color='rgb(53, 53, 53)'>
+          <Popover content={content} color="rgb(53, 53, 53)">
             <Badge count={1}>
               <Avatar
                 size="large"
                 src={
                   userinfo && userinfo.url
                     ? userinfo.url
-                    : "https://tse1-mm.cn.bing.net/th/id/OIP-C.7GLMYPqMlt2LgkbPsOnDIAAAAA?rs=1&pid=ImgDetMain"
+                    : 'https://tse1-mm.cn.bing.net/th/id/OIP-C.7GLMYPqMlt2LgkbPsOnDIAAAAA?rs=1&pid=ImgDetMain'
                 }
               />
             </Badge>
@@ -584,9 +130,21 @@ const Wy_Avatar = () => {
         </div>
       )}
 
-      <LoginModal />
+      <Modal
+        title="登录"
+        open={isModalOpen}
+        onOk={handleOk}
+        onCancel={handleCancel}
+        footer={null}
+        width={530}
+      >
+        <LoginContent
+          onLoginSuccess={handleOk}
+
+        />
+      </Modal>
     </>
-  )
-}
+  );
+};
 
-export default Wy_Avatar
+export default Wy_Avatar;

+ 13 - 4
src/pages/layout/components/Wy_Header/index.tsx

@@ -1,10 +1,18 @@
 import Wy_Avatar from './components/Wy_Avatar'
-import Wy_Search from './components/Wy_Search'
+import CreatorCenter from './components/CreatorCenter'
 import Wy_Tabs from './components/Wy_Tabs'
 import './index.css'
 import { SearchOutlined } from '@ant-design/icons'
 import { Input } from 'antd'
+import { useNavigate } from 'react-router-dom'
 const Wy_Header = () => {
+  const navigate = useNavigate();
+
+  const handleSearch = (value: string) => { 
+    // 这里可以添加搜索逻辑
+    console.log('搜索:', value);
+    navigate(`/search?type=song&value=${encodeURIComponent(value)}`);
+  };
   return (
     <div>
       <div className="wy_header_content">
@@ -16,17 +24,18 @@ const Wy_Header = () => {
           <Input
             placeholder="音乐/视频/电台/用户"
             prefix={<SearchOutlined />}
+            onPressEnter={(e) => handleSearch(e.target.value)}
           />
         </div>
         <div className='button'>
-          <Wy_Search/>
+          <CreatorCenter />
         </div>
         <div className='Avatar'>
-          <Wy_Avatar/>
+          <Wy_Avatar />
         </div>
       </div>
       <div className='Divider'>
-        
+
       </div>
     </div>
   )

+ 12 - 0
src/pages/layout/index.css

@@ -12,3 +12,15 @@
   border-top: 1px solid #ababab;
   height: 320px;
 }
+.video-player video {
+  width: 100%;
+  height: auto;
+  max-width: 100%;
+  max-height: 80vh; /* 限制最大高度为视口的80% */
+  object-fit: contain; /* 保持宽高比,防止变形 */
+}
+.video-container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 20px;
+}

+ 6 - 4
src/pages/layout/index.tsx

@@ -6,6 +6,7 @@ import Wy_Content from './components/Wy_Content'
 import Wy_Footer from './components/Wy_Footer'
 import { ConfigProvider } from 'antd'
 import type { ThemeConfig } from 'antd/es/config-provider/context'
+import MusicPlayer from './pages/testPage'
 
 // 定义红色主题
 const theme: ThemeConfig = {
@@ -20,18 +21,19 @@ const WYLayout = () => {
       <Flex gap="middle" wrap>
         <Layout>
           <Header className='Layout-Header'>
-            <Wy_Header/>
+            <Wy_Header />
           </Header>
           <Content>
-            <Wy_Content/>
+            <Wy_Content />
           </Content>
           <Footer className='Layout-Footer'>
-            <Wy_Footer/>
+            <Wy_Footer />
           </Footer>
         </Layout>
       </Flex>
+      {/* 浮窗播放条 */}
+      <MusicPlayer />
     </ConfigProvider>
   )
 }
-
 export default WYLayout

+ 22 - 0
src/pages/layout/pages/LoginPage/index.css

@@ -0,0 +1,22 @@
+.login-page {
+  width: 980px;
+  margin: 0 auto;
+  border-right: 1px solid #ddd;
+  border-left: 1px solid #ddd;
+  padding-top: 50px;
+  padding-bottom: 300px;
+}
+.login-page .login-content {
+  width: 540px;
+  margin: 0 auto;
+  text-align: center;
+}
+.login-page .login-content .login-content-title {
+  padding-bottom: 20px;
+  font-size: 30px;
+  font-weight: bolder;
+}
+.login-page .login-content .login-content-content {
+  border: 1px solid #ddd;
+  padding: 30px 10px 10px 30px;
+}

+ 26 - 0
src/pages/layout/pages/LoginPage/index.less

@@ -0,0 +1,26 @@
+.login-page {
+  width: 980px;
+  margin: 0 auto;
+  border-right: 1px solid #ddd;
+  border-left: 1px solid #ddd;
+  padding-top: 50px;
+  padding-bottom: 300px;
+
+  .login-content {
+    width: 540px;
+    margin: 0 auto;
+    text-align: center;
+
+
+    .login-content-title {
+      padding-bottom: 20px;
+      font-size: 30px;
+      font-weight: bolder;
+    }
+
+    .login-content-content {
+      border: 1px solid #ddd;
+      padding: 30px 10px 10px 30px;
+    }
+  }
+}

+ 28 - 0
src/pages/layout/pages/LoginPage/index.tsx

@@ -0,0 +1,28 @@
+// src/pages/layout/pages/LoginPage/index.tsx
+import { useNavigate } from 'react-router-dom';
+import LoginContent from '../../components/LoginContent';
+import './index.css';
+
+const LoginPage = () => {
+  const navigate = useNavigate();
+
+  const handleLoginSuccess = () => {
+    // 登录成功后跳转到首页或其他页面
+    navigate('/');
+  };
+
+  return (
+    <div className="login-page">
+      <div className="login-content">
+        <div className="login-content-title">请用你的云音乐账号登录</div>
+        <div className="login-content-content">
+          <LoginContent
+            onLoginSuccess={handleLoginSuccess}
+          />
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default LoginPage;

+ 16 - 0
src/pages/layout/pages/Search/components/SearchTabs/index.css

@@ -0,0 +1,16 @@
+/* src/pages/layout/components/SearchTabs/index.css */
+.search-tabs .ant-tabs-nav .ant-tabs-tab {
+  padding: 0 16px;
+  height: 40px;
+  line-height: 40px;
+  border-bottom: 2px solid transparent;
+}
+
+.search-tabs .ant-tabs-nav .ant-tabs-tab-active {
+  border-bottom-color: #ff4d4f;
+  color: #ff4d4f;
+}
+
+.search-tabs .ant-tabs-nav .ant-tabs-tab:hover {
+  color: #ff4d4f;
+}

+ 30 - 0
src/pages/layout/pages/Search/components/SearchTabs/index.tsx

@@ -0,0 +1,30 @@
+import React from 'react';
+import { Tabs } from 'antd';
+import './index.css';
+
+const SearchTabs: React.FC<{ onTabChange: (key: string) => void }> = ({ onTabChange }) => {
+  const items = [
+    { key: 'song', label: '单曲' },
+    { key: 'artist', label: '歌手' },
+    { key: 'album', label: '专辑' },
+    { key: 'video', label: '视频' },
+    { key: 'lyric', label: '歌词' },
+    { key: 'playlist', label: '歌单' },
+    { key: 'user', label: '用户' },
+  ];
+
+  const onChange = (key: string) => {
+    onTabChange(key);
+  };
+
+  return (
+    <Tabs
+      defaultActiveKey="song"
+      items={items}
+      onChange={onChange}
+      className="search-tabs"
+    />
+  );
+};
+
+export default SearchTabs;

+ 48 - 0
src/pages/layout/pages/Search/index.css

@@ -0,0 +1,48 @@
+/* src/pages/layout/pages/find/search/index.css */
+.search-page {
+  padding: 20px;
+  width: 980px;
+  margin: 0 auto;
+}
+
+.search-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.search-header input {
+  flex: 1;
+  padding: 10px;
+  border: 1px solid #ccc;
+  border-radius: 4px 0 0 4px;
+}
+
+.search-header button {
+  padding: 10px 20px;
+  background-color: #ff4d4f;
+  color: white;
+  border: none;
+  border-radius: 0 4px 4px 0;
+  cursor: pointer;
+}
+
+.search-results {
+  margin-top: 20px;
+}
+
+.search-results-list {
+  list-style: none;
+  padding: 0;
+}
+
+.result-item {
+  display: flex;
+  justify-content: space-between;
+  padding: 10px 0;
+  border-bottom: 1px solid #eee;
+}
+
+.result-item span {
+  margin-right: 10px;
+}

+ 53 - 0
src/pages/layout/pages/Search/index.tsx

@@ -0,0 +1,53 @@
+// src/pages/layout/pages/find/search/index.tsx
+import React, { useState } from 'react';
+import './index.css';
+import SearchTabs from './components/SearchTabs';
+const Search: React.FC = () => {
+  const [selectedType, setSelectedType] = useState('song');
+  const handleTabChange = (key: string) => {
+    setSelectedType(key);
+    // 这里可以添加其他逻辑,例如发送请求获取对应类型的搜索结果
+  };
+
+  return (
+    <div className="search-page">
+      <div className="search-header">
+        <input type="text" placeholder="搜索..." defaultValue="游京" />
+        <button>搜索</button>
+      </div>
+
+      <SearchTabs onTabChange={handleTabChange} />
+
+      <div className="search-results">
+        {/* 根据 selectedType 显示相应的搜索结果 */}
+        {selectedType === 'song' && (
+          <div className="search-results-list">
+            {/* 单曲搜索结果 */}
+            <div className="result-item">
+              <span>《游京》(我司看饼香)</span>
+              <span>DJ阿农</span>
+              <span>《游京》(我司看饼香)</span>
+              <span>03:38</span>
+            </div>
+            {/* 更多单曲结果... */}
+          </div>
+        )}
+
+        {selectedType === 'artist' && (
+          <div className="search-results-list">
+            {/* 歌手搜索结果 */}
+            <div className="result-item">
+              <span>DJ阿农</span>
+              <span>歌手</span>
+            </div>
+            {/* 更多歌手结果... */}
+          </div>
+        )}
+
+        {/* 其他类型的搜索结果... */}
+      </div>
+    </div>
+  );
+};
+
+export default Search;

+ 106 - 0
src/pages/layout/pages/SongDetail/components/SongDetailPage.css

@@ -0,0 +1,106 @@
+.song-detail-container {
+  display: flex;
+  gap: 40px;
+  padding: 30px;
+  background: #fff;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+}
+.song-detail-container .cover-section {
+  flex: 1;
+  text-align: center;
+}
+.song-detail-container .cover-section .album-cover {
+  width: 300px;
+  height: 300px;
+  border-radius: 50%;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+  margin-bottom: 20px;
+  object-fit: cover;
+}
+.song-detail-container .cover-section .actions {
+  display: flex;
+  gap: 10px;
+  justify-content: center;
+  margin-bottom: 15px;
+}
+.song-detail-container .cover-section .actions button {
+  padding: 8px 16px;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+}
+.song-detail-container .cover-section .actions .play-btn {
+  background: #007bff;
+  color: white;
+}
+.song-detail-container .cover-section .actions .add-to-playlist-btn {
+  background: #f0f0f0;
+  color: #333;
+}
+.song-detail-container .cover-section .external-player-link {
+  color: #007bff;
+  text-decoration: none;
+  margin: 10px 0;
+  font-size: 14px;
+}
+.song-detail-container .cover-section .open-client-btn {
+  padding: 10px 20px;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  background: #f9f9f9;
+  cursor: pointer;
+  font-size: 14px;
+  margin-top: 10px;
+}
+.song-detail-container .info-section {
+  flex: 2;
+  padding: 20px;
+}
+.song-detail-container .info-section .title-section {
+  margin-bottom: 10px;
+}
+.song-detail-container .info-section .tag {
+  background: #e74c3c;
+  color: white;
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  margin-right: 8px;
+}
+.song-detail-container .info-section .artist-info,
+.song-detail-container .info-section .album-info {
+  margin: 8px 0;
+  color: #666;
+}
+.song-detail-container .info-section .action-buttons {
+  display: flex;
+  gap: 10px;
+  margin: 20px 0;
+}
+.song-detail-container .info-section .action-buttons .btn-play {
+  background: #007bff;
+  color: white;
+  border: none;
+  padding: 8px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.song-detail-container .info-section .action-buttons .btn-favorite,
+.song-detail-container .info-section .action-buttons .btn-share,
+.song-detail-container .info-section .action-buttons .btn-download,
+.song-detail-container .info-section .action-buttons .btn-comment {
+  background: #f0f0f0;
+  border: 1px solid #ccc;
+  padding: 8px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.song-detail-container .info-section .action-buttons .btn-comment span {
+  margin-right: 5px;
+}
+.song-detail-container .info-section .music-info {
+  margin-top: 20px;
+  color: #666;
+  line-height: 1.6;
+}

+ 125 - 0
src/pages/layout/pages/SongDetail/components/SongDetailPage.less

@@ -0,0 +1,125 @@
+// src/pages/layout/pages/testPage/song-detail.less
+
+.song-detail-container {
+  display: flex;
+  gap: 40px;
+  padding: 30px;
+  background: #fff;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+
+  .cover-section {
+    flex: 1;
+    text-align: center;
+
+    .album-cover {
+      width: 300px;
+      height: 300px;
+      border-radius: 50%;
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+      margin-bottom: 20px;
+      object-fit: cover;
+    }
+
+    .actions {
+      display: flex;
+      gap: 10px;
+      justify-content: center;
+      margin-bottom: 15px;
+
+      button {
+        padding: 8px 16px;
+        border: none;
+        border-radius: 4px;
+        cursor: pointer;
+        font-size: 14px;
+      }
+
+      .play-btn {
+        background: #007bff;
+        color: white;
+      }
+
+      .add-to-playlist-btn {
+        background: #f0f0f0;
+        color: #333;
+      }
+    }
+
+    .external-player-link {
+      color: #007bff;
+      text-decoration: none;
+      margin: 10px 0;
+      font-size: 14px;
+    }
+
+    .open-client-btn {
+      padding: 10px 20px;
+      border: 1px solid #ddd;
+      border-radius: 4px;
+      background: #f9f9f9;
+      cursor: pointer;
+      font-size: 14px;
+      margin-top: 10px;
+    }
+  }
+
+  .info-section {
+    flex: 2;
+    padding: 20px;
+
+    .title-section {
+      margin-bottom: 10px;
+    }
+
+    .tag {
+      background: #e74c3c;
+      color: white;
+      padding: 4px 8px;
+      border-radius: 4px;
+      font-size: 12px;
+      margin-right: 8px;
+    }
+
+    .artist-info,
+    .album-info {
+      margin: 8px 0;
+      color: #666;
+    }
+
+    .action-buttons {
+      display: flex;
+      gap: 10px;
+      margin: 20px 0;
+
+      .btn-play {
+        background: #007bff;
+        color: white;
+        border: none;
+        padding: 8px 16px;
+        border-radius: 4px;
+        cursor: pointer;
+      }
+
+      .btn-favorite,
+      .btn-share,
+      .btn-download,
+      .btn-comment {
+        background: #f0f0f0;
+        border: 1px solid #ccc;
+        padding: 8px 16px;
+        border-radius: 4px;
+        cursor: pointer;
+      }
+
+      .btn-comment span {
+        margin-right: 5px;
+      }
+    }
+
+    .music-info {
+      margin-top: 20px;
+      color: #666;
+      line-height: 1.6;
+    }
+  }
+}

+ 95 - 0
src/pages/layout/pages/SongDetail/components/SongDetailPage.tsx

@@ -0,0 +1,95 @@
+// src/pages/layout/pages/testPage/SongDetailPage.tsx
+import React, { useState } from 'react';
+import { UnorderedListOutlined, HeartOutlined, HeartTwoTone } from '@ant-design/icons';
+import './SongDetailPage.css';
+
+interface Song {
+  id: number;
+  title: string;
+  artist: string;
+  url: string;
+  cover: string;
+  duration: string;
+  isFavorite?: boolean;
+  album?: string;           // 所属专辑
+  composer?: string;        // 作曲
+  arranger?: string;        // 编曲
+  description?: string;     // 歌曲描述
+}
+
+const SongDetailPage: React.FC<{ song: Song }> = ({ song }) => {
+  const [isPlaying, setIsPlaying] = useState<boolean>(false);
+  const [isFavorite, setIsFavorite] = useState<boolean>(false);
+
+  // 模拟播放逻辑(可连接 AudioPlayer)
+  const handlePlay = () => {
+    setIsPlaying(true);
+    // 这里可以触发全局播放器播放该歌曲
+    console.log('播放歌曲:', song.title);
+  };
+
+  const handleAddToPlaylist = () => {
+    // 添加到播放列表并播放
+    console.log('添加到播放列表并播放:', song.title);
+    setIsPlaying(true);
+  };
+
+  const toggleFavorite = () => {
+    setIsFavorite(!isFavorite);
+  };
+
+  return (
+    <div className="song-detail-container">
+      {/* 左侧:封面 */}
+      <div className="cover-section">
+        <img src={song.cover} alt={song.title} className="album-cover" />
+        <div className="actions">
+          <button className="play-btn" onClick={handlePlay}>
+            <span>▶</span> 播放
+          </button>
+          <button className="add-to-playlist-btn" onClick={handleAddToPlaylist}>
+            +
+          </button>
+        </div>
+        <a href="#" className="external-player-link">生成外链播放器</a>
+        <button className="open-client-btn">点击打开客户端</button>
+      </div>
+
+      {/* 右侧:歌曲信息 */}
+      <div className="info-section">
+        <div className="title-section">
+          <span className="tag">单曲</span>
+          <h1>{song.title}</h1>
+        </div>
+        <div className="artist-info">
+          歌手:<a href="#">{song.artist}</a>
+        </div>
+        <div className="album-info">
+          所属专辑:<a href="#">{song.album || song.title}</a>
+        </div>
+        <div className="action-buttons">
+          <button className="btn-play" onClick={handlePlay}>
+            <span>▶</span> 播放
+          </button>
+          <button className="btn-favorite" onClick={toggleFavorite}>
+            {isFavorite ? <HeartTwoTone twoToneColor="#eb2f96" /> : <HeartOutlined />}
+            收藏
+          </button>
+          <button className="btn-share">分享</button>
+          <button className="btn-download">下载</button>
+          <button className="btn-comment">
+            <span>💬</span> (6112)
+          </button>
+        </div>
+
+        <div className="music-info">
+          <p>作曲:{song.composer || song.artist}</p>
+          <p>编曲:{song.arranger || song.artist}</p>
+          <p>纯音乐,请欣赏</p>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default SongDetailPage;

+ 6 - 0
src/pages/layout/pages/SongDetail/index.css

@@ -0,0 +1,6 @@
+.SongDetail {
+  width: 980px;
+  margin: 0 auto;
+  border-left: 1px solid #aaaaaa;
+  border-right: 1px solid #aaaaaa;
+}

+ 7 - 0
src/pages/layout/pages/SongDetail/index.less

@@ -0,0 +1,7 @@
+.SongDetail {
+  width: 980px;
+  margin: 0 auto;
+  border-left: 1px solid rgb(170, 170, 170);
+  border-right: 1px solid rgb(170, 170, 170);
+  // border-top: 1px solid rgb(170, 170, 170);
+}

+ 27 - 0
src/pages/layout/pages/SongDetail/index.tsx

@@ -0,0 +1,27 @@
+import Rank_Recommend_body_list_Comment from '../find/rank/compomemts/Rank_Recommend_body_list_Comment'
+import Rank_Recommend_body_list_Comment_list from '../find/rank/compomemts/Rank_Recommend_body_list_Comment_list'
+import SongDetailPage from './components/SongDetailPage';
+import './index.css'
+const SongDetail = () => {
+  const song = {
+    id: 4,
+    title: '忠诚 loyalty(PHONK)',
+    artist: 'chao / BOY / KKK',
+    url: 'http://117.72.120.45:9000/testbucket/123.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '03:45',
+    album: '忠诚 loyalty(PHONK)',
+    composer: 'chao/BOY/KKK',
+    arranger: 'chao/BOY/KKK',
+    description: '纯音乐,请欣赏'
+  };
+
+  return (
+    <div className='SongDetail'>
+      <SongDetailPage song={song} />
+      <Rank_Recommend_body_list_Comment />
+      <Rank_Recommend_body_list_Comment_list />
+    </div>
+  )
+}
+export default SongDetail

+ 0 - 0
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment/indes.css → src/pages/layout/pages/Video/Video_player/index.css


+ 0 - 0
src/pages/layout/pages/Video/Video_player/index.less


+ 58 - 0
src/pages/layout/pages/Video/Video_player/index.tsx

@@ -0,0 +1,58 @@
+import React, { useEffect, useRef } from 'react';
+import DPlayer from 'dplayer';
+import './index.css';
+const Video_Player = () => {
+  // 视频地址(示例)
+  const videoUrl = 'http://117.72.120.45:9000/testbucket/123.mp4';
+  const dpContainer = useRef<HTMLDivElement>(null);
+  const dp = useRef<DPlayer | null>(null);
+  useEffect(() => {
+    if (dpContainer.current) {
+      dp.current = new DPlayer({
+        container: dpContainer.current,
+        video: {
+          url: videoUrl,
+          pic: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+          type: 'auto'
+        },
+        autoplay: false,
+        theme: '#b74747',
+        loop: false,
+        lang: 'zh-cn',
+        screenshot: false,
+        hotkey: true,
+        preload: 'metadata',
+        volume: 0.8,
+        mutex: true,
+        height: 300 // 添加固定高度
+      });
+    }
+
+    return () => {
+      if (dp.current) {
+        dp.current.destroy();
+      }
+    };
+  }, []);
+
+  return (
+    <div className="video-container">
+      {/* 视频标题和作者 */}
+      <div className="video-header">
+        <h1>【红茶】LOL:钻石宗师局羊刀破败强攻翠神</h1>
+        <span className="author">by <a href="#">乱斗王红茶zz</a></span>
+      </div>
+
+      <div className="video-player">
+        <div ref={dpContainer} style={{ height: '400px' }} />
+      </div>
+
+      {/* 版权提示 */}
+      <div className="copyright"> 
+        <span>未经作者授权,禁止转载</span>
+      </div>
+    </div>
+  );
+};
+
+export default Video_Player;

+ 6 - 0
src/pages/layout/pages/Video/index.css

@@ -0,0 +1,6 @@
+.video {
+  width: 780px;
+  margin: 0 auto ;
+  border-left: 1px solid #aaaaaa;
+  border-right: 1px solid #aaaaaa;
+}

+ 6 - 0
src/pages/layout/pages/Video/index.less

@@ -0,0 +1,6 @@
+.video{
+  width: 780px;
+  margin:0 auto ;
+  border-left: 1px solid rgb(170, 170, 170);
+  border-right: 1px solid rgb(170, 170, 170);
+}

+ 14 - 0
src/pages/layout/pages/Video/index.tsx

@@ -0,0 +1,14 @@
+import Rank_Recommend_body_list_Comment from '../find/rank/compomemts/Rank_Recommend_body_list_Comment'
+import Rank_Recommend_body_list_Comment_list from '../find/rank/compomemts/Rank_Recommend_body_list_Comment_list'
+import './index.css'
+import Video_Player from './Video_player'
+const Video = () => {
+  return (
+  <div className="video">
+    <Video_Player></Video_Player>
+    <Rank_Recommend_body_list_Comment />
+    <Rank_Recommend_body_list_Comment_list />
+  </div>
+  )
+}
+export default Video

+ 21 - 0
src/pages/layout/pages/artistinfo/Albums/Albums.tsx

@@ -0,0 +1,21 @@
+import '../index.css'
+const Albums = () => {
+  const albums = [
+    { id: 1, title: 'We Are One', date: '2023.7.24', cover: 'https://...' },
+    { id: 2, title: '离别总是那么突然', date: '2023.3.24', cover: 'https://...' },
+  ]
+  return (
+    <div className="albums">
+      <div className="album-grid">
+        {albums.map((album) => (
+          <div key={album.id} className="album-item">
+            <img src={album.cover} alt={album.title} />
+            <div>{album.title}</div>
+            <div>{album.date}</div>
+          </div>
+        ))}
+      </div>
+    </div>
+  )
+}
+export default Albums

+ 23 - 0
src/pages/layout/pages/artistinfo/Bio/Bio.tsx

@@ -0,0 +1,23 @@
+import '../index.css'
+
+const Bio = () => {
+  return (
+    <div className="bio">
+      <h2>张惠妹简介</h2>
+      <p>
+        张惠妹(aMEI),1972年8月9日出生于中国台湾台东县卑南乡泰安部落,卑南族原住民,中国台湾流行乐女歌手。
+        1996年签约丰华唱片,发行首张专辑《姐妹》,销售量超过108万张,打破96年台湾本地歌手的销售纪录。
+        1997年推出第二张专辑《BAD BOY》,累计销售135万张,打破97年台湾女歌手销售纪录。
+        ...
+      </p>
+      <h3>代表作品</h3>
+      <p>姐妹、Bad Boy、听海、我可以抱你吗、解脱、如果你也听说、牵手、趁早、记得、我要快乐、我最亲爱的</p>
+      <h3>主要成就</h3>
+      <ul>
+        <li>第21届台湾金曲奖年度最佳歌曲奖</li>
+        <li>三届台湾金曲奖最佳国语女歌手奖</li>
+      </ul>
+    </div>
+  )}
+export default Bio
+ 

+ 35 - 0
src/pages/layout/pages/artistinfo/HotWorks/HotWorks.tsx

@@ -0,0 +1,35 @@
+import '../index.css'
+
+const HotWorks = () => {
+  const songs = [
+    { id: 1, title: '张惠妹', duration: '03:20', album: '专辑名称' },
+    { id: 2, title: '听海', duration: '04:11', album: '专辑名称' },
+  ]
+
+  return (
+    <div className="hot-works">
+      <table>
+        <thead>
+          <tr>
+            <th>#</th>
+            <th>歌曲</th>
+            <th>时长</th>
+            <th>专辑</th>
+          </tr>
+        </thead>
+        <tbody>
+          {songs.map((song) => (
+            <tr key={song.id}>
+              <td>{song.id}</td>
+              <td>{song.title}</td>
+              <td>{song.duration}</td>
+              <td>{song.album}</td>
+            </tr>
+          ))}
+        </tbody>
+      </table>
+    </div>
+  )
+}
+
+export default HotWorks

+ 23 - 0
src/pages/layout/pages/artistinfo/MVs/MVs.tsx

@@ -0,0 +1,23 @@
+import '../index.css'
+
+const MVs = () => {
+  const mvs = [
+    { id: 1, title: '离别总是那么突然', cover: 'https://...' },
+    { id: 2, title: '对等关系', cover: 'https://...' },
+  ]
+
+  return (
+    <div className="mvs">
+      <div className="mv-grid">
+        {mvs.map((mv) => (
+          <div key={mv.id} className="mv-item">
+            <img src={mv.cover} alt={mv.title} />
+            <div>{mv.title}</div>
+          </div>
+        ))}
+      </div>
+    </div>
+  )
+}
+
+export default MVs

+ 52 - 0
src/pages/layout/pages/artistinfo/index.css

@@ -0,0 +1,52 @@
+.artistinfo-container {
+  padding: 20px;
+  width: 710px;
+  font-family: 'Microsoft YaHei', sans-serif;
+  margin: 0 auto;
+  border-left: 1px solid #999999;
+  border-right: 1px solid #999999;
+}
+.artistinfo-container .artist-header {
+  margin-bottom: 20px;
+}
+.artistinfo-container .artist-header img {
+  width: 640px;
+  height: 300px;
+  object-fit: cover;
+}
+.artistinfo-container .artist-header .artist-info {
+  margin-left: 20px;
+  display: flex;
+}
+.artistinfo-container .artist-header .artist-info h1 {
+  font-size: 24px;
+  margin: 0;
+}
+.artistinfo-container .artist-header .artist-info p {
+  color: #666;
+  margin-top: 8px;
+}
+.artistinfo-container .tabs {
+  display: flex;
+  gap: 10px;
+  margin-bottom: 20px;
+  border-bottom: 1px solid #ddd;
+}
+.artistinfo-container .tabs button {
+  padding: 10px 20px;
+  background: none;
+  border: none;
+  cursor: pointer;
+  font-size: 14px;
+  transition: all 0.3s ease;
+}
+.artistinfo-container .tabs button:hover {
+  color: #ff4d4d;
+}
+.artistinfo-container .tabs button.active {
+  color: #ff4d4d;
+  border-bottom: 2px solid #ff4d4d;
+}
+.artistinfo-container .content {
+  min-height: 400px;
+}

+ 62 - 0
src/pages/layout/pages/artistinfo/index.less

@@ -0,0 +1,62 @@
+// src/pages/layout/pages/find/singer/artistinfo/index.less
+.artistinfo-container {
+  padding: 20px;
+  width: 710px;
+  font-family: 'Microsoft YaHei', sans-serif;
+  margin: 0 auto;
+  border-left: 1px solid rgb(153, 153, 153);
+  border-right: 1px solid rgb(153, 153, 153);
+  .artist-header {
+    margin-bottom: 20px;
+
+    img {
+      width: 640px;
+      height: 300px;
+      object-fit: cover;
+    }
+
+    .artist-info {
+      margin-left: 20px;
+      display: flex;
+
+      h1 {
+        font-size: 24px;
+        margin: 0;
+      }
+
+      p {
+        color: #666;
+        margin-top: 8px;
+      }
+    }
+  }
+
+  .tabs {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 20px;
+    border-bottom: 1px solid #ddd;
+
+    button {
+      padding: 10px 20px;
+      background: none;
+      border: none;
+      cursor: pointer;
+      font-size: 14px;
+      transition: all 0.3s ease;
+
+      &:hover {
+        color: #ff4d4d;
+      }
+
+      &.active {
+        color: #ff4d4d;
+        border-bottom: 2px solid #ff4d4d;
+      }
+    }
+  }
+
+  .content {
+    min-height: 400px;
+  }
+}

+ 63 - 0
src/pages/layout/pages/artistinfo/index.tsx

@@ -0,0 +1,63 @@
+// src/pages/layout/pages/find/singer/artistinfo/index.tsx
+import './index.css'
+import { useState } from 'react'
+import HotWorks from './HotWorks/HotWorks'
+import Albums from './Albums/Albums'
+import MVs from './MVs/MVs'
+import Bio from './Bio/Bio'
+
+const Artistinfo = () => {
+  const [activeTab, setActiveTab] = useState('hot')
+
+  return (
+    <div className="artistinfo-container">
+      {/* 头部区域 */}
+      <div className="artist-header">
+        <div className="artist-info">
+          <h1 >张惠妹</h1>
+          <p style={{paddingLeft:30,color:'rgb(153, 153, 153)'}}>中国台湾流行乐女歌手</p>
+        </div>
+        <img src="https://p2.music.126.net/53zsgOWFlGYS7bpwz92iNw==/109951168490195700.jpg?param=640y300" alt="艺人头像" />
+
+      </div>
+
+      {/* Tab 切换 */}
+      <div className="tabs">
+        <button
+          className={activeTab === 'hot' ? 'active' : ''}
+          onClick={() => setActiveTab('hot')}
+        >
+          热门作品
+        </button>
+        <button
+          className={activeTab === 'albums' ? 'active' : ''}
+          onClick={() => setActiveTab('albums')}
+        >
+          所有专辑
+        </button>
+        <button
+          className={activeTab === 'mvs' ? 'active' : ''}
+          onClick={() => setActiveTab('mvs')}
+        >
+          相关MV
+        </button>
+        <button
+          className={activeTab === 'bio' ? 'active' : ''}
+          onClick={() => setActiveTab('bio')}
+        >
+          艺人介绍
+        </button>
+      </div>
+
+      {/* 内容区域 */}
+      <div className="content">
+        {activeTab === 'hot' && <HotWorks />}
+        {activeTab === 'albums' && <Albums />}
+        {activeTab === 'mvs' && <MVs />}
+        {activeTab === 'bio' && <Bio />}
+      </div>
+    </div>
+  )
+}
+
+export default Artistinfo

+ 4 - 0
src/pages/layout/pages/find/album/index.css

@@ -0,0 +1,4 @@
+.Album {
+  width: 980px;
+  margin: 0 auto;
+}

+ 4 - 0
src/pages/layout/pages/find/album/index.less

@@ -0,0 +1,4 @@
+.Album{
+  width: 980px;
+  margin: 0 auto;
+}

+ 4 - 3
src/pages/layout/pages/find/album/index.tsx

@@ -1,8 +1,9 @@
+import Recommend_index from '../recommend/components/Recommend_body_left/components/Recommend_index'
 import './index.css'
-const album=()=>{
+const album = () => {
   return (
-    <div className="Mv">
-      album
+    <div className="Album">
+      <Recommend_index></Recommend_index>
     </div>
   )
 }

+ 0 - 12
src/pages/layout/pages/find/playlist/index.css

@@ -1,12 +0,0 @@
-.playlist-content {
-  width: 980px;
-  height: 1000px;
-}
-.playlist-content .playlist_left {
-  width: 710px;
-  background-color: #c33535;
-}
-.playlist-content .playlist_right {
-  width: 270px;
-  background-color: #302d2d;
-}

+ 0 - 12
src/pages/layout/pages/find/playlist/index.less

@@ -1,12 +0,0 @@
-.playlist-content {
-  width: 980px;
-  height: 1000px;
-  .playlist_left {
-    width: 710px;
-    background-color: #c33535;
-  }
-  .playlist_right {
-    width: 270px;
-    background-color: #302d2d;
-  }
-}

+ 0 - 12
src/pages/layout/pages/find/playlist/index.tsx

@@ -1,12 +0,0 @@
-import './index.css'
-const Playlist = () => {
-  return (
-    <div className="playlist">
-      <div className="playlist-content">
-        <div className="playlist_left"></div>
-        <div className="playlist_right"></div>
-      </div>
-    </div>
-  )
-}
-export default Playlist

+ 18 - 0
src/pages/layout/pages/find/rank/compomemts/RankPlaylist/index.tsx

@@ -0,0 +1,18 @@
+// src/pages/layout/pages/find/rank/components/RankPlaylist/index.tsx
+import Rank_Recommend_body_right from '../../compomemts/Rank_Recommend_body_right'
+import Rank_Recommend_body_list from '../../compomemts/Rank_Recommend_body_list'
+import Rank_Recommend_body_list_Comment from '../../compomemts/Rank_Recommend_body_list_Comment'
+import Rank_Recommend_body_list_Comment_list from '../../compomemts/Rank_Recommend_body_list_Comment_list'
+
+const RankPlaylist = () => {
+  return (
+    <div>
+      <Rank_Recommend_body_right />
+      <Rank_Recommend_body_list />
+      <Rank_Recommend_body_list_Comment />
+      <Rank_Recommend_body_list_Comment_list />
+    </div>
+  )
+}
+
+export default RankPlaylist

+ 2 - 0
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment/index.css

@@ -2,6 +2,7 @@
   margin: 30px 0 30px 40px;
 }
 .Rank_Recommend_body_list_Comment .Rank_Recommend_body_list_Comment_title {
+  margin-right: 30px;
   font-size: 20px;
   height: 30px;
   line-height: 30px;
@@ -16,6 +17,7 @@
   padding-left: 10px;
   /* 避免文字贴着箭头 */
   margin-left: 10px;
+  width: 600px;
 }
 .Rank_Recommend_body_list_Comment .Rank_Recommend_body_list_Comment_content .comment-box .Rank_Recommend_body_list_Comment_button {
   display: flex;

+ 2 - 0
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment/index.less

@@ -1,6 +1,7 @@
 .Rank_Recommend_body_list_Comment {
   margin: 30px 0 30px 40px;
   .Rank_Recommend_body_list_Comment_title {
+    margin-right: 30px;
     font-size: 20px;
     height: 30px;
     line-height: 30px;
@@ -13,6 +14,7 @@
       position: relative;
       padding-left: 10px; /* 避免文字贴着箭头 */
       margin-left: 10px;
+      width: 600px;
       .Rank_Recommend_body_list_Comment_button {
         display: flex;
         justify-content: space-between;

+ 1 - 1
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment_list/indes.css

@@ -1,5 +1,5 @@
 .Rank_Recommend_body_list_Comment_list {
-  margin: 0 0 30px 40px;
+  padding: 40px;
 }
 .Rank_Recommend_body_list_Comment_list .Rank_Recommend_body_list_Comment_list_ttitle {
   color: #000000;

+ 1 - 1
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list_Comment_list/indes.less

@@ -1,5 +1,5 @@
 .Rank_Recommend_body_list_Comment_list {
-  margin: 0 0 30px 40px;
+  padding: 40px;
   .Rank_Recommend_body_list_Comment_list_ttitle {
     color: rgb(0, 0, 0);
     font-weight: bolder;

+ 3 - 1
src/pages/layout/pages/find/rank/index.css

@@ -10,5 +10,7 @@
   padding-bottom: 20px;
 }
 .Rank .Rank_right {
-  width: 730px;
+  width: 750px;
+  padding-right: 40px;
+  border: 1px solid #cad1d1;
 }

+ 3 - 1
src/pages/layout/pages/find/rank/index.less

@@ -9,6 +9,8 @@
     padding-bottom: 20px;
   }
   .Rank_right {
-    width: 730px;
+    width: 750px;
+    padding-right: 40px;
+    border: 1px solid rgb(202, 209, 209);
   }
 }

+ 6 - 8
src/pages/layout/pages/find/rank/index.tsx

@@ -1,8 +1,9 @@
 import Rank_Recommend_body_left from './compomemts/Rank_Recommend_body_left'
-import Rank_Recommend_body_list from './compomemts/Rank_Recommend_body_list'
-import Rank_Recommend_body_list_Comment from './compomemts/Rank_Recommend_body_list_Comment'
-import Rank_Recommend_body_list_Comment_list from './compomemts/Rank_Recommend_body_list_Comment_list'
-import Rank_Recommend_body_right from './compomemts/Rank_Recommend_body_right'
+// import Rank_Recommend_body_list from './compomemts/Rank_Recommend_body_list'
+// import Rank_Recommend_body_list_Comment from './compomemts/Rank_Recommend_body_list_Comment'
+// import Rank_Recommend_body_list_Comment_list from './compomemts/Rank_Recommend_body_list_Comment_list'
+// import Rank_Recommend_body_right from './compomemts/Rank_Recommend_body_right'
+import RankPlaylist from './compomemts/RankPlaylist'
 import './index.css'
 const Rank = () => {
   return (
@@ -12,10 +13,7 @@ const Rank = () => {
         <Rank_Recommend_body_left />
       </div>
       <div className="Rank_right">
-        <Rank_Recommend_body_right />
-        <Rank_Recommend_body_list />
-        <Rank_Recommend_body_list_Comment />
-        <Rank_Recommend_body_list_Comment_list />
+        <RankPlaylist></RankPlaylist>
       </div>
     </div>
   )

+ 10 - 0
src/pages/layout/pages/musician/ApplyMusicianPage/index.css

@@ -0,0 +1,10 @@
+.musician_Mine {
+  width: 980px;
+  margin: 0 auto;
+}
+.musician_Mine .musician_Image {
+  text-align: center;
+}
+.musician_Mine .musician_title {
+  text-align: left;
+}

+ 11 - 0
src/pages/layout/pages/musician/ApplyMusicianPage/index.less

@@ -0,0 +1,11 @@
+.musician_Mine {
+  width: 980px;
+  margin: 0 auto;
+  .musician_Image {
+    text-align: center;
+  }
+
+  .musician_title {
+    text-align: left;
+  }
+}

+ 421 - 0
src/pages/layout/pages/musician/ApplyMusicianPage/index.tsx

@@ -0,0 +1,421 @@
+import { Image, Upload, DatePicker, Select, Input, Button, Checkbox, Form, InputNumber, message } from 'antd';
+import type { UploadChangeParam } from 'antd/es/upload';
+import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
+import './index.css';
+import { uploadFile } from '@/apis/upload';
+import { applyMusicianApis } from '@/apis/musician';
+
+const { TextArea } = Input;
+const { Option } = Select;
+
+const ApplyMusicianPage = () => {
+  // 定义表单字段类型
+  type FieldType = {
+    artistName?: string;
+    avatar?: string;
+    headerImage?: string;
+    gender?: string;
+    birthday?: Date;
+    region?: string;
+    genre?: string;
+    company?: string;
+    introduction?: string;
+    realName?: string;
+    phone?: string;
+    code?: string;
+    email?: string;
+    nationality?: string;
+    idCard?: string;
+    agreeFaceRecognition?: boolean;
+    invitationCode?: string;
+    wechat?: string;
+    platformName?: string;
+    platformAccount?: string;
+    fanCount?: number;
+    agreeTerms?: boolean;
+  };
+  const [messageApi, contextHolder] = message.useMessage();
+
+  const onFinish: (values: FieldType) => Promise<void> = async (values) => {
+    console.log(values);
+    const res = await applyMusicianApis(values);
+    if (res.code === 200) {
+      // 显示成功消息
+      messageApi.open({
+        type: 'success',
+        content: '申请已提交',
+      })
+    }
+  };
+
+  const onFinishFailed: (errorInfo: any) => void = (errorInfo) => {
+    console.log('提交失败:', errorInfo);
+  };
+
+  const handleAvatarChange = (info: UploadChangeParam) => {
+    if (info.file.status === 'done') {
+      console.log('上传成功:', info.file.response);
+    }
+  };
+
+  const customUploadAVatar = async (options: any) => {
+    const { file, onSuccess, onError, onProgress, data } = options;
+
+    try {
+      // 调用上传函数,并传入进度回调
+      const res = await uploadFile(data, file, onProgress);
+
+      // 上传成功,调用 onSuccess,并传递响应数据
+      onSuccess(res, file);
+    } catch (error) {
+      // 上传失败,调用 onError
+      onError(error);
+    }
+  };
+
+  // 二维码刷新逻辑(模拟)
+  const refreshQRCode = () => {
+    console.log('刷新二维码');
+  };
+
+  return (
+    <div className="musician_Mine">
+      {/* 顶部横幅 */}
+      <div className="musician_Image">
+        <Image
+          width={980}
+          src="https://web-asd-asd.oss-cn-beijing.aliyuncs.com/musician.jpeg"
+          alt="音乐人实名认证"
+        />
+      </div>
+
+      <div className="musician_title">
+        <h1>音乐人实名认证</h1>
+        <Form
+          name="artistRegistration"
+          labelCol={{ span: 6 }}
+          wrapperCol={{ span: 16 }}
+          style={{ maxWidth: 800 }}
+          initialValues={{
+            remember: true,
+            gender: '男',
+            region: '中国',
+            genre: '流行',
+            phone: '147****2671',
+          }}
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          {/* 音乐人信息 */}
+          <h3>音乐人信息</h3>
+          <Form.Item<FieldType>
+            label="音乐人姓名"
+            name="artistName"
+            rules={[{ required: true, message: '请输入需要申请或认领的音乐人名称' }]}
+          >
+            <Input placeholder="请输入音乐人名称" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="音乐人头像"
+            name="avatar"
+            getValueFromEvent={(e) => {
+              if (Array.isArray(e)) {
+                return e;
+              }
+              // 从上传组件获取文件列表,并转换为表单所需的值
+              const file = e?.file;
+              if (file && file.status === 'done' && file.response) {
+                // 返回上传成功的文件URL
+                return file.response.data || '';
+              }
+              return e?.fileList || [];
+            }}
+          >
+            <Upload
+              customRequest={customUploadAVatar}
+              data={'ARTIST_COVERS'}
+              listType="picture-card"
+              onChange={handleAvatarChange}
+              maxCount={1}
+              accept=".jpg,.jpeg,.png"
+            >
+              <div>
+                <PlusOutlined />
+                <div style={{ marginTop: 8 }}>上传头像</div>
+              </div>
+            </Upload>
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="音乐人页头图(网页)"
+            name="headerImage"
+            getValueFromEvent={(e) => {
+              if (Array.isArray(e)) {
+                return e;
+              }
+              // 从上传组件获取文件列表,并转换为表单所需的值
+              const file = e?.file;
+              if (file && file.status === 'done' && file.response) {
+                // 返回上传成功的文件URL
+                return file.response.data || '';
+              }
+              return e?.fileList || [];
+            }}
+          >
+            <Upload
+              customRequest={customUploadAVatar}
+              data={'ARTIST_COVERS'}
+              listType="picture-card"
+              onChange={handleAvatarChange}
+              maxCount={1}
+              accept=".jpg,.jpeg,.png"
+            >
+              <div>
+                <UploadOutlined />
+                <div style={{ marginTop: 8 }}>上传背景图</div>
+              </div>
+            </Upload>
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="性别"
+            name="gender"
+            rules={[{ required: true, message: '请选择性别' }]}
+          >
+            <Select>
+              <Option value="男">男</Option>
+              <Option value="女">女</Option>
+              <Option value="团体">团体</Option>
+            </Select>
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="生日"
+            name="birthday"
+            rules={[{ required: true, message: '请选择出生日期' }]}
+          >
+            <DatePicker style={{ width: '100%' }} />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="地区"
+            name="region"
+            rules={[{ required: true, message: '请选择地区' }]}
+          >
+            <Input placeholder="如:北京" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="流派风格"
+            name="genre"
+            rules={[{ required: true, message: '请选择流派风格' }]}
+          >
+            <Select>
+              <Option value="流行">流行</Option>
+              <Option value="摇滚">摇滚</Option>
+              <Option value="电子">电子</Option>
+              <Option value="说唱">说唱</Option>
+              <Option value="民谣">民谣</Option>
+            </Select>
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="所属公司/厂牌"
+            name="company"
+            rules={[{ required: true, message: '请选择所属公司' }]}
+          >
+            <Input placeholder="请输入公司名称" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="介绍"
+            name="introduction"
+            rules={[
+              { required: true, message: '请填写个人介绍' },
+              { min: 10, message: '介绍不能少于10字' },
+              { max: 1000, message: '介绍不能超过1000字' },
+            ]}
+          >
+            <TextArea rows={4} placeholder="可填写个人信息、代表作品、创作经历等" />
+          </Form.Item>
+
+          {/* 实名认证 */}
+          <h3>实名认证</h3>
+          <Form.Item<FieldType>
+            label="真实姓名"
+            name="realName"
+            rules={[{ required: true, message: '请输入真实姓名' }]}
+          >
+            <Input placeholder="请输入真实姓名" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="手机号"
+            name="phone"
+            rules={[{ required: true, message: '请输入手机号' }]}
+          >
+            <Input placeholder="请输入手机号" disabled />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="验证码"
+            name="code"
+            rules={[{ required: true, message: '请输入验证码' }]}
+          >
+            <Input placeholder="请输入验证码" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="邮箱"
+            name="email"
+            rules={[{ required: true, message: '请输入邮箱' }, { type: 'email', message: '请输入正确的邮箱格式' }]}
+          >
+            <Input placeholder="请输入邮箱" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="国籍/地区"
+            name="nationality"
+            rules={[{ required: true, message: '请选择国籍/地区' }]}
+          >
+            <Input placeholder="如:中国" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="证件号"
+            name="idCard"
+            rules={[{ required: true, message: '请输入身份证号' }]}
+          >
+            <Input placeholder="请输入身份证号" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="身份认证"
+            name="agreeFaceRecognition"
+            valuePropName="checked"
+            rules={[{ required: true, message: '请同意面部识别' }]}
+          >
+            <Checkbox>
+              经审慎考虑,同意提供本人面部识别特征(属个人敏感信息),以核实本人身份,保障账号安全
+            </Checkbox>
+          </Form.Item>
+
+          {/* 二维码展示 */}
+          <Form.Item<FieldType>
+            label="身份认证"
+            name="qrCode"
+            valuePropName="fileList"
+            getValueFromEvent={(e) => {
+              if (Array.isArray(e)) {
+                return e;
+              }
+              return e?.fileList;
+            }}
+          >
+            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
+              <Upload
+                action="/upload.do"
+                listType="picture-card"
+                maxCount={1}
+                accept=".png,.jpg"
+                showUploadList={false}
+              >
+                <img
+                  src="https://via.placeholder.com/150"
+                  alt="QR Code"
+                  style={{ width: 150, height: 150 }}
+                />
+              </Upload>
+              <Button type="link" onClick={refreshQRCode}>
+                刷新二维码
+              </Button>
+            </div>
+          </Form.Item>
+
+          {/* 邀请码 */}
+          <Form.Item<FieldType>
+            label="邀请码"
+            name="invitationCode"
+            extra="输入邀请码可获得云豆奖励,邀请人和你均可获得奖励"
+          >
+            <Input placeholder="请输入邀请码" />
+          </Form.Item>
+
+          {/* 微信号 */}
+          <Form.Item<FieldType>
+            label="微信号"
+            name="wechat"
+            extra="填写你的微信号,有助于快速审核通过"
+          >
+            <Input placeholder="请输入微信号" />
+          </Form.Item>
+
+          {/* 站外平台信息 */}
+          <h3>站外信息</h3>
+          <Form.Item<FieldType>
+            label="站外平台"
+            name="platformName"
+            rules={[{ required: true, message: '请选择站外平台' }]}
+          >
+            <Select>
+              <Option value="微博">微博</Option>
+              <Option value="抖音">抖音</Option>
+              <Option value="B站">B站</Option>
+              <Option value="小红书">小红书</Option>
+              <Option value="其他">其他</Option>
+            </Select>
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="用户ID或URL或昵称"
+            name="platformAccount"
+            rules={[{ required: true, message: '请输入用户ID或URL或昵称' }]}
+          >
+            <Input placeholder="如:@张三、https://example.com" />
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label="平台粉丝数"
+            name="fanCount"
+            rules={[{ required: true, message: '请输入粉丝数量' }]}
+          >
+            <InputNumber min={0} style={{ width: '100%' }} />
+          </Form.Item>
+
+          {/* 隐私协议 */}
+          <Form.Item<FieldType>
+            label="我已阅读并同意"
+            name="agreeTerms"
+            valuePropName="checked"
+            rules={[{ required: true, message: '请勾选同意服务条款' }]}
+          >
+            <Checkbox>
+              《网易音乐人服务条款》
+            </Checkbox>
+          </Form.Item>
+
+          <Form.Item<FieldType>
+            label=""
+            name="termsContent"
+            valuePropName="checked"
+          >
+            <div style={{ fontSize: 12, color: '#666', lineHeight: 1.8 }}>
+              经审慎考虑,同意提供本人真实姓名、手机号及验证码、国籍/地区及证件号码(包括身份证、护照、港澳台通行证,以上均属个人敏感信息),以核实本人身份,保障账号安全;同意提供其他信息(其中,邮箱为个人敏感信息,头像、简介可能包含个人敏感信息),以便平台优化服务,便于与本人取得联系。
+            </div>
+          </Form.Item>
+
+          {/* 提交按钮 */}
+          <Form.Item wrapperCol={{ offset: 6, span: 16 }}>
+            <Button type="primary" htmlType="submit" style={{ width: '100%', backgroundColor: '#ff4d4f', borderColor: '#ff4d4f' }}>
+              提交申请
+            </Button>
+          </Form.Item>
+        </Form>
+      </div>
+    </div>
+  );
+};
+
+export default ApplyMusicianPage;

+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/Home/index.css


+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/Home/index.less


+ 5 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/Home/index.tsx

@@ -0,0 +1,5 @@
+import './index.css'
+const musicianHome = () => {
+  return <div>musicianHome</div>
+}
+export default musicianHome;

+ 4 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/components/artInfo/index.css

@@ -0,0 +1,4 @@
+.ArtAvatar {
+  text-align: center;
+  padding: 40px 0 ;
+}

+ 4 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/components/artInfo/index.less

@@ -0,0 +1,4 @@
+.ArtAvatar{
+  text-align: center;
+  padding:40px 0 ;
+}

+ 13 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/components/artInfo/index.tsx

@@ -0,0 +1,13 @@
+import { Avatar } from "antd"
+import './index.css'
+
+const ArtAvatar = () => {
+  return (
+    <div className="ArtAvatar">
+      <Avatar size={100} src={'https://p1.music.126.net/bJ-I9VZMMLehc1-AToNZfA==/109951164599442634.jpg?param=180y180'} />
+      <p>张惠妹</p>
+      <p>网易云音乐人</p>
+    </div>
+  )
+}
+export default ArtAvatar

+ 13 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/index.css

@@ -0,0 +1,13 @@
+.MusicianDashboardPage {
+  width: 980px;
+  min-height: 100vh;
+  margin: 0 auto;
+  display: flex;
+}
+.MusicianDashboardPage .Menu_content {
+  flex: 1;
+}
+.MusicianDashboardPage .MusicianDashboardPage_button {
+  display: block;
+  margin: 0 auto;
+}

+ 15 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/index.less

@@ -0,0 +1,15 @@
+.MusicianDashboardPage {
+  width: 980px;
+  min-height: 100vh;
+  margin: 0 auto;
+  display: flex;
+
+  .Menu_content {
+    flex: 1;
+  }
+
+  .MusicianDashboardPage_button {
+    display: block;
+    margin: 0 auto;
+  }
+}

+ 111 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/index.tsx

@@ -0,0 +1,111 @@
+import './index.css';
+import {
+  AppstoreOutlined,
+  FileTextOutlined,
+  BarChartOutlined,
+  CopyrightOutlined,
+  DollarOutlined,
+  ToolOutlined,
+  DownloadOutlined
+} from '@ant-design/icons';
+import type { MenuProps } from 'antd';
+import { Button, Menu } from 'antd';
+import ArtAvator from './components/artInfo';
+import { Outlet, useNavigate } from 'react-router-dom';
+type MenuItem = Required<MenuProps>['items'][number];
+// 定义菜单数据,与设计图完全匹配
+const items: MenuItem[] = [
+  {
+    key: '/',
+    label: '首页',
+    icon: <AppstoreOutlined />
+  },
+  {
+    key: 'works',
+    label: '作品管理',
+    icon: <FileTextOutlined />,
+    // 可以根据实际需求添加子菜单
+    children: [
+      { key: 'wsongs', label: '歌曲' },
+      { key: 'wlyrics', label: '歌词' },
+      { key: 'warrangements', label: '编曲' }
+    ]
+  },
+  {
+    key: 'promotion',
+    label: '运营推广',
+    icon: <  BarChartOutlined />,
+    children: [
+      { key: 'promote-task', label: '推广任务' },
+      { key: 'fan-group', label: '乐迷团' },
+      { key: 'statistics', label: '数据统计' }
+    ]
+  },
+  {
+    key: 'copyright',
+    label: '版权中心',
+    icon: <CopyrightOutlined />,
+    children: [
+      { key: 'copyright-sign', label: '版权签约' },
+      { key: 'authorization', label: '授权管理' },
+      { key: 'dispute', label: '版权纠纷' }
+    ]
+  },
+  {
+    key: 'income',
+    label: '我的收益',
+    icon: <DollarOutlined />,
+    children: [
+      { key: 'income-detail', label: '收益明细' },
+      { key: 'withdraw', label: '提现管理' },
+      { key: 'invoice', label: '发票管理' }
+    ]
+  },
+  {
+    key: 'tools',
+    label: '创作工具箱',
+    icon: <ToolOutlined />,
+    children: [
+      { key: 'ai-composer', label: 'AI作曲' },
+      { key: 'studio', label: '在线制作' },
+      { key: 'courses', label: '创作课程' }
+    ]
+  }
+];
+
+const MusicianDashboardPage = () => {
+  const navigate = useNavigate();
+  const onClick: MenuProps['onClick'] = (e) => {
+    console.log('点击菜单:', e);
+    if (e.key) {
+      if (e.keyPath[1] === undefined) {
+        navigate(`/musician/${e.key}`);
+        return;
+      }
+      navigate(`/musician/${e.keyPath[1]}/${e.key}`);
+    }
+  };
+
+  return (
+
+    <div className="MusicianDashboardPage">
+      <div>
+        <ArtAvator />
+        <Button className='MusicianDashboardPage_button' type="primary" shape="round" icon={<DownloadOutlined />} size='Large'>
+          发布作品
+        </Button>
+        <Menu
+          onClick={onClick}
+          style={{ width: 256 }}
+          defaultSelectedKeys={['/']} // 默认选中"首页"
+          defaultOpenKeys={['works']} // 默认展开"作品管理"
+          mode="inline"
+          items={items}
+        />
+      </div>
+      <Outlet />
+    </div>
+  );
+};
+
+export default MusicianDashboardPage;

+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements/index.css


+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements/index.less


+ 5 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements/index.tsx

@@ -0,0 +1,5 @@
+import './index.css'
+const warrangements=()=>{
+  return <div>warrangements</div>
+}
+export default warrangements

+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics/index.css


+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics/index.less


+ 5 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics/index.tsx

@@ -0,0 +1,5 @@
+import './index.css'
+const Wlyrics=()=>{
+  return <div>wlyrics</div>
+}
+export default Wlyrics

+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs/index.css


+ 0 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs/index.less


+ 5 - 0
src/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs/index.tsx

@@ -0,0 +1,5 @@
+import './index.css'
+const Wsongs=()=>{
+  return <div>songs</div>
+}
+export default Wsongs

+ 70 - 3
src/pages/layout/pages/musician/index.css

@@ -1,10 +1,77 @@
-.musician_Mine {
+.musician-loading {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 700px;
   width: 980px;
+  flex-direction: column;
+  background-color: #f5f5f5;
   margin: 0 auto;
 }
-.musician_Mine .musician_Image {
+.musician-loading .spinner {
+  border: 4px solid rgba(0, 0, 0, 0.1);
+  border-left-color: #1890ff;
+  border-radius: 50%;
+  width: 40px;
+  height: 40px;
+  animation: spin 1s linear infinite;
+  margin-bottom: 16px;
+}
+.musician-loading .loading-text {
+  font-size: 16px;
+  color: #666;
+}
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+.status-page {
+  width: 980px;
+  height: 800px;
+  margin: 0 auto;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
   text-align: center;
 }
-.musician_Mine .musician_title {
+.pending-page,
+.rejected-page,
+.frozen-page {
+  width: 980px;
+  height: 700px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+}
+.reason-box {
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  padding: 15px;
+  margin: 20px 0;
   text-align: left;
+  width: 80%;
+}
+.reason-box h3 {
+  margin-top: 0;
+}
+.status-page button {
+  background-color: #007bff;
+  color: white;
+  border: none;
+  padding: 10px 20px;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.status-page button:hover {
+  background-color: #0056b3;
+}
+.rejected-page {
+  color: #dc3545;
+}
+.frozen-page {
+  color: #ffc107;
 }

+ 84 - 6
src/pages/layout/pages/musician/index.less

@@ -1,11 +1,89 @@
-.musician_Mine {
+// musician.less
+.musician-loading {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 700px;
   width: 980px;
+  flex-direction: column;
+  background-color: #f5f5f5;
   margin: 0 auto;
-  .musician_Image {
-    text-align: center;
+  
+  .spinner {
+    border: 4px solid rgba(0, 0, 0, 0.1);
+    border-left-color: #1890ff;
+    border-radius: 50%;
+    width: 40px;
+    height: 40px;
+    animation: spin 1s linear infinite;
+    margin-bottom: 16px;
   }
-
-  .musician_title {
-    text-align: left;
+  
+  .loading-text {
+    font-size: 16px;
+    color: #666;
+  }
+  
+  @keyframes spin {
+    to {
+      transform: rotate(360deg);
+    }
   }
+}
+
+.status-page {
+  width: 980px;
+  height: 800px;
+  margin: 0 auto;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+}
+
+.pending-page,
+.rejected-page,
+.frozen-page {
+  width: 980px;
+  height: 700px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+}
+
+.reason-box {
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  padding: 15px;
+  margin: 20px 0;
+  text-align: left;
+  width: 80%;
+}
+
+.reason-box h3 {
+  margin-top: 0;
+}
+
+.status-page button {
+  background-color: #007bff;
+  color: white;
+  border: none;
+  padding: 10px 20px;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+.status-page button:hover {
+  background-color: #0056b3;
+}
+
+.rejected-page {
+  color: #dc3545;
+}
+
+.frozen-page {
+  color: #ffc107;
 }

+ 126 - 384
src/pages/layout/pages/musician/index.tsx

@@ -1,391 +1,133 @@
-import { Image, Upload, DatePicker, Select, Input, Button, Checkbox, Form, InputNumber } from 'antd';
-import type { UploadChangeParam } from 'antd/es/upload';
-import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
-import './index.css';
-
-const { TextArea } = Input;
-const { Option } = Select;
-
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useMessageStore } from '@/store/useMessageStore';
+import ApplyMusicianPage from './ApplyMusicianPage';
+import MusicianDashboardPage from './MusicianDashboardPage';
+import { applyMusicianStatusApis } from '@/apis/musician';
+import './index.css'
 const Musician = () => {
-  // 定义表单字段类型
-  type FieldType = {
-    artistName?: string;
-    avatar?: string;
-    banner?: string;
-    gender?: string;
-    birthday?: Date;
-    region?: string;
-    genre?: string;
-    company?: string;
-    introduction?: string;
-    realName?: string;
-    phone?: string;
-    code?: string;
-    email?: string;
-    nationality?: string;
-    idCard?: string;
-    agreeFaceRecognition?: boolean;
-    inviteCode?: string;
-    wechat?: string;
-    externalPlatform?: string;
-    externalId?: string;
-    fanCount?: number;
-    agreeTerms?: boolean;
-  };
-
-  const onFinish: (values: FieldType) => void = (values) => {
-    console.log('提交成功:', values);
-  };
-
-  const onFinishFailed: (errorInfo: any) => void = (errorInfo) => {
-    console.log('提交失败:', errorInfo);
-  };
-
-  // 头像上传处理
-  const handleAvatarChange = (info: UploadChangeParam) => {
-    if (info.file.status === 'done') {
-      console.log('上传成功:', info.file.response);
+  const navigate = useNavigate();
+  const setMessage = useMessageStore((state) => state.setMessage);
+  const [loading, setLoading] = useState(true);
+  const [userStatus, setUserStatus] = useState<'unlogged' | 'notApplied' | 'pending' | 'approved' | 'rejected' | 'frozen'>('unlogged');
+  const [rejectReason, setRejectReason] = useState('');
+  const [freezeReason, setFreezeReason] = useState('');
+  useEffect(() => {
+    const token = localStorage.getItem('token');
+    if (!token) {
+      setUserStatus('unlogged');
+      setLoading(false);
+      return;
     }
-  };
-
-  // 背景图上传处理
-  const handleBannerChange = (info: UploadChangeParam) => {
-    if (info.file.status === 'done') {
-      console.log('上传成功:', info.file.response);
-    }
-  };
-
-  // 二维码刷新逻辑(模拟)
-  const refreshQRCode = () => {
-    console.log('刷新二维码');
-  };
-
-  return (
-    <div className="musician_Mine">
-      {/* 顶部横幅 */}
-      <div className="musician_Image">
-        <Image
-          width={980}
-          src="https://web-asd-asd.oss-cn-beijing.aliyuncs.com/musician.jpeg"
-          alt="音乐人实名认证"
-        />
+    // 查询音乐人状态
+    const fetchMusicianStatus = async () => {
+      try {
+        const res = await applyMusicianStatusApis();
+        console.log(res);
+
+        // 如果没有申请记录
+        if (!res.data) {
+          setUserStatus('notApplied');
+          return;
+        }
+
+        const { status, reject_reason, freeze_reason } = res.data;
+
+        switch (status) {
+          case 0: // 待审核
+            setUserStatus('pending');
+            break;
+          case 1: // 已通过
+            setUserStatus('approved');
+            break;
+          case 2: // 已拒绝
+            setUserStatus('rejected');
+            setRejectReason(reject_reason || '');
+            break;
+          case 3: // 已冻结
+            setUserStatus('frozen');
+            setFreezeReason(freeze_reason || '');
+            break;
+          default:
+            setUserStatus('notApplied');
+        }
+      } catch (error) {
+        console.error('获取音乐人状态失败:', error);
+        setUserStatus('notApplied');
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    fetchMusicianStatus();
+  }, []);
+
+  // 在加载判断部分替换原来的<div>加载中...</div>
+  if (loading) {
+    return (
+      <div className="musician-loading">
+        <div className="spinner"></div>
+        <p className="loading-text">正在加载音乐人信息...</p>
       </div>
-
-      <div className="musician_title">
-        <h1>音乐人实名认证</h1>
-        <Form
-          name="artistRegistration"
-          labelCol={{ span: 6 }}
-          wrapperCol={{ span: 16 }}
-          style={{ maxWidth: 800 }}
-          initialValues={{
-            remember: true,
-            gender: '男',
-            region: '中国',
-            genre: '流行',
-            phone: '147****2671',
-          }}
-          onFinish={onFinish}
-          onFinishFailed={onFinishFailed}
-          autoComplete="off"
-        >
-          {/* 音乐人信息 */}
-          <h3>音乐人信息</h3>
-          <Form.Item<FieldType>
-            label="音乐人姓名"
-            name="artistName"
-            rules={[{ required: true, message: '请输入需要申请或认领的音乐人名称' }]}
-          >
-            <Input placeholder="请输入音乐人名称" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="音乐人头像"
-            name="avatar"
-            valuePropName="fileList"
-            getValueFromEvent={(e) => {
-              if (Array.isArray(e)) {
-                return e;
-              }
-              return e?.fileList;
-            }}
-          >
-            <Upload
-              action="/upload.do"
-              listType="picture-card"
-              onChange={handleAvatarChange}
-              maxCount={1}
-              accept=".jpg,.jpeg,.png"
-            >
-              <div>
-                <PlusOutlined />
-                <div style={{ marginTop: 8 }}>上传头像</div>
-              </div>
-            </Upload>
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="音乐人页头图(网页)"
-            name="banner"
-            valuePropName="fileList"
-            getValueFromEvent={(e) => {
-              if (Array.isArray(e)) {
-                return e;
-              }
-              return e?.fileList;
-            }}
-          >
-            <Upload
-              action="/upload.do"
-              listType="picture-card"
-              onChange={handleBannerChange}
-              maxCount={1}
-              accept=".jpg,.jpeg,.png"
-            >
-              <div>
-                <UploadOutlined />
-                <div style={{ marginTop: 8 }}>上传背景图</div>
-              </div>
-            </Upload>
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="性别"
-            name="gender"
-            rules={[{ required: true, message: '请选择性别' }]}
-          >
-            <Select>
-              <Option value="男">男</Option>
-              <Option value="女">女</Option>
-              <Option value="团体">团体</Option>
-            </Select>
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="生日"
-            name="birthday"
-            rules={[{ required: true, message: '请选择出生日期' }]}
-          >
-            <DatePicker style={{ width: '100%' }} />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="地区"
-            name="region"
-            rules={[{ required: true, message: '请选择地区' }]}
-          >
-            <Input placeholder="如:北京" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="流派风格"
-            name="genre"
-            rules={[{ required: true, message: '请选择流派风格' }]}
-          >
-            <Select>
-              <Option value="流行">流行</Option>
-              <Option value="摇滚">摇滚</Option>
-              <Option value="电子">电子</Option>
-              <Option value="说唱">说唱</Option>
-              <Option value="民谣">民谣</Option>
-            </Select>
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="所属公司/厂牌"
-            name="company"
-            rules={[{ required: true, message: '请选择所属公司' }]}
-          >
-            <Input placeholder="请输入公司名称" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="介绍"
-            name="introduction"
-            rules={[
-              { required: true, message: '请填写个人介绍' },
-              { min: 10, message: '介绍不能少于10字' },
-              { max: 1000, message: '介绍不能超过1000字' },
-            ]}
-          >
-            <TextArea rows={4} placeholder="可填写个人信息、代表作品、创作经历等" />
-          </Form.Item>
-
-          {/* 实名认证 */}
-          <h3>实名认证</h3>
-          <Form.Item<FieldType>
-            label="真实姓名"
-            name="realName"
-            rules={[{ required: true, message: '请输入真实姓名' }]}
-          >
-            <Input placeholder="请输入真实姓名" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="手机号"
-            name="phone"
-            rules={[{ required: true, message: '请输入手机号' }]}
-          >
-            <Input placeholder="请输入手机号" disabled />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="验证码"
-            name="code"
-            rules={[{ required: true, message: '请输入验证码' }]}
-          >
-            <Input placeholder="请输入验证码" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="邮箱"
-            name="email"
-            rules={[{ required: true, message: '请输入邮箱' }, { type: 'email', message: '请输入正确的邮箱格式' }]}
-          >
-            <Input placeholder="请输入邮箱" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="国籍/地区"
-            name="nationality"
-            rules={[{ required: true, message: '请选择国籍/地区' }]}
-          >
-            <Input placeholder="如:中国" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="证件号"
-            name="idCard"
-            rules={[{ required: true, message: '请输入身份证号' }]}
-          >
-            <Input placeholder="请输入身份证号" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="身份认证"
-            name="agreeFaceRecognition"
-            valuePropName="checked"
-            rules={[{ required: true, message: '请同意面部识别' }]}
-          >
-            <Checkbox>
-              经审慎考虑,同意提供本人面部识别特征(属个人敏感信息),以核实本人身份,保障账号安全
-            </Checkbox>
-          </Form.Item>
-
-          {/* 二维码展示 */}
-          <Form.Item<FieldType>
-            label="身份认证"
-            name="qrCode"
-            valuePropName="fileList"
-            getValueFromEvent={(e) => {
-              if (Array.isArray(e)) {
-                return e;
-              }
-              return e?.fileList;
-            }}
-          >
-            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
-              <Upload
-                action="/upload.do"
-                listType="picture-card"
-                maxCount={1}
-                accept=".png,.jpg"
-                showUploadList={false}
-              >
-                <img
-                  src="https://via.placeholder.com/150"
-                  alt="QR Code"
-                  style={{ width: 150, height: 150 }}
-                />
-              </Upload>
-              <Button type="link" onClick={refreshQRCode}>
-                刷新二维码
-              </Button>
-            </div>
-          </Form.Item>
-
-          {/* 邀请码 */}
-          <Form.Item<FieldType>
-            label="邀请码"
-            name="inviteCode"
-            extra="输入邀请码可获得云豆奖励,邀请人和你均可获得奖励"
-          >
-            <Input placeholder="请输入邀请码" />
-          </Form.Item>
-
-          {/* 微信号 */}
-          <Form.Item<FieldType>
-            label="微信号"
-            name="wechat"
-            extra="填写你的微信号,有助于快速审核通过"
-          >
-            <Input placeholder="请输入微信号" />
-          </Form.Item>
-
-          {/* 站外平台信息 */}
-          <h3>站外信息</h3>
-          <Form.Item<FieldType>
-            label="站外平台"
-            name="externalPlatform"
-            rules={[{ required: true, message: '请选择站外平台' }]}
-          >
-            <Select>
-              <Option value="微博">微博</Option>
-              <Option value="抖音">抖音</Option>
-              <Option value="B站">B站</Option>
-              <Option value="小红书">小红书</Option>
-              <Option value="其他">其他</Option>
-            </Select>
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="用户ID或URL或昵称"
-            name="externalId"
-            rules={[{ required: true, message: '请输入用户ID或URL或昵称' }]}
-          >
-            <Input placeholder="如:@张三、https://example.com" />
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label="平台粉丝数"
-            name="fanCount"
-            rules={[{ required: true, message: '请输入粉丝数量' }]}
-          >
-            <InputNumber min={0} style={{ width: '100%' }} />
-          </Form.Item>
-
-          {/* 隐私协议 */}
-          <Form.Item<FieldType>
-            label="我已阅读并同意"
-            name="agreeTerms"
-            valuePropName="checked"
-            rules={[{ required: true, message: '请勾选同意服务条款' }]}
-          >
-            <Checkbox>
-              《网易音乐人服务条款》
-            </Checkbox>
-          </Form.Item>
-
-          <Form.Item<FieldType>
-            label=""
-            name="termsContent"
-            valuePropName="checked"
-          >
-            <div style={{ fontSize: 12, color: '#666', lineHeight: 1.8 }}>
-              经审慎考虑,同意提供本人真实姓名、手机号及验证码、国籍/地区及证件号码(包括身份证、护照、港澳台通行证,以上均属个人敏感信息),以核实本人身份,保障账号安全;同意提供其他信息(其中,邮箱为个人敏感信息,头像、简介可能包含个人敏感信息),以便平台优化服务,便于与本人取得联系。
-            </div>
-          </Form.Item>
-
-          {/* 提交按钮 */}
-          <Form.Item wrapperCol={{ offset: 6, span: 16 }}>
-            <Button type="primary" htmlType="submit" style={{ width: '100%', backgroundColor: '#ff4d4f', borderColor: '#ff4d4f' }}>
-              提交申请
-            </Button>
-          </Form.Item>
-        </Form>
+    );
+  }
+
+  // 根据状态渲染不同页面
+  if (userStatus === 'unlogged') {
+    navigate('/login');
+    return null;
+  }
+
+  if (userStatus === 'notApplied') {
+    return <ApplyMusicianPage />;
+  }
+
+  if (userStatus === 'pending') {
+    return (
+      <div className="status-page pending-page">
+        <h2>申请审核中</h2>
+        <p>您的音乐人申请正在审核中,请耐心等待。</p>
+      </div>
+    );
+  }
+
+  if (userStatus === 'rejected') {
+    return (
+      <div className="status-page rejected-page">
+        <h2>申请被拒绝</h2>
+        <p>很遗憾,您的音乐人申请未通过审核。</p>
+        {rejectReason && (
+          <div className="reason-box">
+            <h3>拒绝原因:</h3>
+            <p>{rejectReason}</p>
+          </div>
+        )}
+        <button onClick={() => navigate('/musician/apply')}>
+          重新申请
+        </button>
       </div>
-    </div>
-  );
+    );
+  }
+
+  if (userStatus === 'frozen') {
+    return (
+      <div className="status-page frozen-page">
+        <h2>账号已冻结</h2>
+        <p>您的音乐人账号已被冻结。</p>
+        {freezeReason && (
+          <div className="reason-box">
+            <h3>冻结原因:</h3>
+            <p>{freezeReason}</p>
+          </div>
+        )}
+        <button onClick={() => navigate('/support')}>
+          联系客服
+        </button>
+      </div>
+    );
+  }
+
+  return <MusicianDashboardPage />;
 };
 
 export default Musician;

+ 6 - 0
src/pages/layout/pages/playlist/index.css

@@ -0,0 +1,6 @@
+.playlist {
+  width: 780px;
+  margin: 0 auto ;
+  border: 1px solid #aaaaaa;
+  padding-right: 40px;
+}

+ 6 - 0
src/pages/layout/pages/playlist/index.less

@@ -0,0 +1,6 @@
+.playlist {
+  width: 780px;
+  margin:0 auto ;
+  border: 1px solid rgb(170, 170, 170);
+  padding-right: 40px;
+}

+ 10 - 0
src/pages/layout/pages/playlist/index.tsx

@@ -0,0 +1,10 @@
+import RankPlaylist from '../find/rank/compomemts/RankPlaylist'
+import './index.css'
+const Playlist = () => {
+  return (
+    <div className="playlist">
+      <RankPlaylist></RankPlaylist>
+    </div>
+  )
+}
+export default Playlist

+ 206 - 0
src/pages/layout/pages/testPage/index.css

@@ -0,0 +1,206 @@
+.music-player-wrapper {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background-color: #282828;
+  color: #fff;
+  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+}
+.player-container {
+  padding-top: -20px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 8px 16px;
+  height: 60px;
+}
+/* 左侧歌曲信息 */
+.track-info {
+  display: flex;
+  align-items: center;
+  margin-right: 20px;
+}
+.track-cover {
+  width: 40px;
+  height: 40px;
+  border-radius: 4px;
+  object-fit: cover;
+  margin-right: 12px;
+}
+.track-meta {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+.track-title {
+  font-size: 14px;
+  font-weight: 500;
+  line-height: 1.2;
+}
+.track-artist {
+  font-size: 12px;
+  color: #b3b3b3;
+  line-height: 1.2;
+}
+/* 中间播放器容器(占主要宽度) */
+.player-ui-container {
+  width: 780px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.player-ui {
+  color: #fff;
+  width: 100%;
+  /* 让播放器控件充满容器 */
+  background-color: #282828;
+  height: 76px;
+  display: flex;
+  justify-content: center;
+}
+/* 修复按钮交互 */
+.rhap-skip-button,
+.rhap-volume-button {
+  cursor: pointer;
+}
+/* 右侧播放列表按钮 */
+.playlist-toggle {
+  background: none;
+  border: none;
+  color: #b3b3b3;
+  cursor: pointer;
+  margin-left: 16px;
+  transition: color 0.2s ease;
+}
+.playlist-toggle:hover {
+  color: #fff;
+}
+/* 播放列表面板 */
+.playlist-panel {
+  position: absolute;
+  bottom: 60px;
+  left: 0;
+  right: 0;
+  background-color: #383838;
+  max-height: 300px;
+  overflow-y: auto;
+  box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.5);
+  border-top: 1px solid #444;
+}
+.playlist-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #444;
+}
+.playlist-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: 600;
+}
+.close-playlist {
+  background: none;
+  border: none;
+  color: #b3b3b3;
+  cursor: pointer;
+  font-size: 20px;
+}
+.close-playlist:hover {
+  color: #fff;
+}
+.track-list {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+.track-item {
+  display: flex;
+  align-items: center;
+  padding: 8px 16px;
+  cursor: pointer;
+  transition: background-color 0.15s ease;
+}
+.track-item:hover,
+.track-item.active {
+  background-color: #484848;
+}
+.item-cover {
+  width: 32px;
+  height: 32px;
+  border-radius: 2px;
+  margin-right: 12px;
+}
+.item-info {
+  flex-grow: 1;
+  min-width: 0;
+}
+.item-title {
+  font-size: 14px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.item-artist {
+  font-size: 12px;
+  color: #b3b3b3;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.item-duration {
+  font-size: 12px;
+  color: #b3b3b3;
+  margin-left: 16px;
+}
+.playlist-panel::-webkit-scrollbar {
+  width: 6px;
+}
+.playlist-panel::-webkit-scrollbar-track {
+  background: #383838;
+}
+.playlist-panel::-webkit-scrollbar-thumb {
+  background: #555;
+  border-radius: 3px;
+}
+.playlist-panel::-webkit-scrollbar-thumb:hover {
+  background: #666;
+}
+/* 保留你原有样式,补充以下优化 */
+/* 确保播放器原生按钮样式适配深色主题 */
+.rhap-skip-button,
+.rhap-volume-button,
+.rhap-play-pause-button {
+  color: #fff !important;
+  cursor: pointer;
+}
+.rhap-skip-button:hover,
+.rhap-volume-button:hover,
+.rhap-play-pause-button:hover {
+  color: #1db954 !important;
+  /* 网易云绿色高亮 */
+}
+/* 进度条样式优化(适配深色主题) */
+.rhap_progress-bar {
+  background-color: #444 !important;
+}
+.rhap_progress-filled {
+  background-color: #1db954 !important;
+}
+.rhap_progress-indicator {
+  background-color: #fff !important;
+  border: 2px solid #1db954 !important;
+}
+/* 音量条样式优化 */
+.rhap_volume-bar {
+  background-color: #444 !important;
+}
+.rhap_volume-filled {
+  background-color: #fff !important;
+}
+.rhap_volume-indicator {
+  background-color: #fff !important;
+}

+ 239 - 0
src/pages/layout/pages/testPage/index.less

@@ -0,0 +1,239 @@
+.music-player-wrapper {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background-color: #282828;
+  color: #fff;
+  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+}
+
+.player-container {
+  padding-top: -20px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 8px 16px;
+  height: 60px;
+}
+
+/* 左侧歌曲信息 */
+.track-info {
+  display: flex;
+  align-items: center;
+  margin-right: 20px;
+}
+
+.track-cover {
+  width: 40px;
+  height: 40px;
+  border-radius: 4px;
+  object-fit: cover;
+  margin-right: 12px;
+}
+
+.track-meta {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.track-title {
+  font-size: 14px;
+  font-weight: 500;
+  line-height: 1.2;
+}
+
+.track-artist {
+  font-size: 12px;
+  color: #b3b3b3;
+  line-height: 1.2;
+}
+
+/* 中间播放器容器(占主要宽度) */
+.player-ui-container {
+  width: 780px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.player-ui {
+  color:  #fff;
+  width: 100%; /* 让播放器控件充满容器 */
+  background-color:#282828;
+  height: 76px;
+  display: flex;
+  justify-content: center;
+}
+
+/* 修复按钮交互 */
+.rhap-skip-button, .rhap-volume-button {
+  cursor: pointer;
+}
+
+/* 右侧播放列表按钮 */
+.playlist-toggle {
+  background: none;
+  border: none;
+  color: #b3b3b3;
+  cursor: pointer;
+  margin-left: 16px;
+  transition: color 0.2s ease;
+}
+
+.playlist-toggle:hover {
+  color: #fff;
+}
+
+/* 播放列表面板 */
+.playlist-panel {
+  position: absolute;
+  bottom: 60px;
+  left: 0;
+  right: 0;
+  background-color: #383838;
+  max-height: 300px;
+  overflow-y: auto;
+  box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.5);
+  border-top: 1px solid #444;
+}
+
+.playlist-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #444;
+}
+
+.playlist-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: 600;
+}
+
+.close-playlist {
+  background: none;
+  border: none;
+  color: #b3b3b3;
+  cursor: pointer;
+  font-size: 20px;
+}
+
+.close-playlist:hover {
+  color: #fff;
+}
+
+.track-list {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.track-item {
+  display: flex;
+  align-items: center;
+  padding: 8px 16px;
+  cursor: pointer;
+  transition: background-color 0.15s ease;
+}
+
+.track-item:hover,
+.track-item.active {
+  background-color: #484848;
+}
+
+.item-cover {
+  width: 32px;
+  height: 32px;
+  border-radius: 2px;
+  margin-right: 12px;
+}
+
+.item-info {
+  flex-grow: 1;
+  min-width: 0;
+}
+
+.item-title {
+  font-size: 14px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.item-artist {
+  font-size: 12px;
+  color: #b3b3b3;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.item-duration {
+  font-size: 12px;
+  color: #b3b3b3;
+  margin-left: 16px;
+}
+
+.playlist-panel::-webkit-scrollbar {
+  width: 6px;
+}
+
+.playlist-panel::-webkit-scrollbar-track {
+  background: #383838;
+}
+
+.playlist-panel::-webkit-scrollbar-thumb {
+  background: #555;
+  border-radius: 3px;
+}
+
+.playlist-panel::-webkit-scrollbar-thumb:hover {
+  background: #666;
+}
+/* 保留你原有样式,补充以下优化 */
+
+/* 确保播放器原生按钮样式适配深色主题 */
+.rhap-skip-button,
+.rhap-volume-button,
+.rhap-play-pause-button {
+  color: #fff !important;
+  cursor: pointer;
+}
+
+.rhap-skip-button:hover,
+.rhap-volume-button:hover,
+.rhap-play-pause-button:hover {
+  color: #1db954 !important; /* 网易云绿色高亮 */
+}
+
+/* 进度条样式优化(适配深色主题) */
+.rhap_progress-bar {
+  background-color: #444 !important;
+}
+
+.rhap_progress-filled {
+  background-color: #1db954 !important;
+}
+
+.rhap_progress-indicator {
+  background-color: #fff !important;
+  border: 2px solid #1db954 !important;
+}
+
+/* 音量条样式优化 */
+.rhap_volume-bar {
+  background-color: #444 !important;
+}
+
+.rhap_volume-filled {
+  background-color: #fff !important;
+}
+
+.rhap_volume-indicator {
+  background-color: #fff !important;
+}

+ 185 - 0
src/pages/layout/pages/testPage/index.tsx

@@ -0,0 +1,185 @@
+import React, { useState, useRef, useEffect } from 'react';
+import AudioPlayer from 'react-h5-audio-player';
+import 'react-h5-audio-player/lib/styles.css';
+import './index.css';
+import { UnorderedListOutlined } from '@ant-design/icons';
+
+interface Song {
+  id: number;
+  title: string;
+  artist: string;
+  url: string;
+  cover: string;
+  duration: string;
+  isFavorite?: boolean;
+  album?: string;           // 所属专辑
+  composer?: string;        // 作曲
+  arranger?: string;        // 编曲
+  description?: string;     // 歌曲描述
+}
+
+const playlist: Song[] = [
+  {
+    id: 1,
+    title: '奇妙能力歌',
+    artist: '陈粒',
+    url: 'https://music.163.com/song/media/outer/url?id=28539182.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '03:18',
+    album: '如也',
+    composer: '陈粒',
+    arranger: '陈粒',
+    description: '独立音乐人陈粒代表作,独特的嗓音和诗意的歌词'
+  },
+  {
+    id: 2,
+    title: '南山南',
+    artist: '马頔',
+    url: 'https://music.163.com/song/media/outer/url?id=28248294.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '04:32',
+    album: '孤岛',
+    composer: '马頔',
+    arranger: '马頔',
+    description: '民谣歌手马頔经典作品,深情的旋律打动人心'
+  },
+  {
+    id: 3,
+    title: '董小姐',
+    artist: '宋冬野',
+    url: 'https://music.163.com/song/media/outer/url?id=26460053.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '04:15',
+    album: '摩登天空7',
+    composer: '宋冬野',
+    arranger: '宋冬野',
+    description: '民谣音乐人宋冬野代表作,简单却直击心灵'
+  },
+  {
+    id: 4,
+    title: '玫瑰',
+    artist: '贰佰',
+    url: 'https://music.163.com/song/media/outer/url?id=28539183.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '04:38',
+    album: '玫瑰',
+    composer: '贰佰',
+    arranger: '贰佰',
+    description: '独立摇滚音乐人贰佰经典作品,充满力量的嗓音'
+  },
+  {
+    id: 5,
+    title: '理想三旬',
+    artist: '陈鸿宇',
+    url: 'https://music.163.com/song/media/outer/url?id=407954569.mp3',
+    cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
+    duration: '04:26',
+    album: '浓烟下的诗歌电台',
+    composer: '陈鸿宇',
+    arranger: '陈鸿宇',
+    description: '独立音乐人陈鸿宇代表作,低沉磁性的嗓音'
+  }
+];
+
+const MusicPlayer: React.FC = () => {
+  const [currentTrackIndex, setCurrentTrackIndex] = useState<number>(0);
+  const [isPlaying, setIsPlaying] = useState<boolean>(false);
+  const [isPlaylistOpen, setIsPlaylistOpen] = useState<boolean>(false);
+  const audioPlayerRef = useRef<AudioPlayer>(null);
+  const currentTrack = playlist[currentTrackIndex];
+
+  // 修改 handlePrev 函数
+  const handlePrev = () => {
+    setCurrentTrackIndex((prev) => (prev - 1 + playlist.length) % playlist.length);
+    setIsPlaying(true); // 确保切换后自动播放
+  };
+
+  // 修改 handleNext 函数
+  const handleNext = () => {
+    setCurrentTrackIndex((prev) => (prev + 1) % playlist.length);
+    setIsPlaying(true); // 确保切换后自动播放
+  };
+  // 播放列表项点击事件
+  const handleTrackSelect = (index: number) => {
+    setCurrentTrackIndex(index);
+    setIsPlaying(true);
+    setIsPlaylistOpen(false);
+  };
+
+
+  return (
+    <div className="music-player-wrapper">
+      <div className="player-container">
+        {/* 左侧:歌曲封面 + 信息 */}
+        <div className="track-info">
+          <img src={currentTrack.cover} alt={currentTrack.title} className="track-cover" />
+          <div className="track-meta">
+            <div className="track-title">{currentTrack.title}</div>
+            <div className="track-artist">{currentTrack.artist}</div>
+          </div>
+        </div>
+
+        {/* 中间:播放器核心(使用原生上一首/下一首按钮) */}
+        <div className="player-ui-container">
+
+          <AudioPlayer
+            ref={audioPlayerRef}
+            key={currentTrackIndex}
+            src={currentTrack.url}
+            autoPlay={isPlaying}
+            onPlay={() => setIsPlaying(true)}
+            onPause={() => setIsPlaying(false)}
+            onEnded={handleNext}
+            showSkipControls
+            className="player-ui"
+            listenInterval={1000}
+            onClickPrevious={handlePrev}
+            onClickNext={handleNext}
+          />
+        </div>
+
+        {/* 右侧:播放列表按钮(AntD 图标) */}
+        <button
+          className="playlist-toggle"
+          onClick={() => setIsPlaylistOpen(!isPlaylistOpen)}
+          aria-label="显示/隐藏播放列表"
+        >
+          <UnorderedListOutlined />
+        </button>
+      </div>
+
+      {/* 播放列表 */}
+      {isPlaylistOpen && (
+        <div className="playlist-panel">
+          <div className="playlist-header">
+            <h3>播放列表</h3>
+            <button
+              onClick={() => setIsPlaylistOpen(false)}
+              className="close-playlist"
+            >
+              ×
+            </button>
+          </div>
+          <ul className="track-list">
+            {playlist.map((track, index) => (
+              <li
+                key={track.id}
+                className={index === currentTrackIndex ? 'track-item active' : 'track-item'}
+                onClick={() => handleTrackSelect(index)}
+              >
+                <img src={track.cover} alt={track.title} className="item-cover" />
+                <div className="item-info">
+                  <div className="item-title">{track.title}</div>
+                  <div className="item-artist">{track.artist}</div>
+                </div>
+                <div className="item-duration">{track.duration}</div>
+              </li>
+            ))}
+          </ul>
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default MusicPlayer;

+ 63 - 3
src/router/index.tsx

@@ -12,14 +12,26 @@ import Rank from '@/pages/layout/pages/find/rank'
 import SongList from '@/pages/layout/pages/find/songList'
 import Album from '@/pages/layout/pages/find/album'
 import Singer from '@/pages/layout/pages/find/singer'
-import Playlist from '@/pages/layout/pages/find/playlist'
-import AuthCallback from '@/pages/layout/components/Wy_Header/components/AuthCallback'
+import Playlist from '@/pages/layout/pages/playlist'
+import AuthCallback from '@/pages/layout/components/LoginContent/components/AuthCallback'
 import Mine_Artist from '@/pages/layout/pages/mine/artist'
 import Mine_Mv from '@/pages/layout/pages/mine/mv'
 import Mine_Radio from '@/pages/layout/pages/mine/radio'
 import Mine_Palylist from '@/pages/layout/pages/mine/playlist'
 import SongTab from '@/pages/layout/pages/song'
 import CreatorCenter from '@/pages/layout/pages/CreatorCenter'
+import Artistinfo from '@/pages/layout/pages/artistinfo'
+import Video from '@/pages/layout/pages/Video'
+import Search from '@/pages/layout/pages/Search'
+import MusicPlayer from '@/pages/layout/pages/testPage'
+import SongDetail from '@/pages/layout/pages/SongDetail'
+import LoginPage from '@/pages/layout/pages/LoginPage'
+import LoginContent from '@/pages/layout/components/LoginContent'
+import Wsongs from '@/pages/layout/pages/musician/MusicianDashboardPage/works/wsongs'
+import Wlyrics from '@/pages/layout/pages/musician/MusicianDashboardPage/works/wlyrics'
+import Warrangements from '@/pages/layout/pages/musician/MusicianDashboardPage/works/warrangements'
+import MusicianHome from '@/pages/layout/pages/musician/MusicianDashboardPage/Home'
+
 const router = createBrowserRouter([
   //   <AuthRoute>
   //   <WYLayout />
@@ -54,9 +66,18 @@ const router = createBrowserRouter([
             path: '/find/album',
             element: <Album />,
           },
+          {
+            path: '/find/artistinfo',
+            element: <Artistinfo />,
+          },
           {
             path: '/find/paylist',
             element: <Playlist />,
+          }
+          ,
+          {
+            path: '/find/video',
+            element: <Video />,
           },
         ],
       },
@@ -81,6 +102,7 @@ const router = createBrowserRouter([
             path: '/mine/playlist/:id',
             element: <Mine_Palylist />,
           },
+
         ]
       },
       {
@@ -94,6 +116,26 @@ const router = createBrowserRouter([
       {
         path: '/musician',
         element: <Musician />,
+        children: [
+          { index: true, element: <Navigate to="/musician/musicianHome" /> },
+          {
+            path: '/musician/musicianHome',
+            element: <MusicianHome />
+          },
+          {
+            path: '/musician/works/wsongs',
+            element: <Wsongs />
+          },
+          {
+            path: '/musician/works/wlyrics',
+            element: <Wlyrics />
+          },
+          {
+            path: '/musician/works/warrangements',
+            element: <Warrangements />
+          }
+
+        ]
       },
 
       {
@@ -111,7 +153,25 @@ const router = createBrowserRouter([
       {
         path: '/creatorCenter',
         element: <CreatorCenter />
-      }
+      },
+      {
+        path: '/search',
+        element: <Search />,
+      },
+      {
+        path: '/testAudioPage',
+        element: <MusicPlayer />,
+      },
+      {
+        path: '/songdetail',
+        element: <SongDetail />,
+      }, {
+        path: '/login',
+        element: <LoginPage />,
+      }, {
+        path: '/LoginContent',
+        element: <LoginContent />,
+      },
     ],
   },
 ])

+ 28 - 23
src/utils/request.ts

@@ -1,40 +1,45 @@
 import axios from 'axios'
 import router from '../router/index'
 import { getToken, removeToken, removeUserinfo } from './index'
+import { message } from 'antd';
 const request = axios.create({
   baseURL: '/api',
   timeout: 100000
 })
 
 // 添加请求拦截器
-request.interceptors.request.use((config)=> {
-  const token=getToken()
-  if(token){
+request.interceptors.request.use((config) => {
+  const token = getToken()
+  if (token) {
     console.log(token.access_token);
-    config.headers.Authorization=`Bearer ${token.access_token}`
+    config.headers.Authorization = `Bearer ${token.access_token}`
   }
-    return config
-  }, (error)=> {
-    return Promise.reject(error)
+  return config
+}, (error) => {
+  return Promise.reject(error)
 })
 
 // 添加响应拦截器
-request.interceptors.response.use((response)=> {
-    // 2xx 范围内的状态码都会触发该函数。
-    // 对响应数据做点什么
-    return response.data
-  }, (error)=> {
-    console.log(error);
-    
-    // 超出 2xx 范围的状态码都会触发该函数。
-    // 对响应错误做点什么
-    // if(error.response.status===401){
-    //   removeToken()
-    //   removeUserinfo()
-    //   router.navigate('/find/recommend')
-    //   window.location.reload()
-    // }
-    return Promise.reject(error)
+request.interceptors.response.use((response) => {
+  // 2xx 范围内的状态码都会触发该函数。
+  // 对响应数据做点什么
+  return response.data
+}, (error) => {
+  console.log(error);
+
+  // 超出 2xx 范围的状态码都会触发该函数。
+  // 对响应错误做点什么
+  if (error.response.status === 401) {
+    message.error('登录已过期,请重新登录')
+    removeToken()
+    removeUserinfo()
+    router.navigate('/find/recommend')
+    window.location.reload()
+  }
+  if (error.response.status === 500) {
+    message.error(error.response.data.message)
+  }
+  return Promise.reject(error)
 })
 //路由前置守卫
 export { request }