|
|
@@ -1,93 +1,161 @@
|
|
|
-// src/pages/layout/pages/mine/playlist/index.tsx
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
import { useParams } from 'react-router-dom';
|
|
|
-import './index.css'; // 假设使用 less 样式文件
|
|
|
-
|
|
|
-interface Song {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- artist: string;
|
|
|
- duration: string;
|
|
|
- album: string;
|
|
|
-}
|
|
|
+import './index.css';
|
|
|
+import { getPayListDetail } from '@/apis/payList';
|
|
|
+import { Image, Spin } from 'antd';
|
|
|
+import { LoadingOutlined } from '@ant-design/icons';
|
|
|
|
|
|
+// 定义API返回的数据结构
|
|
|
interface Playlist {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- cover: string;
|
|
|
- description: string;
|
|
|
- creator: string;
|
|
|
+ id: number;
|
|
|
+ userId: number;
|
|
|
+ playlistName: string;
|
|
|
+ coverUrl: string | null;
|
|
|
+ description: string | null;
|
|
|
+ tag: string | null;
|
|
|
+ songCount: number;
|
|
|
+ playCount: number;
|
|
|
createTime: string;
|
|
|
+ updateTime: string;
|
|
|
+ status: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+interface Song {
|
|
|
+ id: number;
|
|
|
+ songName: string;
|
|
|
+ artistId: number;
|
|
|
+ albumId: number | null;
|
|
|
+ duration: number;
|
|
|
+ fileUrl: string;
|
|
|
+ coverUrl: string;
|
|
|
+ releaseTime: string;
|
|
|
+ lyrics: string;
|
|
|
playCount: number;
|
|
|
+ isPaid: number;
|
|
|
+ price: number;
|
|
|
+ createTime: string;
|
|
|
+ updateTime: string;
|
|
|
+ status: number;
|
|
|
+ auditReason: string | null;
|
|
|
+ auditTime: string | null;
|
|
|
+ publishTime: string | null;
|
|
|
+ shelfTime: string | null;
|
|
|
+ offShelfTime: string | null;
|
|
|
+ deleteFlag: number;
|
|
|
+ deleteTime: string | null;
|
|
|
+ songType: string | null;
|
|
|
+ version: string | null;
|
|
|
+ workType: string | null;
|
|
|
+ genre: string | null;
|
|
|
+ language: string | null;
|
|
|
+ lyricist: string | null;
|
|
|
+ composer: string | null;
|
|
|
+ arranger: string | null;
|
|
|
+ singerName: string | null;
|
|
|
+}
|
|
|
+
|
|
|
+interface PlaylistData {
|
|
|
+ playlist: Playlist;
|
|
|
songs: Song[];
|
|
|
}
|
|
|
|
|
|
const Mine_Playlist = () => {
|
|
|
- const { id } = useParams<{ id: string }>(); // 获取路由参数
|
|
|
- const [playlist, setPlaylist] = useState<Playlist | null>(null);
|
|
|
+ const { id } = useParams<{ id: string }>();
|
|
|
+ const [playlistData, setPlaylistData] = useState<PlaylistData | null>(null);
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
+ const [error, setError] = useState<string | null>(null);
|
|
|
|
|
|
- // 模拟请求歌单详情
|
|
|
- const fetchPlaylistDetails = async (playlistId: string) => {
|
|
|
- try {
|
|
|
- setLoading(true);
|
|
|
-
|
|
|
- // 模拟从 API 获取数据(实际项目中替换为 axios 或 fetch)
|
|
|
- const mockData: Playlist = {
|
|
|
- id: playlistId,
|
|
|
- name: '我喜欢的音乐',
|
|
|
- cover: ' https://p1.music.126.net/viEFZrxTnTWkXweF5TdbyQ==/109951164281883574.jpg?param=200y200', // 替换为真实封面 URL
|
|
|
- description: '我的收藏歌单',
|
|
|
- creator: '生活在经验里_q',
|
|
|
- createTime: '2017-04-02',
|
|
|
- playCount: 2258,
|
|
|
- songs: [
|
|
|
- { id: '1', name: '拉个勾说永远爱我', artist: 'DJ', duration: '03:08', album: '经典过时了吗' },
|
|
|
- { id: '2', name: '半点心', artist: '草蜢', duration: '03:52', album: '潘源良二五精神永不忘' },
|
|
|
- { id: '3', name: '美丽的神话 - (Endless Love 2)', artist: '孙楠/韩红', duration: '04:52', album: '忘不了你' },
|
|
|
- { id: '4', name: '全是爱', artist: '凤凰传奇', duration: '04:14', album: '最炫民族风' },
|
|
|
- { id: '5', name: '路边的野花不要采', artist: '邓丽君', duration: '02:03', album: '世纪10星 - 难忘篇' },
|
|
|
- { id: '6', name: 'Gimme! Gimme! Gimme! (A Man After Midnight)', artist: 'ABBA', duration: '04:49', album: 'ABBA Gold: Greatest Hits' },
|
|
|
- { id: '7', name: '为你我受冷风吹', artist: '林忆莲', duration: '04:19', album: 'Love, Sandy' },
|
|
|
- { id: '8', name: 'Old Time Rock and Roll', artist: 'Bob Seger', duration: '03:14', album: 'Stranger in Town' },
|
|
|
- { id: '9', name: '别看我只是一只羊(喜羊羊与灰太狼)动画片主题曲', artist: '古倩敏', duration: '02:44', album: '喜羊羊与灰太狼' },
|
|
|
- { id: '10', name: '你是我的玫瑰花', artist: '庞龙', duration: '03:49', album: '你是我的玫瑰花' },
|
|
|
- { id: '11', name: '快乐女孩 - (动画《甜心格格》主题曲)', artist: '刘惜君', duration: '02:55', album: '快乐女孩' },
|
|
|
- ],
|
|
|
- };
|
|
|
-
|
|
|
- setPlaylist(mockData);
|
|
|
- } catch (error) {
|
|
|
- console.error('获取歌单失败:', error);
|
|
|
- } finally {
|
|
|
- setLoading(false);
|
|
|
- }
|
|
|
+ // 格式化时间为 YYYY-MM-DD 格式
|
|
|
+ const formatDate = (dateString: string) => {
|
|
|
+ const date = new Date(dateString);
|
|
|
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
|
};
|
|
|
|
|
|
+ // 将秒数转换为 mm:ss 格式
|
|
|
+ const formatDuration = (seconds: number) => {
|
|
|
+ const mins = Math.floor(seconds / 60);
|
|
|
+ const secs = seconds % 60;
|
|
|
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 请求歌单详情
|
|
|
useEffect(() => {
|
|
|
+ const fetchPlaylistDetails = async () => {
|
|
|
+ try {
|
|
|
+ setLoading(true);
|
|
|
+ setError(null);
|
|
|
+
|
|
|
+ if (!id) {
|
|
|
+ throw new Error('缺少歌单ID');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用API获取真实数据
|
|
|
+ const response = await getPayListDetail(Number(id));
|
|
|
+
|
|
|
+ if (response.code === 200) {
|
|
|
+ // 直接使用返回的数据,不再需要解析JSON字符串
|
|
|
+ setPlaylistData(response.data);
|
|
|
+ } else {
|
|
|
+ throw new Error(response.message || '获取歌单失败');
|
|
|
+ }
|
|
|
+ } catch (err: any) {
|
|
|
+ console.error('获取歌单失败:', err);
|
|
|
+ setError(err.message || '获取歌单失败');
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
if (id) {
|
|
|
- fetchPlaylistDetails(id);
|
|
|
+ fetchPlaylistDetails();
|
|
|
}
|
|
|
}, [id]);
|
|
|
|
|
|
if (loading) {
|
|
|
- return <div className="loading">加载中...</div>;
|
|
|
+ return (
|
|
|
+ <div className="loading" style={{
|
|
|
+ display: 'flex',
|
|
|
+ justifyContent: 'center',
|
|
|
+ alignItems: 'center',
|
|
|
+ height: '100vh',
|
|
|
+ width: '100vw',
|
|
|
+ position: 'fixed',
|
|
|
+ top: 0,
|
|
|
+ left: 0,
|
|
|
+ backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
|
|
+ zIndex: 9999
|
|
|
+ }}>
|
|
|
+ <Spin indicator={<LoadingOutlined style={{ fontSize: 48 }} spin />} />
|
|
|
+ </div>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- if (!playlist) {
|
|
|
+ if (error) {
|
|
|
+ return <div className="error">错误: {error}</div>;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!playlistData || !playlistData.playlist) {
|
|
|
return <div className="not-found">歌单不存在</div>;
|
|
|
}
|
|
|
|
|
|
+ const { playlist, songs } = playlistData;
|
|
|
+
|
|
|
+ // 确保 songs 是数组,如果不是则默认为空数组
|
|
|
+ const validSongs = Array.isArray(songs) ? songs : [];
|
|
|
+
|
|
|
return (
|
|
|
<div className="playlist-page">
|
|
|
{/* 歌单头部 */}
|
|
|
<div className="playlist-header">
|
|
|
- <img src={playlist.cover} alt="歌单封面" className="cover-img" />
|
|
|
+ <img
|
|
|
+ src={playlist.coverUrl || 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'}
|
|
|
+ alt={playlist.playlistName}
|
|
|
+ className="cover-img"
|
|
|
+ />
|
|
|
<div className="playlist-info">
|
|
|
- <h1>{playlist.name}</h1>
|
|
|
+ <h1>{playlist.playlistName}</h1>
|
|
|
<div className="meta">
|
|
|
- <span className="creator">{playlist.creator}</span>
|
|
|
- <span className="create-time"> {playlist.createTime} 创建</span>
|
|
|
+ <span className="create-time"> {formatDate(playlist.createTime)} 创建</span>
|
|
|
</div>
|
|
|
<div className="actions">
|
|
|
<button className="play-btn">播放</button>
|
|
|
@@ -104,31 +172,34 @@ const Mine_Playlist = () => {
|
|
|
</div>
|
|
|
|
|
|
{/* 歌曲列表 */}
|
|
|
- <div className="song-list-container">
|
|
|
- <div className="song-list-header">
|
|
|
- <div className="song-number">序号</div>
|
|
|
- <div className="song-title">歌曲标题</div>
|
|
|
- <div className="song-duration">时长</div>
|
|
|
- <div className="song-artist">歌手</div>
|
|
|
- <div className="song-album">专辑</div>
|
|
|
- </div>
|
|
|
+ {validSongs.length > 0 ? (
|
|
|
+ <div className="song-list-container">
|
|
|
+ <div className="song-list-header">
|
|
|
+ <div className="song-number">序号</div>
|
|
|
+ <div className="song-title">歌曲标题</div>
|
|
|
+ <div className="song-duration">时长</div>
|
|
|
+ <div className="song-artist">歌手</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <ul className="song-list">
|
|
|
- {playlist?.songs.map((song, index) => (
|
|
|
- <li key={song.id} className="song-item">
|
|
|
- <div className="song-number">{index + 1}</div>
|
|
|
- <div className="song-title">
|
|
|
- <span className="play-icon">▶</span>
|
|
|
- {song.name}
|
|
|
- {song.album && <span className="album-tag">({song.album})</span>}
|
|
|
- </div>
|
|
|
- <div className="song-duration">{song.duration}</div>
|
|
|
- <div className="song-artist">{song.artist}</div>
|
|
|
- <div className="song-album">{song.album}</div>
|
|
|
- </li>
|
|
|
- ))}
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
+ <ul className="song-list">
|
|
|
+ {validSongs.map((song, index) => (
|
|
|
+ <li key={song.id} className="song-item">
|
|
|
+ <div className="song-number">{index + 1}</div>
|
|
|
+ <div className="song-title">
|
|
|
+ <span className="play-icon">▶</span>
|
|
|
+ {song.songName}
|
|
|
+ </div>
|
|
|
+ <div className="song-duration">{formatDuration(song.duration)}</div>
|
|
|
+ <div className="song-artist">{song.singerName || '未知歌手'}</div>
|
|
|
+ </li>
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <div>
|
|
|
+ <Image preview={false} src="http://117.72.120.45:9000/public/musician/playlist/songNull.png" />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</div>
|
|
|
);
|
|
|
};
|