|
|
@@ -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;
|