index.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import React, { useState } from 'react';
  2. import AudioPlayer from 'react-h5-audio-player';
  3. import 'react-h5-audio-player/lib/styles.css';
  4. interface Song {
  5. id: number;
  6. title: string;
  7. artist: string;
  8. url: string;
  9. cover: string;
  10. duration: string;
  11. }
  12. const MusicPlayer: React.FC = () => {
  13. const playlist: Song[] = [
  14. {
  15. id: 1,
  16. title: '放生',
  17. artist: '陈旧',
  18. url: 'http://117.72.120.45:9000/testbucket/1232e131.mp3',
  19. cover: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
  20. duration: '04:24'
  21. },
  22. {
  23. id: 2,
  24. title: '夜曲',
  25. artist: '周杰伦',
  26. url: 'http://music.163.com/song/media/outer/url?id=123456.mp3',
  27. cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
  28. duration: '03:45'
  29. },
  30. {
  31. id: 3,
  32. title: '青花瓷',
  33. artist: '周杰伦',
  34. url: 'http://music.163.com/song/media/outer/url?id=789012.mp3',
  35. cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
  36. duration: '03:58'
  37. }
  38. ];
  39. const [currentTrackIndex, setCurrentTrackIndex] = useState(0);
  40. const [isPlaying, setIsPlaying] = useState(false);
  41. const [audioError, setAudioError] = useState<string | null>(null);
  42. const currentTrack = playlist[currentTrackIndex];
  43. // 上一首逻辑
  44. const handlePrevious = () => {
  45. setCurrentTrackIndex(prev => (prev === 0 ? playlist.length - 1 : prev - 1));
  46. setAudioError(null);
  47. if (isPlaying) setIsPlaying(true); // 切换后继续播放
  48. };
  49. // 下一首逻辑
  50. const handleNext = () => {
  51. setCurrentTrackIndex(prev => (prev === playlist.length - 1 ? 0 : prev + 1));
  52. setAudioError(null);
  53. if (isPlaying) setIsPlaying(true); // 切换后继续播放
  54. };
  55. // 播放列表项点击
  56. const handleTrackSelect = (index: number) => {
  57. setCurrentTrackIndex(index);
  58. setIsPlaying(true);
  59. setAudioError(null);
  60. };
  61. return (
  62. <div style={{ padding: '20px' }}>
  63. {/* 错误提示 */}
  64. {audioError && (
  65. <div style={{ color: 'red', padding: '10px', backgroundColor: '#ffe6e6', borderRadius: '4px', marginBottom: '10px' }}>
  66. {audioError}
  67. </div>
  68. )}
  69. {/* 播放器:适配 3.10.1 版本的核心 Props */}
  70. <AudioPlayer
  71. key={currentTrackIndex} // 切换歌曲时强制刷新组件
  72. src={currentTrack.url}
  73. onPlay={() => setIsPlaying(true)}
  74. onPause={() => setIsPlaying(false)}
  75. onClickPrevious={handlePrevious} // 绑定上一首(3.10.1 支持)
  76. onClickNext={handleNext} // 绑定下一首(3.10.1 支持)
  77. onEnded={handleNext} // 播放结束自动下一首
  78. onError={(e) => setAudioError('音频加载失败,请检查网络')}
  79. showSkipControls={true} // 显示上一首/下一首按钮
  80. header={
  81. // 手动渲染封面图和歌曲信息(替代 3.10.1 不支持的 cover 属性)
  82. <div style={{ textAlign: 'center', marginBottom: '10px' }}>
  83. <img
  84. src={currentTrack.cover}
  85. alt={currentTrack.title}
  86. style={{ width: '80px', height: '80px', borderRadius: '8px', marginBottom: '8px' }}
  87. />
  88. <h3 style={{ margin: '0', fontSize: '16px' }}>{currentTrack.title}</h3>
  89. <p style={{ margin: '0', fontSize: '14px', color: '#666' }}>{currentTrack.artist}</p>
  90. </div>
  91. }
  92. autoPlay={false}
  93. style={{
  94. // 自定义播放器样式(适配深色/浅色主题)
  95. backgroundColor: '#f5f5f5',
  96. padding: '10px',
  97. borderRadius: '8px'
  98. }}
  99. />
  100. {/* 播放列表 */}
  101. <div style={{ marginTop: '20px' }}>
  102. <h3 style={{ fontSize: '16px' }}>播放列表</h3>
  103. <ul style={{ listStyle: 'none', padding: '0', border: '1px solid #eee', borderRadius: '8px' }}>
  104. {playlist.map((song, index) => (
  105. <li
  106. key={song.id}
  107. onClick={() => handleTrackSelect(index)}
  108. style={{
  109. padding: '12px',
  110. backgroundColor: index === currentTrackIndex ? '#e6f7ff' : '#fff',
  111. borderBottom: index !== playlist.length - 1 ? '1px solid #eee' : 'none',
  112. cursor: 'pointer',
  113. display: 'flex',
  114. alignItems: 'center'
  115. }}
  116. >
  117. {/* 播放状态指示器 */}
  118. {index === currentTrackIndex && isPlaying && (
  119. <span style={{ marginRight: '10px', color: '#1890ff' }}>▶</span>
  120. )}
  121. {/* 列表项封面图 */}
  122. <img
  123. src={song.cover}
  124. alt={song.title}
  125. style={{ width: '36px', height: '36px', borderRadius: '4px', marginRight: '10px' }}
  126. />
  127. {/* 歌曲信息 */}
  128. <div style={{ flex: 1 }}>
  129. <div style={{ fontSize: '14px', fontWeight: index === currentTrackIndex ? 'bold' : 'normal' }}>
  130. {song.title}
  131. </div>
  132. <div style={{ fontSize: '12px', color: '#666' }}>
  133. {song.artist} · {song.duration}
  134. </div>
  135. </div>
  136. </li>
  137. ))}
  138. </ul>
  139. </div>
  140. </div>
  141. );
  142. };
  143. export default MusicPlayer;