|
@@ -1,10 +1,12 @@
|
|
|
-import React, { useState, useRef } from 'react';
|
|
|
|
|
|
|
+// src/pages/layout/pages/testPage/index.tsx
|
|
|
|
|
+import React, { useState, useRef, useEffect } from 'react';
|
|
|
import AudioPlayer from 'react-h5-audio-player';
|
|
import AudioPlayer from 'react-h5-audio-player';
|
|
|
import 'react-h5-audio-player/lib/styles.css';
|
|
import 'react-h5-audio-player/lib/styles.css';
|
|
|
import './index.css';
|
|
import './index.css';
|
|
|
import { UnorderedListOutlined } from '@ant-design/icons';
|
|
import { UnorderedListOutlined } from '@ant-design/icons';
|
|
|
|
|
+import { useMusicPlayer } from '@/context/MusicPlayerContext';
|
|
|
|
|
|
|
|
-interface Song {
|
|
|
|
|
|
|
+export interface Song {
|
|
|
id: number;
|
|
id: number;
|
|
|
title: string;
|
|
title: string;
|
|
|
artist: string;
|
|
artist: string;
|
|
@@ -18,7 +20,7 @@ interface Song {
|
|
|
description?: string; // 歌曲描述
|
|
description?: string; // 歌曲描述
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const playlist: Song[] = [
|
|
|
|
|
|
|
+const defaultPlaylist: Song[] = [
|
|
|
{
|
|
{
|
|
|
id: 1,
|
|
id: 1,
|
|
|
title: '奇妙能力歌',
|
|
title: '奇妙能力歌',
|
|
@@ -82,63 +84,113 @@ const playlist: Song[] = [
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
const MusicPlayer: React.FC = () => {
|
|
const MusicPlayer: React.FC = () => {
|
|
|
- const [currentTrackIndex, setCurrentTrackIndex] = useState<number>(0);
|
|
|
|
|
- const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
|
|
|
|
- const [isPlaylistOpen, setIsPlaylistOpen] = useState<boolean>(false);
|
|
|
|
|
|
|
+ const {
|
|
|
|
|
+ currentTrackIndex,
|
|
|
|
|
+ isPlaying,
|
|
|
|
|
+ playlist,
|
|
|
|
|
+ isPlaylistOpen,
|
|
|
|
|
+ setIsPlaying,
|
|
|
|
|
+ setCurrentTrackIndex,
|
|
|
|
|
+ setIsPlaylistOpen,
|
|
|
|
|
+ setPlaylist
|
|
|
|
|
+ } = useMusicPlayer();
|
|
|
|
|
+
|
|
|
const audioPlayerRef = useRef<AudioPlayer>(null);
|
|
const audioPlayerRef = useRef<AudioPlayer>(null);
|
|
|
- const currentTrack = playlist[currentTrackIndex];
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化播放列表
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (playlist.length === 0) {
|
|
|
|
|
+ setPlaylist(defaultPlaylist);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [playlist.length, setPlaylist]);
|
|
|
|
|
+
|
|
|
|
|
+ // 确保当前索引有效
|
|
|
|
|
+ const validCurrentTrackIndex = playlist.length > 0
|
|
|
|
|
+ ? Math.max(0, Math.min(currentTrackIndex, playlist.length - 1))
|
|
|
|
|
+ : 0;
|
|
|
|
|
+
|
|
|
|
|
+ const currentTrack = playlist.length > 0
|
|
|
|
|
+ ? playlist[validCurrentTrackIndex]
|
|
|
|
|
+ : defaultPlaylist[0];
|
|
|
|
|
|
|
|
// 修改 handlePrev 函数
|
|
// 修改 handlePrev 函数
|
|
|
const handlePrev = () => {
|
|
const handlePrev = () => {
|
|
|
- setCurrentTrackIndex((prev) => (prev - 1 + playlist.length) % playlist.length);
|
|
|
|
|
- setIsPlaying(true); // 确保切换后自动播放
|
|
|
|
|
|
|
+ if (playlist.length > 0) {
|
|
|
|
|
+ setCurrentTrackIndex((prev) => (prev - 1 + playlist.length) % playlist.length);
|
|
|
|
|
+ setIsPlaying(true);
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 修改 handleNext 函数
|
|
// 修改 handleNext 函数
|
|
|
const handleNext = () => {
|
|
const handleNext = () => {
|
|
|
- setCurrentTrackIndex((prev) => (prev + 1) % playlist.length);
|
|
|
|
|
- setIsPlaying(true); // 确保切换后自动播放
|
|
|
|
|
|
|
+ if (playlist.length > 0) {
|
|
|
|
|
+ setCurrentTrackIndex((prev) => (prev + 1) % playlist.length);
|
|
|
|
|
+ setIsPlaying(true);
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
// 播放列表项点击事件
|
|
// 播放列表项点击事件
|
|
|
const handleTrackSelect = (index: number) => {
|
|
const handleTrackSelect = (index: number) => {
|
|
|
- setCurrentTrackIndex(index);
|
|
|
|
|
- setIsPlaying(true);
|
|
|
|
|
- setIsPlaylistOpen(false);
|
|
|
|
|
|
|
+ if (playlist.length > 0) {
|
|
|
|
|
+ setCurrentTrackIndex(index);
|
|
|
|
|
+ setIsPlaying(true);
|
|
|
|
|
+ setIsPlaylistOpen(false);
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // 当播放状态改变时,确保音频元素正确响应
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (audioPlayerRef.current) {
|
|
|
|
|
+ const audioElement = audioPlayerRef.current.audio.current;
|
|
|
|
|
+ if (audioElement) {
|
|
|
|
|
+ if (isPlaying) {
|
|
|
|
|
+ // 确保在播放前音频源已设置
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ audioElement.play().catch(e => console.log('播放失败:', e));
|
|
|
|
|
+ }, 0);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ audioElement.pause();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [isPlaying, validCurrentTrackIndex]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div className="music-player-wrapper">
|
|
<div className="music-player-wrapper">
|
|
|
<div className="player-container">
|
|
<div className="player-container">
|
|
|
{/* 左侧:歌曲封面 + 信息 */}
|
|
{/* 左侧:歌曲封面 + 信息 */}
|
|
|
<div className="track-info">
|
|
<div className="track-info">
|
|
|
- <img src={currentTrack.cover} alt={currentTrack.title} className="track-cover" />
|
|
|
|
|
|
|
+ <img
|
|
|
|
|
+ src={currentTrack?.cover || defaultPlaylist[0]?.cover}
|
|
|
|
|
+ alt={currentTrack?.title || '未知歌曲'}
|
|
|
|
|
+ className="track-cover"
|
|
|
|
|
+ />
|
|
|
<div className="track-meta">
|
|
<div className="track-meta">
|
|
|
- <div className="track-title">{currentTrack.title}</div>
|
|
|
|
|
- <div className="track-artist">{currentTrack.artist}</div>
|
|
|
|
|
|
|
+ <div className="track-title">{currentTrack?.title || '未知歌曲'}</div>
|
|
|
|
|
+ <div className="track-artist">{currentTrack?.artist || '未知艺术家'}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* 中间:播放器核心(使用原生上一首/下一首按钮) */}
|
|
|
|
|
|
|
+ {/* 中间:播放器核心 */}
|
|
|
<div className="player-ui-container">
|
|
<div className="player-ui-container">
|
|
|
-
|
|
|
|
|
<AudioPlayer
|
|
<AudioPlayer
|
|
|
ref={audioPlayerRef}
|
|
ref={audioPlayerRef}
|
|
|
- key={currentTrackIndex}
|
|
|
|
|
- src={currentTrack.url}
|
|
|
|
|
- autoPlay={isPlaying}
|
|
|
|
|
|
|
+ key={validCurrentTrackIndex}
|
|
|
|
|
+ src={currentTrack?.url || defaultPlaylist[0]?.url}
|
|
|
|
|
+ autoPlay={false} // 关键:不使用autoPlay,而是手动控制
|
|
|
onPlay={() => setIsPlaying(true)}
|
|
onPlay={() => setIsPlaying(true)}
|
|
|
onPause={() => setIsPlaying(false)}
|
|
onPause={() => setIsPlaying(false)}
|
|
|
onEnded={handleNext}
|
|
onEnded={handleNext}
|
|
|
- showSkipControls
|
|
|
|
|
|
|
+ showSkipControls={playlist.length > 0}
|
|
|
className="player-ui"
|
|
className="player-ui"
|
|
|
listenInterval={1000}
|
|
listenInterval={1000}
|
|
|
onClickPrevious={handlePrev}
|
|
onClickPrevious={handlePrev}
|
|
|
onClickNext={handleNext}
|
|
onClickNext={handleNext}
|
|
|
|
|
+ disabled={playlist.length === 0}
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* 右侧:播放列表按钮(AntD 图标) */}
|
|
|
|
|
|
|
+ {/* 右侧:播放列表按钮 */}
|
|
|
<button
|
|
<button
|
|
|
className="playlist-toggle"
|
|
className="playlist-toggle"
|
|
|
onClick={() => setIsPlaylistOpen(!isPlaylistOpen)}
|
|
onClick={() => setIsPlaylistOpen(!isPlaylistOpen)}
|
|
@@ -161,10 +213,10 @@ const MusicPlayer: React.FC = () => {
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
<ul className="track-list">
|
|
<ul className="track-list">
|
|
|
- {playlist.map((track, index) => (
|
|
|
|
|
|
|
+ {(playlist.length > 0 ? playlist : defaultPlaylist).map((track, index) => (
|
|
|
<li
|
|
<li
|
|
|
key={track.id}
|
|
key={track.id}
|
|
|
- className={index === currentTrackIndex ? 'track-item active' : 'track-item'}
|
|
|
|
|
|
|
+ className={index === validCurrentTrackIndex ? 'track-item active' : 'track-item'}
|
|
|
onClick={() => handleTrackSelect(index)}
|
|
onClick={() => handleTrackSelect(index)}
|
|
|
>
|
|
>
|
|
|
<img src={track.cover} alt={track.title} className="item-cover" />
|
|
<img src={track.cover} alt={track.title} className="item-cover" />
|