update(AudioPlayer): Add playback feature.

mack-mac
mackt 8 months ago
parent 307cc0f6cf
commit 8e0bc75ad9

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 10H14V3" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 14H10V21" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 5H19V12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 19H5V12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5303 1.46967C18.2374 1.17678 17.7626 1.17678 17.4697 1.46967C17.1768 1.76256 17.1768 2.23744 17.4697 2.53033L19.1893 4.25H8C4.82436 4.25 2.25 6.82436 2.25 10V16C2.25 19.1756 4.82436 21.75 8 21.75H16C19.1757 21.75 21.75 19.1755 21.75 15.9999V14.5C21.75 14.0858 21.4142 13.75 21 13.75C20.5858 13.75 20.25 14.0858 20.25 14.5V15.9999C20.25 18.3471 18.3472 20.25 16 20.25H8C5.65279 20.25 3.75 18.3472 3.75 16V10C3.75 7.65279 5.65279 5.75 8 5.75H19.1893L17.4697 7.46967C17.1768 7.76256 17.1768 8.23744 17.4697 8.53033C17.7626 8.82322 18.2374 8.82322 18.5303 8.53033L21.5303 5.53033C21.6768 5.38388 21.75 5.19194 21.75 5C21.75 4.89831 21.7298 4.80134 21.6931 4.71291C21.6565 4.62445 21.6022 4.54158 21.5303 4.46967L18.5303 1.46967Z" fill="black" fill-opacity="0.95"/>
</svg>

After

Width:  |  Height:  |  Size: 917 B

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5303 1.46967C18.2374 1.17678 17.7626 1.17678 17.4697 1.46967C17.1768 1.76256 17.1768 2.23744 17.4697 2.53033L19.1893 4.25H8C4.82436 4.25 2.25 6.82436 2.25 10V12C2.25 12.4142 2.58579 12.75 3 12.75C3.41421 12.75 3.75 12.4142 3.75 12V10C3.75 7.65279 5.65279 5.75 8 5.75H19.1893L17.4697 7.46967C17.1768 7.76256 17.1768 8.23744 17.4697 8.53033C17.7626 8.82322 18.2374 8.82322 18.5303 8.53033L21.5303 5.53033C21.6768 5.38388 21.75 5.19194 21.75 5C21.75 4.89831 21.7298 4.80134 21.6931 4.71291C21.6565 4.62445 21.6022 4.54158 21.5303 4.46967L18.5303 1.46967Z" fill="black" fill-opacity="0.95"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.46967 22.5303C5.76256 22.8232 6.23744 22.8232 6.53033 22.5303C6.82322 22.2374 6.82322 21.7626 6.53033 21.4697L4.81066 19.75H16C19.1756 19.75 21.75 17.1756 21.75 14V12C21.75 11.5858 21.4142 11.25 21 11.25C20.5858 11.25 20.25 11.5858 20.25 12V14C20.25 16.3472 18.3472 18.25 16 18.25H4.81066L6.53033 16.5303C6.82322 16.2374 6.82322 15.7626 6.53033 15.4697C6.23744 15.1768 5.76256 15.1768 5.46967 15.4697L2.46967 18.4697C2.32322 18.6161 2.25 18.8081 2.25 19C2.25 19.1017 2.27024 19.1987 2.30691 19.2871C2.34351 19.3755 2.39776 19.4584 2.46967 19.5303L5.46967 22.5303Z" fill="black" fill-opacity="0.95"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,4 +1,4 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4.70001" y="2.90002" width="4.5" height="16.2" rx="0.736" fill="white"/> <rect x="5" y="3" width="5" height="18" rx="0.8" fill="white" fill-opacity="0.95"/>
<rect x="12.8" y="2.90002" width="4.5" height="16.2" rx="0.736" fill="white"/> <rect x="14" y="3" width="5" height="18" rx="0.8" fill="white" fill-opacity="0.95"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 264 B

After

Width:  |  Height:  |  Size: 272 B

