Bläddra i källkod

生成消费订单

xiang 14 timmar sedan
förälder
incheckning
9611445a0b
34 ändrade filer med 1234 tillägg och 274 borttagningar
  1. 7 0
      src/apis/chart.ts
  2. 4 0
      src/apis/chartItem.ts
  3. 4 0
      src/pages/layout/components/Wy_Header/components/Wy_Avatar/index.tsx
  4. 44 0
      src/pages/layout/components/payLoading/index.css
  5. 44 0
      src/pages/layout/components/payLoading/index.less
  6. 23 0
      src/pages/layout/components/payLoading/index.tsx
  7. 1 1
      src/pages/layout/pages/artistinfo/index.tsx
  8. 3 1
      src/pages/layout/pages/find/rank/compomemts/CommentInput/index.css
  9. 3 1
      src/pages/layout/pages/find/rank/compomemts/CommentInput/index.less
  10. 14 5
      src/pages/layout/pages/find/rank/compomemts/RankPlaylist/index.tsx
  11. 41 9
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_left/index.css
  12. 55 10
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_left/index.less
  13. 59 34
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_left/index.tsx
  14. 1 1
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list/index.less
  15. 137 93
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list/index.tsx
  16. 10 12
      src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_right/index.tsx
  17. 52 13
      src/pages/layout/pages/find/rank/index.tsx
  18. 16 5
      src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill/index.tsx
  19. 5 0
      src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill_item/index.css
  20. 19 2
      src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill_item/index.less
  21. 67 27
      src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill_item/index.tsx
  22. 2 3
      src/pages/layout/pages/find/singer/index.tsx
  23. 1 1
      src/pages/layout/pages/mine/components/PlaylistDetail/PlaylistHeader/index.tsx
  24. 100 0
      src/pages/layout/pages/payError/index.css
  25. 125 0
      src/pages/layout/pages/payError/index.less
  26. 67 36
      src/pages/layout/pages/payError/index.tsx
  27. 98 0
      src/pages/layout/pages/paySuccess/index.css
  28. 121 0
      src/pages/layout/pages/paySuccess/index.less
  29. 52 15
      src/pages/layout/pages/paySuccess/index.tsx
  30. 6 0
      src/pages/layout/pages/pushSong/index.css
  31. 6 0
      src/pages/layout/pages/pushSong/index.less
  32. 26 3
      src/pages/layout/pages/pushSong/index.tsx
  33. 1 1
      src/pages/layout/pages/testPage/index.tsx
  34. 20 1
      src/router/index.tsx

+ 7 - 0
src/apis/chart.ts

@@ -0,0 +1,7 @@
+import { request } from "@/utils";
+export const getChartListApi = () => {
+  return request(`/chart/list`)
+}
+export const getChartRecommendListApi = () => {
+  return request(`/chart/recommend`)
+}

+ 4 - 0
src/apis/chartItem.ts

@@ -0,0 +1,4 @@
+import { request } from "@/utils";
+export const getChartitemApi = (id: number) => {
+  return request(`/chart-item/item`, { params: { id } })
+}

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

@@ -6,6 +6,7 @@ import {
   MessageOutlined,
   SettingOutlined,
   StarOutlined,
+  TransactionOutlined,
   UserOutlined,
 } from '@ant-design/icons';
 import { Avatar, Badge, Modal, Popover } from 'antd';
@@ -91,6 +92,9 @@ const Wy_Avatar = () => {
         <div>
           <SettingOutlined /> 个人设置
         </div>
+        <div>
+          <TransactionOutlined /> 余额充值
+        </div>
         <div>
           <IdcardOutlined /> 实名认证
         </div>

+ 44 - 0
src/pages/layout/components/payLoading/index.css

@@ -0,0 +1,44 @@
+.musician-loading-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 9999;
+}
+.musician-loading-overlay .musician-loading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: white;
+  padding: 30px;
+  border-radius: 10px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+.musician-loading-overlay .musician-loading .spinner {
+  width: 40px;
+  height: 40px;
+  border: 4px solid #f3f3f3;
+  border-top: 4px solid #3498db;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+}
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+.musician-loading-overlay .musician-loading .loading-text {
+  margin-top: 15px;
+  font-size: 16px;
+  color: #333;
+  font-weight: 500;
+}

+ 44 - 0
src/pages/layout/components/payLoading/index.less

@@ -0,0 +1,44 @@
+.musician-loading-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 9999;
+  
+  .musician-loading {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    background: white;
+    padding: 30px;
+    border-radius: 10px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    
+    .spinner {
+      width: 40px;
+      height: 40px;
+      border: 4px solid #f3f3f3;
+      border-top: 4px solid #3498db;
+      border-radius: 50%;
+      animation: spin 1s linear infinite;
+      
+      @keyframes spin {
+        0% { transform: rotate(0deg); }
+        100% { transform: rotate(360deg); }
+      }
+    }
+    
+    .loading-text {
+      margin-top: 15px;
+      font-size: 16px;
+      color: #333;
+      font-weight: 500;
+    }
+  }
+}

+ 23 - 0
src/pages/layout/components/payLoading/index.tsx

@@ -0,0 +1,23 @@
+import './index.css'
+interface PayLoadingProps {
+  visible?: boolean;
+  text?: string;
+}
+
+const PayLoading: React.FC<PayLoadingProps> = ({ 
+  visible = true, 
+  text = '正在加载...'
+}) => {
+  if (!visible) return null;
+  
+  return (
+    <div className="musician-loading-overlay">
+      <div className="musician-loading">
+        <div className="spinner"></div>
+        <div className="loading-text">{text}</div>
+      </div>
+    </div>
+  );
+};
+
+export default PayLoading;

+ 1 - 1
src/pages/layout/pages/artistinfo/index.tsx