@ -1,5 +1,3 @@
<svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="27" cy="27" r="27" fill="#C43737"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M20.0103 10.2691C21.3409 11.0395 21.3409 12.9605 20.0103 13.7309L7.00207 21.262C5.66874 22.0339 4 21.0718 4 19.5311L4 4.4689C4 2.92823 5.66874 1.96611 7.00207 2.73804L20.0103 10.2691Z" fill="white" fill-opacity="0.95"/>
<rect x="20.7" y="18.9" width="4.5" height="16.2" rx="0.736" fill="white"/>
<rect x="28.8" y="18.9" width="4.5" height="16.2" rx="0.736" fill="white"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 372 B

@ -1,4 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.5 2.625H18.6667C18.1834 2.625 17.7917 3.01675 17.7917 3.5C17.7917 3.98325 18.1834 4.375 18.6667 4.375H22.3876L2.88128 23.8813C2.53957 24.223 2.53957 24.777 2.88128 25.1187C3.22299 25.4604 3.77701 25.4604 4.11872 25.1187L23.625 5.61244V9.33333C23.625 9.81658 24.0168 10.2083 24.5 10.2083C24.9833 10.2083 25.375 9.81658 25.375 9.33333V3.5C25.375 3.26247 25.2804 3.04704 25.1267 2.88938C25.1215 2.88399 25.1161 2.87867 25.1108 2.87342C25.0287 2.79343 24.9349 2.73275 24.8349 2.69139C24.7318 2.64861 24.6186 2.625 24.5 2.625Z" fill="black" fill-opacity="0.7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3076 24.8374C25.2659 24.9373 25.2048 25.0311 25.1244 25.113C25.1206 25.1168 25.1168 25.1206 25.113 25.1244C24.9466 25.2878 24.7312 25.3713 24.5146 25.3749C24.5092 25.375 24.5037 25.375 24.4983 25.375H18.6667C18.1834 25.375 17.7917 24.9832 17.7917 24.5C17.7917 24.0168 18.1834 23.625 18.6667 23.625H22.3876L16.8813 18.1187C16.5396 17.777 16.5396 17.223 16.8813 16.8813C17.223 16.5396 17.777 16.5396 18.1187 16.8813L23.625 22.3876V18.6667C23.625 18.1834 24.0168 17.7917 24.5 17.7917C24.9833 17.7917 25.375 18.1834 25.375 18.6667V24.5C25.375 24.6196 25.351 24.7336 25.3076 24.8374ZM11.1187 9.88128C11.4604 10.223 11.4604 10.777 11.1187 11.1187C10.777 11.4604 10.223 11.4604 9.88128 11.1187L2.88128 4.11872C2.53957 3.77701 2.53957 3.22299 2.88128 2.88128C3.22299 2.53957 3.77701 2.53957 4.11872 2.88128L11.1187 9.88128Z" fill="black" fill-opacity="0.7"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 2.25C15.5858 2.25 15.25 2.58579 15.25 3C15.25 3.41421 15.5858 3.75 16 3.75H19.1893L2.46967 20.4697C2.17678 20.7626 2.17678 21.2374 2.46967 21.5303C2.76256 21.8232 3.23744 21.8232 3.53033 21.5303L20.25 4.81066V8C20.25 8.41421 20.5858 8.75 21 8.75C21.4142 8.75 21.75 8.41421 21.75 8V3C21.75 2.80806 21.6768 2.61612 21.5303 2.46967C21.4584 2.39776 21.3755 2.34351 21.2871 2.30691C21.1987 2.27024 21.1017 2.25 21 2.25H16Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 21.75C15.5858 21.75 15.25 21.4142 15.25 21C15.25 20.5858 15.5858 20.25 16 20.25H19.1893L14.4697 15.5303C14.1768 15.2374 14.1768 14.7626 14.4697 14.4697C14.7626 14.1768 15.2374 14.1768 15.5303 14.4697L20.25 19.1893V16C20.25 15.5858 20.5858 15.25 21 15.25C21.4142 15.25 21.75 15.5858 21.75 16V21C21.75 21.1919 21.6768 21.3839 21.5303 21.5303C21.4584 21.6022 21.3755 21.6565 21.2871 21.6931C21.1987 21.7298 21.1017 21.75 21 21.75H16ZM9.53033 8.46967C9.82322 8.76256 9.82322 9.23744 9.53033 9.53033C9.23744 9.82322 8.76256 9.82322 8.46967 9.53033L2.46967 3.53033C2.17678 3.23744 2.17678 2.76256 2.46967 2.46967C2.76256 2.17678 3.23744 2.17678 3.53033 2.46967L9.53033 8.46967Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -1,4 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.5 11.6667H16.3333V3.50002" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.5 16.3333H11.6667V24.5" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 341 B

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5303 1.46967C18.2374 1.17678 17.7626 1.17678 17.4697 1.46967C17.1768 1.76256 17.1768 2.23744 17.4697 2.53033L19.1893 4.25H8C4.82436 4.25 2.25 6.82436 2.25 10V12C2.25 12.4142 2.58579 12.75 3 12.75C3.41421 12.75 3.75 12.4142 3.75 12V10C3.75 7.65279 5.65279 5.75 8 5.75H19.1893L17.4697 7.46967C17.1768 7.76256 17.1768 8.23744 17.4697 8.53033C17.7626 8.82322 18.2374 8.82322 18.5303 8.53033L21.5303 5.53033C21.6768 5.38388 21.75 5.19194 21.75 5C21.75 4.89831 21.7298 4.80134 21.6931 4.71291C21.6565 4.62445 21.6022 4.54158 21.5303 4.46967L18.5303 1.46967Z" fill="black" fill-opacity="0.95"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.183 8.12895C13.3891 8.26848 13.5125 8.50114 13.5125 8.75V15.25C13.5125 15.6642 13.1767 16 12.7625 16C12.3483 16 12.0125 15.6642 12.0125 15.25V9.85778L10.5411 10.4464C10.1565 10.6002 9.72 10.4131 9.56617 10.0285C9.41234 9.64395 9.5994 9.20747 9.98399 9.05364L12.484 8.05365C12.7151 7.96122 12.9769 7.98943 13.183 8.12895Z" fill="black" fill-opacity="0.95"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.46967 22.5303C5.76256 22.8232 6.23744 22.8232 6.53033 22.5303C6.82322 22.2374 6.82322 21.7626 6.53033 21.4697L4.81066 19.75H16C19.1756 19.75 21.75 17.1756 21.75 14V12C21.75 11.5858 21.4142 11.25 21 11.25C20.5858 11.25 20.25 11.5858 20.25 12V14C20.25 16.3472 18.3472 18.25 16 18.25H4.81066L6.53033 16.5303C6.82322 16.2374 6.82322 15.7626 6.53033 15.4697C6.23744 15.1768 5.76256 15.1768 5.46967 15.4697L2.46967 18.4697C2.32322 18.6161 2.25 18.8081 2.25 19C2.25 19.1017 2.27024 19.1987 2.30691 19.2871C2.34351 19.3755 2.39776 19.4584 2.46967 19.5303L5.46967 22.5303Z" fill="black" fill-opacity="0.95"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -36,6 +36,7 @@ export default function Journal() {
// journal: apiGetSongCollect, // journal: apiGetSongCollect,
// }; // };
const result = await apiGetSongCollect({ userId: userInfo?.id, pageNum, pageSize }); const result = await apiGetSongCollect({ userId: userInfo?.id, pageNum, pageSize });
console.log(result);
if (result.code === 200) setList(result.data.rows); if (result.code === 200) setList(result.data.rows);
}; };
@ -74,7 +75,7 @@ export default function Journal() {
</div> </div>
{/* List */} {/* List */}
<SongCardList className="w-full mt-11px mb-[56px] " songList={list} /> <SongCardList className="w-full mt-11px mb-[56px] " songList={list} listId="mylist" />
</div> </div>
</main> </main>
); );

@ -1,84 +1,111 @@
'use client'; 'use client';
import { useState } from 'react'; /**
*
* 1.
* 2.
* 3.
*/
import { useState, useRef, useEffect } from 'react';
import Image from 'next/image';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { AutoScrollContainer } from '@/components'; import PlayerControl from './PlayerControl';
import useAudioStore from '@/store/audio'; import useAudioStore from '@/store/audio';
export default function AudioPlayer({ className }: { className?: string }) { export default function AudioPlayer({ className }: { className?: string }) {
const { audioInfo, playList } = useAudioStore( const audioRef = useRef(new Audio());
const [trackProgress, setTrackProgress] = useState<number>(0); // 音频进度
const isReady = useRef<boolean>(false); // 是否完成加载
const { duration } = audioRef.current; // 音频总时长;
const { playStatus, setPlayStatus, audio, switchSongByDiff } = useAudioStore(
useShallow((state) => ({ useShallow((state) => ({
audioInfo: state.audioInfo, playStatus: state.playStatus,
setPlayStatus: state.setPlayStatus,
audio: state.audio,
playList: state.playList, playList: state.playList,
switchSongByDiff: state.switchSongByDiff,
})), })),
); );
const [curTime, setCurTime] = useState('00:00'); // 播放/暂停
const [totalTime, setTotalTime] = useState('00:00'); const handlePlay = () => {
setPlayStatus(!playStatus);
};
// 上一首
const handlePlayPre = () => {
switchSongByDiff(-1);
};
// 下一首
const handlePlayNext = () => {
switchSongByDiff(1);
};
// 调整进度
const handleChangeProgress = (value: string) => {
audioRef.current.currentTime = Number(value);
setTrackProgress(audioRef.current.currentTime);
};
// 给 audio 绑定监听
const handleTimeUpdate = (status: boolean) => {
status
? audioRef.current.addEventListener('timeupdate', onTimeUpdate)
: audioRef.current.removeEventListener('timeupdate', onTimeUpdate);
};
// 监听进度
const onTimeUpdate = () => {
if (audioRef.current.ended) handlePlayNext();
setTrackProgress(audioRef.current.currentTime);
};
// 监听播放/暂停
useEffect(() => {
playStatus ? audioRef.current.play() : audioRef.current.pause();
}, [playStatus]);
// 监听切换歌曲
useEffect(() => {
console.log('触发');
handleTimeUpdate(false);
audioRef.current.pause();
if (!audio) return;
audioRef.current = new Audio(audio.src);
if (isReady.current) {
audioRef.current.play();
setPlayStatus(true);
handleTimeUpdate(true);
} else {
isReady.current = true;
}
}, [audio]);
useEffect(() => {
return () => {
audioRef.current.pause();
handleTimeUpdate(false);
};
}, []);
return ( return (
<div className={`flex flex-row w-[1200px] pt-[29px] ${className}`}> <div>
{/* cover */} <PlayerControl
<div className="w-[72px] h-[72px] rounded-[3px] bg-[#000]"> playStatus={playStatus}
<Image src="/img/music.png" alt="music" width={72} height={72} unoptimized className="w-full h-full" /> audio={audio}
</div> duration={duration}
onPlay={handlePlay}
{/* title & author */} onPrev={handlePlayPre}
<div className="h-full ml-[27px] mr-[44px] py-[14px]"> onNext={handlePlayNext}
<AutoScrollContainer hover width="140px" speed={50}> className={className}
<div className="w-auto h-auto"> trackProgress={trackProgress}
<p className="text-[17px] leading-[23.8px] text-[rgba(0,0,0,0.95)]">{'Ferrum Aeternumaaaaaaaaaaa'}</p> onChangeProgress={handleChangeProgress}
<p className="text-[13px] leading-[18.2px] text-[rgba(0,0,0,0.7)]">{'Ensiferum/mmmmsa'}</p> />
</div>
</AutoScrollContainer>
</div>
{/* progress bar */}
<div className="h-full mt-[29px]">
{/* bar */}
<div className="h-full cursor-pointer">
{/* black */}
<div className="w-[600px] h-[3px] bg-[rgba(0,0,0,1)]" />
{/* gery */}
<div className="w-[600px] h-[3px] mt-[-3px] bg-[rgba(0,0,0,0.1)]" />
{/* point */}
<div className="w-[12px] h-[12px] mt-[-7px] rounded-[50%] bg-[#000]" />
</div>
{/* time */}
<p className="texe-[12px] leading[16.8px]">
<span>{curTime}</span>
<span className="text-[rgba(0,0,0,0.4)]">{` / ${totalTime}`}</span>
</p>
</div>
{/* control */}
<div className="flex flex-row items-center">
<button>
{/* 随机播放 */}
<Image
src={'/img/audio-player/random.svg'}
alt="random"
width={28}
height={28}
className="ml-[55px] mr-[60px]"
/>
</button>
{/* 上一首 */}
<button>
<Image src={'/img/audio-player/next.svg'} alt="pre" width={28} height={28} className="rotate-180" />
</button>
{/* 播放/暂停 */}
<button className="flex justify-center items-center w-[54px] h-[54px] mx-[35px] rounded-[50%] bg-[rgba(180,67,67,1)] overflow-hidden">
{/* <Image src={'/img/audio-player/play.svg'} alt="play" width={28} height={28} /> */}
<Image src={'/img/audio-player/pause.svg'} alt="pause" width={28} height={28} />
</button>
{/* 下一首 */}
<button>
<Image src={'/img/audio-player/next.svg'} alt="next" width={28} height={28} />
</button>
</div>
</div> </div>
); );
} }