@@ -95,7 +95,7 @@ const Artistinfo = () => {
           />
 
           {/* 收藏按钮覆盖在图片上 */}
-          {artinfo.id !== singerInfo.id && (
+          { (!artinfo || artinfo.id !== singerInfo.id) && (
             <Button
               type="primary"
               shape="circle"

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

@@ -14,7 +14,9 @@
   position: relative;
   padding-left: 10px;
   margin-left: 10px;
-  width: 600px;
+  width: auto;
+  padding-right: 40px;
+  overflow: hidden;
 }
 .comment-input-container .comment-input-content .comment-input-box .comment-input-button-group {
   display: flex;

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

@@ -14,7 +14,9 @@
       position: relative;
       padding-left: 10px;
       margin-left: 10px;
-      width: 600px;
+      width: auto;
+      padding-right: 40px;
+      overflow: hidden;
       .comment-input-button-group {
         display: flex;
         justify-content: space-between;

+ 14 - 5
src/pages/layout/pages/find/rank/compomemts/RankPlaylist/index.tsx

@@ -3,13 +3,22 @@ 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 = () => {
+interface Props {
+  selectedRankId: number;
+  selectedRank: any; // 根据实际情况定义类型
+}
+
+const RankPlaylist: React.FC<Props> = ({ selectedRankId, selectedRank }) => {
   return (
     <div style={{ marginBottom: '40px' }}>
-      <Rank_Recommend_body_right />
-      <Rank_Recommend_body_list />
-      <Rank_Recommend_body_list_Comment contentId={"123"} type={2} />
-      <Rank_Recommend_body_list_Comment_list contentId={"111"} type={2} />
+      <Rank_Recommend_body_right 
+        selectedRank={selectedRank} 
+      />
+      <Rank_Recommend_body_list 
+        selectedRankId={selectedRankId} 
+      />
+      <Rank_Recommend_body_list_Comment contentId={selectedRankId} type={3} />
+      <Rank_Recommend_body_list_Comment_list contentId={selectedRankId} type={3} />
     </div>
   )
 }

+ 41 - 9
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_left/index.css

@@ -1,20 +1,52 @@
 .Rank_Recommend_body_left {
-  margin-left: 20px;
+  padding: 16px;
+  background-color: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0);
 }
 .Rank_Recommend_body_left .Rank_Recommend_body_left_title {
-  padding-top: 30px;
-  padding-bottom: 20px;
-  font-family: SimSun, STSong, '宋体', serif;
-  font-weight: bolder;
+  font-size: 18px;
+  font-weight: bold;
+  margin-bottom: 16px;
+  color: #333;
 }
 .Rank_Recommend_body_left .Rank_Recommend_body_left_content {
   display: flex;
-  padding: 10px 0;
+  align-items: center;
+  padding: 8px 0;
+  border-bottom: 1px solid #eee;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content:hover {
+  background-color: #f5f5f5;
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content.selected {
+  background-color: #e6f7ff;
+  border-radius: 4px;
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content.selected .Rank_Recommend_body_left_content_left .ant-image {
+  transform: scale(1.05);
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content.selected .Rank_Recommend_body_left_content_right_top {
+  color: #1890ff;
+  font-weight: 600;
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content:last-child {
+  border-bottom: none;
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content .Rank_Recommend_body_left_content_left {
+  margin-right: 12px;
 }
 .Rank_Recommend_body_left .Rank_Recommend_body_left_content .Rank_Recommend_body_left_content_right {
-  padding-left: 10px;
+  flex: 1;
+}
+.Rank_Recommend_body_left .Rank_Recommend_body_left_content .Rank_Recommend_body_left_content_right .Rank_Recommend_body_left_content_right_top {
+  font-weight: 500;
+  color: #333;
+  margin-bottom: 4px;
 }
 .Rank_Recommend_body_left .Rank_Recommend_body_left_content .Rank_Recommend_body_left_content_right .Rank_Recommend_body_left_content_right_bottom {
-  margin-top: 10px;
-  color: #8e9092;
+  font-size: 12px;
+  color: #999;
 }

+ 55 - 10
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_left/index.less

@@ -1,20 +1,65 @@
 .Rank_Recommend_body_left {
-  margin-left: 20px;
+  padding: 16px;
+  background-color: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0);
+  
   .Rank_Recommend_body_left_title {
-    padding-top: 30px;
-    padding-bottom: 20px;
-    font-family: SimSun, STSong, '宋体', serif;
-    font-weight: bolder;
+    font-size: 18px;
+    font-weight: bold;
+    margin-bottom: 16px;
+    color: #333;
   }
+  
   .Rank_Recommend_body_left_content {
     display: flex;
-    padding: 10px 0;
+    align-items: center;
+    padding: 8px 0;
+    border-bottom: 1px solid #eee;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    
+    &:hover {
+      background-color: #f5f5f5;
+    }
+    
+    &.selected {
+      background-color: #e6f7ff;
+      border-radius: 4px;
+      
+      .Rank_Recommend_body_left_content_left {
+        .ant-image {
+          transform: scale(1.05);
+        }
+      }
+      
+      .Rank_Recommend_body_left_content_right_top {
+        color: #1890ff;
+        font-weight: 600;
+      }
+    }
+    
+    &:last-child {
+      border-bottom: none;
+    }
+    
+    .Rank_Recommend_body_left_content_left {
+      margin-right: 12px;
+    }
+    
     .Rank_Recommend_body_left_content_right {
-      padding-left: 10px;
+      flex: 1;
+      
+      .Rank_Recommend_body_left_content_right_top {
+        font-weight: 500;
+        color: #333;
+        margin-bottom: 4px;
+      }
+      
       .Rank_Recommend_body_left_content_right_bottom {
-        margin-top: 10px;
-        color: rgb(142, 144, 146);
+        font-size: 12px;
+        color: #999;
       }
     }
   }
-}
+}

+ 59 - 34
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_left/index.tsx

@@ -1,46 +1,71 @@
 import { Image } from 'antd'
 import './index.css'
-const Rank_Recommend_body_left = () => {
-  // const Rank_Recommend_body_left_content = () => {}
+import { useState, useEffect } from 'react'
+
+// 定义排行榜数据类型
+interface RankItem {
+  id: number;
+  name: string;
+  updateInfo: string;
+  imageUrl: string
+}
+
+interface Props {
+  chartList: RankItem[];
+  onchange: (id: number) => void;
+  selectedId: number;
+}
+
+const Rank_Recommend_body_left: React.FC<Props> = ({ chartList, onchange, selectedId }) => {
+  // 状态管理:记录当前选中的项
+  const [localSelectedId, setLocalSelectedId] = useState<number>(selectedId)
+
+  // 处理点击事件
+  const handleItemClick = (id: number) => {
+    setLocalSelectedId(id)
+    onchange(id) // 调用父组件传递的回调函数
+  }
+
+  // 当外部selectedId改变时同步本地状态
+  useEffect(() => {
+    setLocalSelectedId(selectedId)
+  }, [selectedId])
 
   return (
     <div className="Rank_Recommend_body_left">
       <div className="Rank_Recommend_body_left_title">云音乐特色榜</div>
-      <div className="Rank_Recommend_body_left_content">
-        <div className="Rank_Recommend_body_left_content_left">
-          <Image
-            preview={false}
-            width={40}
-            src="https://p1.music.126.net/rIi7Qzy2i2Y_1QD7cd0MYA==/109951170048506929.jpg?param=40y40"
-          ></Image>
-        </div>
-        <div className="Rank_Recommend_body_left_content_right">
-          <div className="Rank_Recommend_body_left_content_right_top">
-            飙升榜
-          </div>
-          <div className="Rank_Recommend_body_left_content_right_bottom">
-            更新71首
-          </div>
-        </div>
-      </div>
-      <div className="Rank_Recommend_body_left_content">
-        <div className="Rank_Recommend_body_left_content_left">
-          <Image
-            preview={false}
-            width={40}
-            src="https://p1.music.126.net/rIi7Qzy2i2Y_1QD7cd0MYA==/109951170048506929.jpg?param=40y40"
-          ></Image>
-        </div>
-        <div className="Rank_Recommend_body_left_content_right">
-          <div className="Rank_Recommend_body_left_content_right_top">
-            飙升榜
+
+      {/* 渲染排行榜列表 */}
+      {chartList.map((item) => (
+        <div
+          key={item.id}
+          className={`Rank_Recommend_body_left_content ${localSelectedId === item.id ? 'selected' : ''
+            }`}
+          onClick={() => handleItemClick(item.id)}
+        >
+          <div className="Rank_Recommend_body_left_content_left">
+            <Image
+              preview={false}
+              width={40}
+              src={item.coverUrl}
+              style={{
+                borderRadius: '4px',
+                padding: '4px',
+              }}
+            />
           </div>
-          <div className="Rank_Recommend_body_left_content_right_bottom">
-            更新71首
+          <div className="Rank_Recommend_body_left_content_right">
+            <div className="Rank_Recommend_body_left_content_right_top">
+              {item.chartName}
+            </div>
+            <div className="Rank_Recommend_body_left_content_right_bottom">
+              {item.updateInfo}
+            </div>
           </div>
         </div>
-      </div>
+      ))}
     </div>
   )
 }
-export default Rank_Recommend_body_left
+
+export default Rank_Recommend_body_left

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

@@ -92,4 +92,4 @@
       }
     }
   }
-}
+}

+ 137 - 93
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_list/index.tsx

@@ -1,81 +1,111 @@
-import { PlayCircleOutlined } from '@ant-design/icons'
+import {  CaretRightOutlined } from '@ant-design/icons'
 import './index.css'
 import { Image } from 'antd'
-const Rank_Recommend_ist = [
-  {
-    id: 1,
-    rank: 1,
-    addRank: 68,
-    title: '两难',
-    time: '01:34',
-    singer: '加木',
-    img: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-  },
-  {
-    id: 2,
-    rank: 2,
-    addRank: 30,
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-  },
-  {
-    id: 3,
-    rank: 3,
-    addRank: 'new',
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-  },
-  {
-    id: 4,
-    rank: 4,
-    addRank: 'new',
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: '',
-  },
-  {
-    id: 4,
-    rank: 4,
-    addRank: 'new',
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: '',
-  },
-  {
-    id: 4,
-    rank: 4,
-    addRank: 'new',
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: '',
-  },
-  {
-    id: 4,
-    rank: 4,
-    addRank: 'new',
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: '',
-  },
-  {
-    id: 4,
-    rank: 4,
-    addRank: 'new',
-    title: '青花瓷',
-    time: '01:34',
-    singer: '周杰伦',
-    img: '',
-  },
-]
-const Rank_Recommend_body_list = () => {
+import { getChartitemApi } from '@/apis/chartItem';
+import { useEffect, useState } from 'react';
+import { useMusicPlayer } from '@/context/MusicPlayerContext';
+import { useNavigate } from 'react-router-dom';
+import type { Song } from '@/type/Song';
+
+// 定义 API 返回的数据类型
+interface ChartSongVO {
+  songId: number;
+  songName: string;
+  fileUrl: string;
+  duration: number; // 秒数
+  singerName: string | null;
+  coverUrl: string;
+  playCount: number;
+  chartRank: number;
+  previousRank: number;
+  changeStatus: number; // 0-无变化,1-上升,2-下降,3-新上榜,4-重新上榜
+}
+
+interface Props {
+  selectedRankId: number;
+}
+
+const Rank_Recommend_body_list: React.FC<Props> = ({ selectedRankId }) => {
+  const [chartData, setChartData] = useState<ChartSongVO[]>([]);
+  
+  const {
+    setPlaylist,
+    setCurrentTrackIndex,
+    setIsPlaying
+  } = useMusicPlayer();
+  
+  const navigate = useNavigate();
+
+  // 格式化时间显示
+  const formatDuration = (seconds: number): string => {
+    const mins = Math.floor(seconds / 60);
+    const secs = seconds % 60;
+    return `${mins}:${secs < 10 ? '0' + secs : secs}`;
+  };
+
+  // 获取排行榜数据
+  const getitemFun = async () => {
+    try {
+      const response = await getChartitemApi(selectedRankId);
+      if (response.data && response.data.chartSongs) {
+        setChartData(response.data.chartSongs);
+        
+        // 将API数据转换为Song类型并设置播放列表
+        const songs: Song[] = response.data.chartSongs.map((song: ChartSongVO) => ({
+          id: song.songId,
+          title: song.songName,
+          artist: song.singerName || '未知艺术家',
+          url: song.fileUrl,
+          cover: song.coverUrl,
+          duration: formatDuration(song.duration),
+          album: '排行榜',
+          composer: '',
+          arranger: '',
+          description: `排名: ${song.chartRank}, 变化: ${song.changeStatus}`,
+          playCount: song.playCount
+        }));
+        
+        setPlaylist(songs);
+      }
+    } catch (error) {
+      console.error('获取排行榜数据失败:', error);
+    }
+  };
+
+  // 处理播放按钮点击
+  const handlePlayClick = (index: number, event: React.MouseEvent) => {
+    event.stopPropagation(); // 阻止事件冒泡,避免触发跳转
+    setCurrentTrackIndex(index);
+    setIsPlaying(true);
+  };
+  
+  // 处理整个项目的点击(跳转到详情页)
+  const handleItemClick = (songId: number) => {
+    navigate(`/SongDetail/${songId}`);
+  };
+
+  useEffect(() => {
+    getitemFun();
+  }, [selectedRankId]);
+
+  // 根据 changeStatus 显示排名变化
+  const getRankChangeDisplay = (song: ChartSongVO) => {
+    switch(song.changeStatus) {
+      case 0:
+        return <span className="rank-no-change">—</span>;
+      case 1:
+        return <span className="rank-up">{song.previousRank - song.chartRank}</span>;
+      case 2:
+        return <span className="rank-down">{song.previousRank - song.chartRank}</span>;
+      case 3:
+        return <span className="rank-new">new</span>;
+      case 4:
+        return <span className="rank-reappear">re</span>;
+      default:
+        return <span className="rank-no-change">—</span>;
+    }
+  };
+
   return (
     <div className="Rank_Recommend_body_list">
       <div className="Rank_Recommend_body_list_title">
@@ -85,40 +115,53 @@ const Rank_Recommend_body_list = () => {
         <div className="Rank_Recommend_body_list_title_singer">歌手</div>
       </div>
       <div className="Rank_Recommend_body_list_content">
-        {Rank_Recommend_ist.map((item, index) => (
-          <div className="Rank_Recommend_body_list_content_border" key={index}>
+        {chartData.map((song, index) => (
+          <div 
+            className="Rank_Recommend_body_list_content_border" 
+            key={song.songId}
+            onClick={() => handleItemClick(song.songId)}
+            style={{ cursor: 'pointer' }}
+          >
             <div className="Rank_Recommend_body_list_content_first">
-              <div>{item.rank}</div>
+              <div>{song.chartRank}</div>
               <div
                 className={
-                  item.addRank === 'new'
+                  song.changeStatus === 3
                     ? 'rank-new'
-                    : typeof item.addRank === 'number' && item.addRank > 0
-                    ? 'rank-up'
-                    : typeof item.addRank === 'number' && item.addRank < 0
-                    ? 'rank-down'
-                    : ''
+                    : song.changeStatus === 1
+                      ? 'rank-up'
+                      : song.changeStatus === 2
+                        ? 'rank-down'
+                        : song.changeStatus === 4
+                          ? 'rank-reappear'
+                          : 'rank-no-change'
                 }
               >
-                {item.addRank}
+                {getRankChangeDisplay(song)}
               </div>
             </div>
             <div className="Rank_Recommend_body_list_content_content">
               {index + 1 <= 3 && (
-                <Image src={item.img} width={50} height={50} />
+                <Image preview={false} src={song.coverUrl} width={50} height={50} />
               )}
-              <div className="Rank_Recommend_body_list_content_content_icon">
-                <PlayCircleOutlined />
+              <div 
+                className="Rank_Recommend_body_list_content_content_icon"
+                onClick={(event) => handlePlayClick(index, event)}
+              >
+
+                <CaretRightOutlined 
+                  className="play-icon"
+                />
               </div>
               <div className="Rank_Recommend_body_list_content_content_singTitle">
-                {item.title}
+                {song.songName}
               </div>
             </div>
             <div className="Rank_Recommend_body_list_content_time">
-              {item.time}
+              {formatDuration(song.duration)}
             </div>
             <div className="Rank_Recommend_body_list_content_singer">
-              {item.singer}
+              {song.singerName || '未知艺术家'}
             </div>
           </div>
         ))}
@@ -126,4 +169,5 @@ const Rank_Recommend_body_list = () => {
     </div>
   )
 }
-export default Rank_Recommend_body_list
+
+export default Rank_Recommend_body_list;

+ 10 - 12
src/pages/layout/pages/find/rank/compomemts/Rank_Recommend_body_right/index.tsx

@@ -2,14 +2,12 @@ import { Button, Image } from 'antd'
 import './index.css'
 import {
   ClockCircleOutlined,
-  FolderAddOutlined,
-  MessageOutlined,
   PlayCircleOutlined,
-  PlusOutlined,
-  ShareAltOutlined,
   VerticalAlignBottomOutlined,
 } from '@ant-design/icons'
-const Rank_Recommend_body_right = () => {
+const Rank_Recommend_body_right = ({ selectedRank }) => {
+  console.log(selectedRank);
+
   return (
     <div className="Rank_Recommend_body_right">
       <div className="Rank_Recommend_body_right_title">
@@ -17,22 +15,22 @@ const Rank_Recommend_body_right = () => {
           <Image
             preview={false}
             width={150}
-            src="https://p1.music.126.net/rIi7Qzy2i2Y_1QD7cd0MYA==/109951170048506929.jpg?param=150y150"
+            src={selectedRank.coverUrl}
           />
         </div>
         <div className="Rank_Recommend_body_right_title_Content">
           <div className="Rank_Recommend_body_right_title_Content_title">
-            飙升榜
+            {selectedRank.chartName}
           </div>
           <div className="Rank_Recommend_body_right_title_Content_Time">
-            <ClockCircleOutlined /> 最近更新:06月23日 (更新71首)
+            <ClockCircleOutlined /> 最近更新:{selectedRank.createDate}
           </div>
           <div className="Rank_Recommend_body_right_title_Content_Button">
             <Button type="primary">
               <PlayCircleOutlined />
               播放
             </Button>
-            <Button type="primary">
+            {/* <Button type="primary">
               <PlusOutlined />
             </Button>
             <Button
@@ -45,14 +43,14 @@ const Rank_Recommend_body_right = () => {
             <Button>
               <ShareAltOutlined />
               {'(123123)'}
-            </Button>
+            </Button> */}
             <Button>
               <VerticalAlignBottomOutlined />
               下载
             </Button>
-            <Button style={{width: '90px'}}>
+            {/* <Button style={{width: '90px'}}>
               <MessageOutlined /> {'(123123)'}
-            </Button>
+            </Button> */}
           </div>
         </div>
       </div>

+ 52 - 13
src/pages/layout/pages/find/rank/index.tsx

@@ -1,21 +1,60 @@
+import { getChartListApi } from '@/apis/chart'
 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 RankPlaylist from './compomemts/RankPlaylist'
 import './index.css'
+import { useEffect, useState } from 'react'
+
 const Rank = () => {
+  const [chartList, setchartList] = useState([])
+  const [selectedRankId, setSelectedRankId] = useState<number>(1)
+  
+  const getChartListFun = async () => {
+    try {
+      const res = await getChartListApi()
+      setchartList(res.data)
+      // 如果有数据,设置默认选中第一个
+      if (res.data && res.data.length > 0) {
+        setSelectedRankId(res.data[0]?.id || 1)
+      }
+    } catch (error) {
+      console.error('获取排行榜数据失败:', error)
+    }
+  }
+
+  useEffect(() => {
+    getChartListFun()
+  }, [])
+
+  const onChange = (id: number) => {
+    setSelectedRankId(id)
+  }
+
+  // 获取当前选中的排行榜项
+  const currentSelectedRank = chartList.find(item => item.id === selectedRankId) || null
+
   return (
-    <div className="Rank">
-      <div className="Rank_left">
-        <Rank_Recommend_body_left />
-        <Rank_Recommend_body_left />
-      </div>
-      <div className="Rank_right">
-        <RankPlaylist></RankPlaylist>
-      </div>
+    <div>
+      {chartList.length > 0 ? (
+        <div className="Rank">
+          <div className="Rank_left">
+            <Rank_Recommend_body_left
+              chartList={chartList}
+              onchange={onChange}
+              selectedId={selectedRankId}
+            />
+          </div>
+          <div className="Rank_right">
+            <RankPlaylist 
+              selectedRankId={selectedRankId} 
+              selectedRank={currentSelectedRank}
+            />
+          </div>
+        </div>
+      ) : (
+        <div>加载中...</div>
+      )}
     </div>
   )
 }
-export default Rank
+
+export default Rank

+ 16 - 5
src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill/index.tsx

@@ -1,16 +1,27 @@
+import { getChartRecommendListApi } from '@/apis/chart'
 import Recommend_content_bill_item from '../Recommend_content_bill_item'
 import Recommend_content_title from '../Recommend_content_title'
 import './index.css'
+import { useEffect, useState } from 'react'
 
 const Recommend_content_bill = () => {
-const title='榜单'
+  const [list, setList] = useState([])
+  const getListFun = async () => {
+    const res = await getChartRecommendListApi()
+    setList(res.data)
+  }
+  useEffect(() => {
+    getListFun()
+  }, [])
+  const title = '榜单'
   return (
     <div>
-      <Recommend_content_title title={title} />
+      <Recommend_content_title title={title} morePath='/find/rank' />
       <div className='Recommend_content_bill_item_container'>
-        <Recommend_content_bill_item />
-        <Recommend_content_bill_item />
-        <Recommend_content_bill_item />
+        {list.map((item, index) => (
+          <Recommend_content_bill_item item={item} key={item.id} />
+        ))}
+
       </div>
     </div>
   )

+ 5 - 0
src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill_item/index.css

@@ -13,6 +13,7 @@
   font-size: 30px;
   margin-top: 10px;
   color: #9c9c9c;
+  cursor: pointer;
 }
 .recommend_Bill_List .recommend_Bill_List_item .recommend_Bill_List_item_content .recommend_Bill_List_item_right .recommend_Bill_List_item_right_icon :nth-child(2) {
   margin-left: 10px;
@@ -25,6 +26,7 @@
   display: flex;
   height: 30px;
   line-height: 30px;
+  cursor: pointer;
 }
 .recommend_Bill_List .recommend_Bill_List_item .recommend_Bill_List_item_content_bottom .recommend_Bill_List_item_content_bottom_item:nth-child(even) {
   background-color: #ffffff;
@@ -37,6 +39,9 @@
 }
 .recommend_Bill_List .recommend_Bill_List_item .recommend_Bill_List_item_content_bottom .recommend_Bill_List_item_content_bottom_item .title {
   flex: 1;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 .recommend_Bill_List .recommend_Bill_List_item .recommend_Bill_List_item_content_bottom > .recommend_Bill_List_item_content_bottom_item:nth-child(-n + 3) .num {
   color: #ff0000;

+ 19 - 2
src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill_item/index.less

@@ -3,27 +3,36 @@
     width: 230px;
     padding: 20px;
     border: 1px solid rgb(156, 156, 156);
+
     .recommend_Bill_List_item_content {
       display: flex;
+
       .recommend_Bill_List_item_right {
         margin-left: 20px;
+
         .recommend_Bill_List_item_right_icon {
           font-size: 30px;
           margin-top: 10px;
           color: rgb(156, 156, 156);
+          cursor: pointer;
         }
+
         .recommend_Bill_List_item_right_icon :nth-child(2) {
           margin-left: 10px;
         }
       }
     }
+
     .recommend_Bill_List_item_content_bottom {
       margin-top: 10px;
+
       .recommend_Bill_List_item_content_bottom_item {
         width: 190px;
         display: flex;
         height: 30px;
         line-height: 30px;
+        cursor: pointer;
+
         &:nth-child(even) {
           background-color: #ffffff; // 奇数项白色背景
         }
@@ -31,19 +40,27 @@
         &:nth-child(odd) {
           background-color: #e7e4e4; // 偶数项浅灰色背景
         }
+
         .num {
           margin: auto 10px;
         }
+
         .title {
           flex: 1;
+          white-space: nowrap; // 不换行
+          overflow: hidden; // 超出部分隐藏
+          text-overflow: ellipsis; // 超出部分显示省略号
         }
+
         //最后一个节点
       }
-      & > .recommend_Bill_List_item_content_bottom_item:nth-child(-n + 3) {
+
+      &>.recommend_Bill_List_item_content_bottom_item:nth-child(-n + 3) {
         .num {
           color: rgb(255, 0, 0);
         }
       }
+
       .recommend_Bill_List_item_content_bottom_item:nth-last-child(1) {
         text-align: right;
         justify-content: flex-end; // 内容右对齐
@@ -51,4 +68,4 @@
       }
     }
   }
-}
+}

+ 67 - 27
src/pages/layout/pages/find/recommend/components/Recommend_body_left/components/Recommend_content_bill_item/index.tsx

@@ -1,33 +1,71 @@
 import { Image } from 'antd'
 import { FolderAddOutlined, PlayCircleOutlined } from '@ant-design/icons'
 import './index.css'
-const Bill = [
-  {
-    id: 0,
-    image:
-      'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-    title: '飙升榜',
-    data: [
-      { id: 0, Mname: '倒影里的星星' },
-      { id: 1, Mname: '倒影里的星星' },
-      { id: 2, Mname: '倒影里的星星' },
-      { id: 2, Mname: '倒影里的星星' },
-      { id: 2, Mname: '倒影里的星星' },
-      { id: 2, Mname: '倒影里的星星' },
-      { id: 2, Mname: '倒影里的星星' },
-      { id: 2, Mname: '倒影里的星星' },
-    ],
-  },
-]
-const Recommend_content_bill_item = () => {
+import { useMusicPlayer } from '@/context/MusicPlayerContext'
+import type { Song } from '@/type/Song'
+import { useNavigate } from 'react-router-dom'
+
+interface SongItem {
+  id: number;
+  songName: string;
+  singerName: string | null;
+  fileUrl: string;
+  coverUrl: string;
+  duration: number;
+}
+
+interface ChartItem {
+  chart: {
+    id: number;
+    chartName: string;
+    coverUrl: string;
+  };
+  songList: SongItem[];
+}
+
+const Recommend_content_bill_item = ({ item }: { item: ChartItem }) => {
+
+  const navigate = useNavigate();
+  const {
+    setPlaylist,
+    setCurrentTrackIndex,
+    setIsPlaying
+  } = useMusicPlayer();
+
+  const handlePlayClick = (index: number) => {
+    // 将当前榜单的歌曲列表转换为播放列表
+    const songs: Song[] = item.songList.map(song => ({
+      id: song.id,
+      title: song.songName,
+      artist: song.singerName || '未知艺术家',
+      url: song.fileUrl,
+      cover: song.coverUrl,
+      duration: `${Math.floor(song.duration / 60)}:${song.duration % 60 < 10 ? '0' + song.duration % 60 : song.duration % 60}`,
+      album: item.chart.chartName,
+      composer: '',
+      arranger: '',
+      description: '',
+      playCount: 0
+    }));
+
+    setPlaylist(songs);
+
+    setCurrentTrackIndex(index);
+    setIsPlaying(true);
+  };
+
+  const goPage = () => {
+    navigate('/find/rank');
+  }
+
   return (
     <div>
       <div className="recommend_Bill_List">
         <div className="recommend_Bill_List_item">
           <div className="recommend_Bill_List_item_content">
-            <Image preview={false} width={80} src={Bill[0].image} />
+            <Image preview={false} width={80} src={item.chart.coverUrl} />
             <div className="recommend_Bill_List_item_right">
-              <div>{Bill[0].title}</div>
+              <div>{item.chart.chartName}</div>
               <div className="recommend_Bill_List_item_right_icon">
                 <PlayCircleOutlined />
                 <FolderAddOutlined />
@@ -35,16 +73,17 @@ const Recommend_content_bill_item = () => {
             </div>
           </div>
           <div className="recommend_Bill_List_item_content_bottom">
-            {Bill[0].data.map((item, index) => (
+            {item.songList.map((song, index) => (
               <div
                 className="recommend_Bill_List_item_content_bottom_item"
-                key={index}
+                key={song.id}
+                onClick={() => handlePlayClick(index)}
               >
-                <div className="num">{index+1}</div>
-                <div className="title">{item.Mname}</div>
+                <div className="num">{index + 1}</div>
+                <div className="title">{song.songName}</div>
               </div>
             ))}
-            <div className="recommend_Bill_List_item_content_bottom_item">
+            <div className="recommend_Bill_List_item_content_bottom_item" onClick={goPage}>
               <div className="title">查看全部{'>'}</div>
             </div>
           </div>
@@ -53,4 +92,5 @@ const Recommend_content_bill_item = () => {
     </div>
   )
 }
-export default Recommend_content_bill_item
+
+export default Recommend_content_bill_item

+ 2 - 3
src/pages/layout/pages/find/singer/index.tsx

@@ -26,9 +26,8 @@ const Singer = () => {
       id: -1, // 特殊值,表示不按地区筛选,只按性别筛选
       title: '推荐歌手',
       data: [
-        { id: 0, title: '推荐歌手', region: -1, gender: 0 },
-        { id: 1, title: '入驻歌手', region: -1, gender: 1 },
-        { id: 2, title: '热门歌手', region: -1, gender: 2 },
+        { id: 0, title: '推荐歌手', region: -1, gender: null },
+        { id: 1, title: '热门歌手', region: -2, gender: null },
       ],
     },
     {

+ 1 - 1
src/pages/layout/pages/mine/components/PlaylistDetail/PlaylistHeader/index.tsx

@@ -196,7 +196,7 @@ const PlaylistHeader: React.FC<PlaylistHeaderProps> = ({
           >
             +
           </Button>
-          {showCollect && artinfo.id != playlist.artistId && (
+          {showCollect && (
             isCollection ? (
               <Button
                 loading={loading}

+ 100 - 0
src/pages/layout/pages/payError/index.css

@@ -0,0 +1,100 @@
+.pay-error-container {
+  min-height: 80vh;
+  background: linear-gradient(135deg, #fef0ef 0%, #e2c3c3 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+}
+.pay-error-container .pay-error-wrapper {
+  max-width: 600px;
+  width: 100%;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+  overflow: hidden;
+  padding: 40px 20px;
+}
+.pay-error-container .pay-error-wrapper .ant-result {
+  padding: 0 20px;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-title {
+  font-size: 28px;
+  font-weight: 600;
+  color: #ff4d4f;
+  margin-bottom: 16px;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-subtitle {
+  color: #8c8c8c;
+  font-size: 16px;
+  margin-bottom: 24px;
+}
+.pay-error-container .pay-error-wrapper .ant-result .error-desc {
+  margin: 20px 0;
+}
+.pay-error-container .pay-error-wrapper .ant-result .error-desc .ant-typography {
+  margin-bottom: 12px;
+}
+.pay-error-container .pay-error-wrapper .ant-result .error-desc .ant-typography .ant-typography strong {
+  color: #262626;
+}
+.pay-error-container .pay-error-wrapper .ant-result .error-desc .error-icon {
+  color: #ff4d4f;
+  margin-right: 8px;
+  font-size: 16px;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-extra {
+  margin-top: 30px;
+  display: flex;
+  gap: 16px;
+  justify-content: center;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-extra .action-btn {
+  min-width: 120px;
+  font-size: 16px;
+  height: 44px;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-extra .action-btn.home-btn {
+  background: #ff4d4f;
+  border-color: #ff4d4f;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-extra .action-btn.home-btn:hover {
+  background: #ff7875;
+  border-color: #ff7875;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-extra .action-btn.retry-btn {
+  background: transparent;
+  border: 2px solid #ff4d4f;
+  color: #ff4d4f;
+}
+.pay-error-container .pay-error-wrapper .ant-result .ant-result-extra .action-btn.retry-btn:hover {
+  background: #fff1f0;
+  border-color: #ff7875;
+  color: #ff7875;
+}
+@media (max-width: 768px) {
+  .pay-error-container {
+    padding: 10px;
+  }
+  .pay-error-container .pay-error-wrapper {
+    padding: 20px 10px;
+  }
+  .pay-error-container .pay-error-wrapper .ant-result {
+    padding: 0 10px;
+  }
+  .pay-error-container .pay-error-wrapper .ant-result .ant-result-title {
+    font-size: 24px;
+  }
+  .pay-error-container .pay-error-wrapper .ant-result .ant-result-subtitle {
+    font-size: 14px;
+  }
+  .pay-error-container .pay-error-wrapper .ant-result .error-desc p {
+    font-size: 14px;
+  }
+  .pay-error-container .pay-error-wrapper .ant-result .ant-result-extra {
+    flex-direction: column;
+  }
+  .pay-error-container .pay-error-wrapper .ant-result .ant-result-extra .action-btn {
+    width: 100%;
+  }
+}

+ 125 - 0
src/pages/layout/pages/payError/index.less

@@ -0,0 +1,125 @@
+.pay-error-container {
+  min-height: 80vh;
+  background: linear-gradient(135deg, #fef0ef 0%, #e2c3c3 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+
+  .pay-error-wrapper {
+    max-width: 600px;
+    width: 100%;
+    background: white;
+    border-radius: 12px;
+    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+    overflow: hidden;
+    padding: 40px 20px;
+
+    .ant-result {
+      padding: 0 20px;
+
+      .ant-result-title {
+        font-size: 28px;
+        font-weight: 600;
+        color: #ff4d4f;
+        margin-bottom: 16px;
+      }
+
+      .ant-result-subtitle {
+        color: #8c8c8c;
+        font-size: 16px;
+        margin-bottom: 24px;
+      }
+
+      .error-desc {
+        margin: 20px 0;
+        
+        .ant-typography {
+          margin-bottom: 12px;
+          
+          .ant-typography strong {
+            color: #262626;
+          }
+        }
+
+        .error-icon {
+          color: #ff4d4f;
+          margin-right: 8px;
+          font-size: 16px;
+        }
+      }
+
+      .ant-result-extra {
+        margin-top: 30px;
+        display: flex;
+        gap: 16px;
+        justify-content: center;
+
+        .action-btn {
+          min-width: 120px;
+          font-size: 16px;
+          height: 44px;
+
+          &.home-btn {
+            background: #ff4d4f;
+            border-color: #ff4d4f;
+
+            &:hover {
+              background: #ff7875;
+              border-color: #ff7875;
+            }
+          }
+
+          &.retry-btn {
+            background: transparent;
+            border: 2px solid #ff4d4f;
+            color: #ff4d4f;
+
+            &:hover {
+              background: #fff1f0;
+              border-color: #ff7875;
+              color: #ff7875;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .pay-error-container {
+    padding: 10px;
+    
+    .pay-error-wrapper {
+      padding: 20px 10px;
+      
+      .ant-result {
+        padding: 0 10px;
+        
+        .ant-result-title {
+          font-size: 24px;
+        }
+        
+        .ant-result-subtitle {
+          font-size: 14px;
+        }
+        
+        .error-desc {
+          p {
+            font-size: 14px;
+          }
+        }
+        
+        .ant-result-extra {
+          flex-direction: column;
+          
+          .action-btn {
+            width: 100%;
+          }
+        }
+      }
+    }
+  }
+}

+ 67 - 36
src/pages/layout/pages/payError/index.tsx

@@ -1,41 +1,72 @@
 import { Button, Result, Typography } from 'antd';
 const { Paragraph, Text } = Typography;
 import './index.css'
-import { CloseCircleOutlined } from '@ant-design/icons'
-const payError = () => {
-  return <div>
-    <Result
-      status="error"
-      title="Submission Failed"
-      subTitle="Please check and modify the following information before resubmitting."
-      extra={[
-        <Button type="primary" key="console">
-          Go Console
-        </Button>,
-        <Button key="buy">Buy Again</Button>,
-      ]}
-    >
-      <div className="desc">
-        <Paragraph>
-          <Text
-            strong
-            style={{
-              fontSize: 16,
-            }}
-          >
-            The content you submitted has the following error:
-          </Text>
-        </Paragraph>
-        <Paragraph>
-          <CloseCircleOutlined className="site-result-demo-error-icon" /> Your account has been
-          frozen. <a>Thaw immediately &gt;</a>
-        </Paragraph>
-        <Paragraph>
-          <CloseCircleOutlined className="site-result-demo-error-icon" /> Your account is not yet
-          eligible to apply. <a>Apply Unlock &gt;</a>
-        </Paragraph>
+import {  ExclamationCircleOutlined } from '@ant-design/icons'
+import { useNavigate } from 'react-router-dom';
+
+const PayError = () => {
+  const navigate = useNavigate();
+
+  const handleGoHome = () => {
+    navigate('/');
+  };
+  
+  const handleTryAgain = () => {
+    navigate(-1); // 返回上一页
+  };
+
+  return (
+    <div className="pay-error-container">
+      <div className="pay-error-wrapper">
+        <Result
+          status="error"
+          title="支付失败"
+          subTitle="请检查以下信息后重新尝试支付"
+          extra={[
+            <Button 
+              type="primary" 
+              key="home" 
+              size="large"
+              className="action-btn home-btn"
+              onClick={handleGoHome}
+            >
+              返回主页
+            </Button>,
+            <Button 
+              key="retry" 
+              size="large"
+              className="action-btn retry-btn"
+              onClick={handleTryAgain}
+            >
+              重新支付
+            </Button>,
+          ]}
+        >
+          <div className="error-desc">
+            <Paragraph>
+              <Text
+                strong
+                style={{
+                  fontSize: 16,
+                }}
+              >
+                您的支付遇到以下问题:
+              </Text>
+            </Paragraph>
+            <Paragraph>
+              <ExclamationCircleOutlined className="error-icon" /> 支付信息填写错误,请检查银行卡信息或余额
+            </Paragraph>
+            <Paragraph>
+              <ExclamationCircleOutlined className="error-icon" /> 网络连接不稳定,建议稍后重试
+            </Paragraph>
+            <Paragraph>
+              <ExclamationCircleOutlined className="error-icon" /> 如需帮助,请联系客服获取支持
+            </Paragraph>
+          </div>
+        </Result>
       </div>
-    </Result>
-  </div>
+    </div>
+  );
 }
-export default payError
+
+export default PayError;

+ 98 - 0
src/pages/layout/pages/paySuccess/index.css

@@ -0,0 +1,98 @@
+.pay-success-container {
+  min-height: 80vh;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+}
+.pay-success-container .pay-success-wrapper {
+  max-width: 600px;
+  width: 100%;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+  overflow: hidden;
+  padding: 40px 20px;
+}
+.pay-success-container .pay-success-wrapper .ant-result {
+  padding: 0 20px;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-title {
+  font-size: 28px;
+  font-weight: 600;
+  color: #52c41a;
+  margin-bottom: 16px;
+}
+.pay-success-container .pay-success-wrapper .ant-result .order-info {
+  margin: 20px 0;
+}
+.pay-success-container .pay-success-wrapper .ant-result .order-info p {
+  margin: 8px 0;
+  font-size: 16px;
+  color: #595959;
+}
+.pay-success-container .pay-success-wrapper .ant-result .order-info p:first-child {
+  font-weight: 500;
+}
+.pay-success-container .pay-success-wrapper .ant-result .order-info .order-no {
+  font-weight: 600;
+  color: #1890ff;
+  font-family: 'Monaco', 'Consolas', monospace;
+  background: #f0f8ff;
+  padding: 2px 8px;
+  border-radius: 4px;
+  border: 1px solid #d9d9d9;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-extra {
+  margin-top: 30px;
+  display: flex;
+  gap: 16px;
+  justify-content: center;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-extra .action-btn {
+  min-width: 120px;
+  font-size: 16px;
+  height: 44px;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-extra .action-btn.home-btn {
+  background: #1890ff;
+  border-color: #1890ff;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-extra .action-btn.home-btn:hover {
+  background: #40a9ff;
+  border-color: #40a9ff;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-extra .action-btn.order-btn {
+  background: transparent;
+  border: 2px solid #1890ff;
+  color: #1890ff;
+}
+.pay-success-container .pay-success-wrapper .ant-result .ant-result-extra .action-btn.order-btn:hover {
+  background: #e6f7ff;
+  border-color: #40a9ff;
+  color: #40a9ff;
+}
+@media (max-width: 768px) {
+  .pay-success-container {
+    padding: 10px;
+  }
+  .pay-success-container .pay-success-wrapper {
+    padding: 20px 10px;
+  }
+  .pay-success-container .pay-success-wrapper .ant-result {
+    padding: 0 10px;
+  }
+  .pay-success-container .pay-success-wrapper .ant-result .ant-result-title {
+    font-size: 24px;
+  }
+  .pay-success-container .pay-success-wrapper .ant-result .order-info p {
+    font-size: 14px;
+  }
+  .pay-success-container .pay-success-wrapper .ant-result .ant-result-extra {
+    flex-direction: column;
+  }
+  .pay-success-container .pay-success-wrapper .ant-result .ant-result-extra .action-btn {
+    width: 100%;
+  }
+}

+ 121 - 0
src/pages/layout/pages/paySuccess/index.less

@@ -0,0 +1,121 @@
+.pay-success-container {
+  min-height: 80vh;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+
+  .pay-success-wrapper {
+    max-width: 600px;
+    width: 100%;
+    background: white;
+    border-radius: 12px;
+    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+    overflow: hidden;
+    padding: 40px 20px;
+
+    .ant-result {
+      padding: 0 20px;
+
+      .ant-result-title {
+        font-size: 28px;
+        font-weight: 600;
+        color: #52c41a;
+        margin-bottom: 16px;
+      }
+
+      .order-info {
+        margin: 20px 0;
+        
+        p {
+          margin: 8px 0;
+          font-size: 16px;
+          color: #595959;
+
+          &:first-child {
+            font-weight: 500;
+          }
+        }
+
+        .order-no {
+          font-weight: 600;
+          color: #1890ff;
+          font-family: 'Monaco', 'Consolas', monospace;
+          background: #f0f8ff;
+          padding: 2px 8px;
+          border-radius: 4px;
+          border: 1px solid #d9d9d9;
+        }
+      }
+
+      .ant-result-extra {
+        margin-top: 30px;
+        display: flex;
+        gap: 16px;
+        justify-content: center;
+
+        .action-btn {
+          min-width: 120px;
+          font-size: 16px;
+          height: 44px;
+
+          &.home-btn {
+            background: #1890ff;
+            border-color: #1890ff;
+
+            &:hover {
+              background: #40a9ff;
+              border-color: #40a9ff;
+            }
+          }
+
+          &.order-btn {
+            background: transparent;
+            border: 2px solid #1890ff;
+            color: #1890ff;
+
+            &:hover {
+              background: #e6f7ff;
+              border-color: #40a9ff;
+              color: #40a9ff;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .pay-success-container {
+    padding: 10px;
+    
+    .pay-success-wrapper {
+      padding: 20px 10px;
+      
+      .ant-result {
+        padding: 0 10px;
+        
+        .ant-result-title {
+          font-size: 24px;
+        }
+        
+        .order-info {
+          p {
+            font-size: 14px;
+          }
+        }
+        
+        .ant-result-extra {
+          flex-direction: column;
+          
+          .action-btn {
+            width: 100%;
+          }
+        }
+      }
+    }
+  }
+}

+ 52 - 15
src/pages/layout/pages/paySuccess/index.tsx

@@ -1,18 +1,55 @@
 import { Button, Result } from 'antd'
 import './index.css'
-const paySuccess = () => {
-  return <div>
-    <Result
-      status="success"
-      title="Successfully Purchased Cloud Server ECS!"
-      subTitle="Order number: 2017182818828182881 Cloud server configuration takes 1-5 minutes, please wait."
-      extra={[
-        <Button type="primary" key="console">
-          Go Console
-        </Button>,
-        <Button key="buy">Buy Again</Button>,
-      ]}
-    />
-  </div>
+import { useNavigate } from 'react-router-dom'
+import { useParams } from 'react-router-dom'
+
+const PaySuccess = () => {
+  const params = useParams<{ orderNo: string }>()
+  const navigate = useNavigate()
+  
+  const handleGoHome = () => {
+    navigate('/')
+  }
+  
+  const handleViewOrder = () => {
+    navigate(`/order/${params.orderNo}`)
+  }
+
+  return (
+    <div className="pay-success-container">
+      <div className="pay-success-wrapper">
+        <Result
+          status="success"
+          title="支付成功!"
+          subTitle={
+            <div className="order-info">
+              <p>订单号: <span className="order-no">{params.orderNo}</span></p>
+              <p>感谢您的支持,我们将尽快为您处理订单</p>
+            </div>
+          }
+          extra={[
+            <Button 
+              type="primary" 
+              key="home" 
+              size="large"
+
+              onClick={handleGoHome}
+            >
+              返回主页
+            </Button>,
+            <Button 
+              key="order" 
+              size="large"
+
+              onClick={handleViewOrder}
+            >
+              查看订单
+            </Button>,
+          ]}
+        />
+      </div>
+    </div>
+  )
 }
-export default paySuccess
+
+export default PaySuccess

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

@@ -178,6 +178,12 @@
 .push-song-container .push-song-content .footer-section .payment-info .pay-btn:hover {
   background-color: #da0a15;
 }
+.push-song-container .push-song-content .gotoPay {
+  margin-top: 20px;
+  color: #f5222d;
+  display: flex;
+  justify-content: space-between;
+}
 .push-song-container .link {
   color: #1890ff;
   text-decoration: none;

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

@@ -221,6 +221,12 @@
         }
       }
     }
+    .gotoPay{
+      margin-top: 20px;
+      color: #f5222d;
+      display: flex;
+      justify-content:space-between;
+    }
   }
 
   .link {

+ 26 - 3
src/pages/layout/pages/pushSong/index.tsx

@@ -1,4 +1,4 @@
-import { useParams } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
 import './index.css';
 import { useEffect, useState } from 'react';
 import { Button, Input, Space, DatePicker, Checkbox, message, Image, Tooltip } from 'antd';
@@ -6,6 +6,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
 import type { DatePickerProps } from 'antd';
 import { geSongById } from '@/apis/song';
 import { addOrder, type AddOrderDto } from '@/apis/order';
+import PayLoading from '../../components/payLoading';
 
 const PushSong = () => {
   const { id } = useParams();
@@ -14,6 +15,10 @@ const PushSong = () => {
   const [startDate, setStartDate] = useState<string>('');
   const [agreed, setAgreed] = useState(false);
   const [songInfo, setSongInfo] = useState<any>(null);
+  const [loading, setLoading] = useState(false);
+  const navigator = useNavigate();
+  const [messageapi, content] = message.useMessage()
+  const [gotoPay, setGotoPay] = useState<string | null>(null)
   const handleAmountSelect = (amount: number) => {
     setSelectedAmount(amount);
     setCustomAmount(null); // 清空自定义金额
@@ -40,10 +45,10 @@ const PushSong = () => {
 
   const handleSubmit = async () => {
     if (!agreed) {
-      message.warning('请先阅读并同意服务协议');
+      messageapi.warning('请先阅读并同意服务协议');
       return;
     }
-
+    setLoading(true)
     // 获取最终金额
     const finalAmount = customAmount !== null ? customAmount : selectedAmount;
 
@@ -64,6 +69,14 @@ const PushSong = () => {
 
     const res = await addOrder(submitData)
     console.log(res);
+    setLoading(false)
+    if (res.code == 200) {
+      messageapi.success(res.message);
+      navigator(`/paySuccess/${res.data.orderNo}`, { replace: true });
+    } else {
+      messageapi.error(res.message);
+      setGotoPay(res.message)
+    }
   };
 
   useEffect(() => {
@@ -82,6 +95,8 @@ const PushSong = () => {
 
   return (
     <div className="push-song-container">
+      {content}
+      <PayLoading visible={loading} text='支付中请稍等' />
       <div className="push-song-header">
         <h2>智能推荐</h2>
       </div>
@@ -182,6 +197,14 @@ const PushSong = () => {
             </Button>
           </div>
         </div>
+        {gotoPay && (<div className='gotoPay'>
+          <div className="text">
+            {gotoPay}
+          </div>
+          <div className="gotoPayButton">
+           <Button type="primary" onClick={() => navigator('/rechargePage')}>去充值</Button>
+          </div>
+        </div>)}
       </div>
     </div>
   );

+ 1 - 1
src/pages/layout/pages/testPage/index.tsx

@@ -15,7 +15,7 @@ const defaultPlaylist: Song[] = [
     id: 131,
     title: 'G.E.M.邓紫棋 - 唯一',
     artist: '陈粒',
-    url: 'http://117.72.120.45:9000/song-audio/1767769846676_G.E.M.邓紫棋 - 唯一.mp3',
+    url: 'http://117.72.120.45:9000/song-audio/1767769846676_G.E.M.邓紫棋-唯一.mp3',
     cover: 'http://117.72.120.45:9000/music-covers/1767769925575_c47d220f-fceb-4632-8c48-ede3c1b7a1ac.png',
     duration: '04:13',
     album: '无',

+ 20 - 1
src/router/index.tsx

@@ -37,7 +37,10 @@ import PlaylistDetailWrapper from '@/pages/layout/pages/playDetailPage'
 import AlbumDetail from '@/pages/layout/pages/AlbumDetail'
 import Mv from '@/pages/layout/pages/musician/MusicianDashboardPage/works/mv'
 import UploadMv from '@/pages/layout/pages/musician/MusicianDashboardPage/works/UploadMv'
-
+import PushSong from '@/pages/layout/pages/pushSong'
+import RechargePage from '@/pages/layout/pages/RechargePage'
+import PaySuccess from '@/pages/layout/pages/paySuccess'
+import PayError from '@/pages/layout/pages/payError'
 const router = createBrowserRouter([
   //   <AuthRoute>
   //   <WYLayout />
@@ -202,6 +205,22 @@ const router = createBrowserRouter([
       {
         path: '/album/:id',
         element: <AlbumDetail />,
+      },
+      {
+        path: '/pushSong/:id',
+        element: <PushSong />,
+      },
+      {
+        path: '/rechargePage',
+        element: <RechargePage />,
+      },
+      {
+        path: '/paySuccess/:orderNo',
+        element: <PaySuccess />,
+      },
+      {
+        path: '/payError',
+        element: <PayError />,
       }
     ],
   },