@ -0,0 +1,114 @@
'use client';
import Image from 'next/image';
import { secondToDate } from '@/utils/timeFormat';
import { AutoScrollContainer } from '@/components';
interface Props {
audio: SongInfo | null;
playStatus: boolean;
onPlay: () => void;
onPrev: () => void;
onNext: () => void;
onChangeProgress: (value: string) => void;
trackProgress: number;
duration: number;
className?: string;
}
export default function AudioPlayer({
audio,
playStatus,
onPlay,
onPrev,
onNext,
onChangeProgress,
trackProgress,
duration,
className,
}: Props) {
const currentPercentage = duration ? `${(trackProgress / duration) * 100}%` : '0%';
const trackStyling: string = `
-webkit-gradient(linear, 0% 0%, 100% 0%, color-stop(${currentPercentage}, rgb(24,24,24)), color-stop(${currentPercentage}, rgba(0,0,0,0.1)))
`; // 进度条样式
return (
<div className={`flex flex-row w-[1200px] pt-[29px] ${className}`}>
{/* 专辑封面 */}
<div className="w-[72px] h-[72px] rounded-[3px] bg-[#000]">
{audio?.pic && (
<Image src={audio?.pic} alt="music" width={72} height={72} unoptimized className="w-full h-full" />
)}
</div>
{/* title & author */}
<div className="h-full ml-[27px] mr-[44px] py-[14px]">
<AutoScrollContainer auto hover width="140px" speed={50}>
<div className="w-auto h-auto">
<p className="text-[17px] leading-[23.8px] text-[rgba(0,0,0,0.95)]">{audio?.title}</p>
<p className="text-[13px] leading-[18.2px] text-[rgba(0,0,0,0.7)]">{`${audio?.artist}/${audio?.album}`}</p>
</div>
</AutoScrollContainer>
</div>
{/* progress bar */}
<div className="h-full mt-[29px]">
{/* bar */}
<div className="h-full cursor-pointer">
<input
type="range"
value={trackProgress}
step="1"
min="0"
max={duration ? duration : `${duration}`}
className="w-[600px] h-[3px]"
onChange={(e) => onChangeProgress(e.target.value)}
style={{ background: trackStyling }}
/>
</div>
{/* time */}
<p className="texe-[12px] leading[16.8px]">
<span>{`${secondToDate(trackProgress)}`}</span>
<span className="text-[rgba(0,0,0,0.4)]">{` /${secondToDate(duration)}`}</span>
</p>
</div>
{/* control */}
<div className="flex flex-row items-center">
<button>
{/* 随机播放 */}
<Image
src={'/img/audio-player/random.svg'}
alt="random"
width={28}
height={28}
className="ml-[55px] mr-[60px]"
/>
</button>
{/* 上一首 */}
<button onClick={onPrev}>
<Image src={'/img/audio-player/next.svg'} alt="pre" width={28} height={28} className="rotate-180" />
</button>
{/* 播放/暂停 */}
<button
className="flex justify-center items-center w-[54px] h-[54px] mx-[35px] rounded-[50%] bg-[rgba(180,67,67,1)] overflow-hidden"
onClick={onPlay}
>
{/* <Image src={'/img/audio-player/play.svg'} alt="play" width={28} height={28} /> */}
<Image
src={playStatus ? '/img/audio-player/pause.svg' : '/img/audio-player/play.svg'}
alt="pause"
width={28}
height={28}
/>
</button>
{/* 下一首 */}
<button onClick={onNext}>
<Image src={'/img/audio-player/next.svg'} alt="next" width={28} height={28} />
</button>
</div>
</div>
);
}

@ -76,7 +76,7 @@ export default function LoginForm() {
<form autoComplete="on"> <form autoComplete="on">
<div className="w-full flex flex-col"> <div className="w-full flex flex-col">
<input <input
className="w-full h-[56px] rounded-[100px] px-[14px] bg-[#fff] leading-[20px]" className="w-full h-[56px] rounded-[100px] px-[39px] bg-[#fff] leading-[20px]"
type="text" type="text"
value={phone} value={phone}
maxLength={11} maxLength={11}
@ -86,7 +86,7 @@ export default function LoginForm() {
<div className="relative w-full mt-[20px]"> <div className="relative w-full mt-[20px]">
<input <input
className="w-full h-[56px] rounded-[100px] px-[14px] bg-[#fff] leading-[20px]" className="w-full h-[56px] rounded-[100px] px-[39px] bg-[#fff] leading-[20px]"
type="text" type="text"
value={authCode} value={authCode}
maxLength={6} maxLength={6}

@ -1,11 +1,17 @@
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link';
export default function JournalItem({ id, title, pic, artist, haveCollect }: SongInfo) { export default function JournalItem({
id,
title,
pic,
artist,
haveCollect,
onPlay,
}: SongInfo & { onPlay: (id: string) => void }) {
return ( return (
<Link <div
href={`/single/${id}`} className="flex flex-row items-center justify-between w-full h-[72px] my-[3px] py-[12px] px-[18px] rounded-[3px] hover:bg-[#f2f3f7] group cursor-pointer"
className="flex flex-row items-center justify-between w-full h-[72px] my-[3px] py-[12px] px-[18px] rounded-[3px] hover:bg-[#f2f3f7] group" onClick={() => onPlay(id)}
> >
{/* left */} {/* left */}
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
@ -36,6 +42,6 @@ export default function JournalItem({ id, title, pic, artist, haveCollect }: Son
className="w-[24px] h-[24px] overflow-hidden" className="w-[24px] h-[24px] overflow-hidden"
/> />
</div> </div>
</Link> </div>
); );
} }

@ -1,13 +1,46 @@
/** /**
* *
*/ */
'use client';
import { useShallow } from 'zustand/react/shallow';
import { SongCard } from '@/components'; import { SongCard } from '@/components';
import useAudioStore from '@/store/audio';
interface Props {
listId: string;
songList: SongInfo[];
className?: string;
}
export default function JournalItem({ listId, songList, className }: Props) {
const { playStatus, setPlayStatus, setPlayList, audio, playList, playListId, setAudio } = useAudioStore(
useShallow((state) => ({
playStatus: state.playStatus,
setPlayStatus: state.setPlayStatus,
setplayList: state.setPlayList,
audio: state.audio,
setAudio: state.setAudio,
playList: state.playList,
setPlayList: state.setPlayList,
playListId: state.playListId,
})),
);
const handlePlayList = (audioId: string) => {
// 正在播放当前歌单
if (playListId !== listId) {
setPlayList({ id: listId, list: songList });
}
// 正在播放其他歌单
setAudio(audioId);
};
export default function JournalItem({ songList, className }: { songList: SongInfo[]; className?: string }) {
return ( return (
<div className={`${className}`}> <div className={`${className}`}>
{songList.map((song: SongInfo) => ( {songList.map((song: SongInfo) => (
<SongCard key={song.id} {...song} /> <SongCard key={song.id} {...song} onPlay={(audioId: string) => handlePlayList(audioId)} />
))} ))}
</div> </div>
); );

@ -6,43 +6,91 @@ import { apiGetSongInfo } from '@/services';
interface AuioState { interface AuioState {
/** 播放状态 */ /** 播放状态 */
play: boolean; playStatus: boolean;
/** 播放列表id */
playListId: string;
/** 修改播放列表id */
setPlayListId: (id: string) => void;
/** 播放器显示状态 */ /** 播放器显示状态 */
show: boolean; show: boolean;
/** 当前音频信息 */ /** 正在播放的音频信息 */
audioInfo: SongInfo | null; audio: SongInfo | null;
/**
* @param id id playList
*/
setAudio: (id: string | null) => void;
/** 播放列表 */ /** 播放列表 */
playList: Array<SongInfo | null>; playList: Array<SongInfo>;
/** 播放/暂停 */
setPlayStatus: (value: boolean) => void;
/** 显示/隐藏播放器 */ /** 显示/隐藏播放器 */
setShow: (value: boolean) => void; setShow: (value: boolean) => void;
/** 设置播放列表 */
setPlayList: (params: { id: string; list: SongInfo[] }) => void;
/** 获取歌曲信息 */ /** 获取歌曲信息 */
getSongInfo: (id: string) => Promise<boolean>; getSongInfoById: (id: string) => Promise<boolean>;
/** 根据id切换歌曲 */
switchSongById: (id: string) => Promise<boolean>;
/** 切换歌曲 */ /** 切换歌曲 */
switchSong: (id: string) => Promise<boolean>; switchSongByDiff: (diff: number) => void;
} }
const useAuioState = create<AuioState>()( const useAuioState = create<AuioState>()(
devtools( devtools(
persist( persist(
(set) => { (set, get) => {
const audioInfo = null; // 设置当前播放的音频
const getSongInfo = async (id: string) => { const setAudio = (id: string | null) => {
const audio = get().playList.find((item) => item?.id === id) || null;
set({ audio });
set({ playStatus: true });
};
// 根据 id 获取歌曲信息
const getSongInfoById = async (id: string) => {
const res = await apiGetSongInfo(id); const res = await apiGetSongInfo(id);
if (res.code === 200) set(produce((state) => void (state.audioInfo = res.data))); if (res.code === 200) set({ audio: res.data });
return res.code === 200; return res.code === 200;
}; };
// 设置播放列表
const setPlayListId = (id: string) => {
set({ playListId: id });
};
// 切歌
const switchSongByDiff = (diff: number) => {
const playList = get().playList;
if (!playList) return;
const index = playList.findIndex((item) => item.id === get().audio?.id);
const nextIndex = index + diff;
if (nextIndex < 0) setAudio(playList[playList.length - 1].id);
if (nextIndex >= playList.length) setAudio(playList[0].id);
set({ audio: playList[nextIndex] });
setAudio(playList[nextIndex].id);
return;
};
return { return {
play: false, playStatus: false,
show: false, show: false,
audioInfo, audio: null,
setAudio,
playList: [], playList: [],
switchSong: async (id: string) => { switchSongById: async (id: string) => {
const res = await getSongInfo(id); const res = await getSongInfoById(id);
return res; return res;
}, },
playListId: '',
setPlayListId,
setPlayStatus: (value) => set({ playStatus: value }),
setShow: (value) => set({ show: value }), setShow: (value) => set({ show: value }),
getSongInfo, setPlayList: (params) => {
set({ playListId: params.id });
set({ playList: params.list });
},
getSongInfoById,
switchSongByDiff,
}; };
}, },
{ {

@ -0,0 +1,16 @@
export const secondToDate: (v: number) => string = (second) => {
const hourNum = Math.floor(second / 3600);
const minuteNum = Math.floor((second / 60) % 60);
const secondNum = Math.floor(second % 60);
const h = hourNum ? `${hourNum}:` : '';
const m = minuteNum ? `${unitAddZero(minuteNum)}:` : `00:`;
const s = unitAddZero(secondNum);
return `${h}${m}${s}`;
};
// 格式化个位数
const unitAddZero: (v: number) => string = (v) => {
return v < 10 ? `0${v}` : `${v}`;
};
Loading…
Cancel
Save