mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-05-18 03:17:29 +08:00
✨ feat: 添加直接播放歌单功能,优化播放列表加载逻辑,支持异步加载完整歌单
This commit is contained in:
@@ -61,19 +61,27 @@
|
|||||||
>
|
>
|
||||||
<div class="user-play-item-img">
|
<div class="user-play-item-img">
|
||||||
<img :src="getImgUrl(item.coverImgUrl, '200y200')" alt="" />
|
<img :src="getImgUrl(item.coverImgUrl, '200y200')" alt="" />
|
||||||
<div class="user-play-item-overlay">
|
|
||||||
<div class="user-play-item-play-btn">
|
|
||||||
<i class="iconfont icon-playfill text-3xl text-white"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="user-play-item-title">
|
<div class="user-play-item-title">
|
||||||
<div class="user-play-item-title-name">{{ item.name }}</div>
|
<div class="user-play-item-title-name">{{ item.name }}</div>
|
||||||
|
|
||||||
|
<div class="user-play-item-list">
|
||||||
|
<div
|
||||||
|
v-for="song in item.tracks"
|
||||||
|
:key="song.id"
|
||||||
|
class="user-play-item-list-name"
|
||||||
|
>
|
||||||
|
{{ song.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-play-item-count">
|
<div class="user-play-item-count">
|
||||||
<div class="user-play-item-count-tag">
|
<div class="user-play-item-count-tag">
|
||||||
{{ t('common.songCount', { count: item.trackCount }) }}
|
{{ t('common.songCount', { count: item.trackCount }) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="user-play-item-direct-play" @click.stop="handlePlayPlaylist(item.id)">
|
||||||
|
<i class="iconfont icon-playfill text-xl text-white"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,13 +150,15 @@ import { useI18n } from 'vue-i18n';
|
|||||||
|
|
||||||
import { getDayRecommend, getHotSinger } from '@/api/home';
|
import { getDayRecommend, getHotSinger } from '@/api/home';
|
||||||
import { getListDetail } from '@/api/list';
|
import { getListDetail } from '@/api/list';
|
||||||
|
import { getMusicDetail } from '@/api/music';
|
||||||
import { getUserPlaylist } from '@/api/user';
|
import { getUserPlaylist } from '@/api/user';
|
||||||
import MusicList from '@/components/MusicList.vue';
|
import MusicList from '@/components/MusicList.vue';
|
||||||
import { useArtist } from '@/hooks/useArtist';
|
import { useArtist } from '@/hooks/useArtist';
|
||||||
import { useUserStore } from '@/store';
|
import { usePlayerStore, useUserStore } from '@/store';
|
||||||
import { IDayRecommend } from '@/type/day_recommend';
|
import { IDayRecommend } from '@/type/day_recommend';
|
||||||
import { Playlist } from '@/type/list';
|
import { Playlist } from '@/type/list';
|
||||||
import type { IListDetail } from '@/type/listDetail';
|
import type { IListDetail } from '@/type/listDetail';
|
||||||
|
import { SongResult } from '@/type/music';
|
||||||
import type { IHotSinger } from '@/type/singer';
|
import type { IHotSinger } from '@/type/singer';
|
||||||
import {
|
import {
|
||||||
getImgUrl,
|
getImgUrl,
|
||||||
@@ -159,6 +169,7 @@ import {
|
|||||||
} from '@/utils';
|
} from '@/utils';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const playerStore = usePlayerStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -293,6 +304,93 @@ const toPlaylist = async (id: number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加直接播放歌单的方法
|
||||||
|
const handlePlayPlaylist = async (id: number) => {
|
||||||
|
try {
|
||||||
|
// 先显示加载状态
|
||||||
|
playlistLoading.value = true;
|
||||||
|
|
||||||
|
// 获取歌单详情
|
||||||
|
const { data } = await getListDetail(id);
|
||||||
|
|
||||||
|
if (data?.playlist) {
|
||||||
|
// 先使用已有的tracks开始播放(这些是已经在歌单详情中返回的前几首歌曲)
|
||||||
|
if (data.playlist.tracks?.length > 0) {
|
||||||
|
// 格式化歌曲列表
|
||||||
|
const initialSongs = data.playlist.tracks.map((track) => ({
|
||||||
|
...track,
|
||||||
|
source: 'netease',
|
||||||
|
picUrl: track.al.picUrl
|
||||||
|
})) as unknown as SongResult[];
|
||||||
|
|
||||||
|
// 设置播放列表
|
||||||
|
playerStore.setPlayList(initialSongs);
|
||||||
|
|
||||||
|
// 开始播放第一首
|
||||||
|
await playerStore.setPlay(initialSongs[0]);
|
||||||
|
|
||||||
|
// 如果有trackIds,异步加载完整歌单
|
||||||
|
if (data.playlist.trackIds?.length > initialSongs.length) {
|
||||||
|
loadFullPlaylist(data.playlist.trackIds, initialSongs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭加载状态
|
||||||
|
playlistLoading.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('播放歌单失败:', error);
|
||||||
|
playlistLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 异步加载完整歌单
|
||||||
|
const loadFullPlaylist = async (trackIds: { id: number }[], initialSongs: SongResult[]) => {
|
||||||
|
try {
|
||||||
|
// 获取已加载歌曲的ID集合,避免重复加载
|
||||||
|
const loadedIds = new Set(initialSongs.map((song) => song.id));
|
||||||
|
|
||||||
|
// 筛选出未加载的ID
|
||||||
|
const unloadedTrackIds = trackIds
|
||||||
|
.filter((item) => !loadedIds.has(item.id as number))
|
||||||
|
.map((item) => item.id);
|
||||||
|
|
||||||
|
if (unloadedTrackIds.length === 0) return;
|
||||||
|
|
||||||
|
// 分批获取歌曲详情,每批最多获取500首
|
||||||
|
const batchSize = 500;
|
||||||
|
const allSongs = [...initialSongs];
|
||||||
|
|
||||||
|
for (let i = 0; i < unloadedTrackIds.length; i += batchSize) {
|
||||||
|
const batchIds = unloadedTrackIds.slice(i, i + batchSize);
|
||||||
|
if (batchIds.length > 0) {
|
||||||
|
try {
|
||||||
|
const { data: songsData } = await getMusicDetail(batchIds);
|
||||||
|
if (songsData?.songs?.length) {
|
||||||
|
const formattedSongs = songsData.songs.map((item) => ({
|
||||||
|
...item,
|
||||||
|
source: 'netease',
|
||||||
|
picUrl: item.al.picUrl
|
||||||
|
})) as unknown as SongResult[];
|
||||||
|
|
||||||
|
allSongs.push(...formattedSongs);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取批次歌曲详情失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新完整的播放列表但保持当前播放的歌曲不变
|
||||||
|
if (allSongs.length > initialSongs.length) {
|
||||||
|
console.log('更新播放列表,总歌曲数:', allSongs.length);
|
||||||
|
playerStore.setPlayList(allSongs);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载完整歌单失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 监听登录状态
|
// 监听登录状态
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (userStore.user) {
|
if (userStore.user) {
|
||||||
@@ -416,18 +514,12 @@ const getPlaylistGridClass = (length: number) => {
|
|||||||
&:hover {
|
&:hover {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-5px);
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||||
.user-play-item-overlay {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@apply absolute inset-0 w-full h-full object-cover;
|
@apply absolute inset-0 w-full h-full object-cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-overlay {
|
|
||||||
@apply absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center opacity-0 transition-opacity duration-300;
|
|
||||||
}
|
|
||||||
&-title {
|
&-title {
|
||||||
@apply absolute top-0 left-0 right-0 p-2 bg-gradient-to-b from-black/70 to-transparent z-10;
|
@apply absolute top-0 left-0 right-0 p-2 bg-gradient-to-b from-black/70 to-transparent z-10;
|
||||||
&-name {
|
&-name {
|
||||||
@@ -435,11 +527,17 @@ const getPlaylistGridClass = (length: number) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-count {
|
&-count {
|
||||||
@apply absolute bottom-2 right-2 z-10;
|
@apply absolute bottom-2 left-2 z-10;
|
||||||
&-tag {
|
&-tag {
|
||||||
@apply px-2 py-0.5 text-xs text-white bg-black/50 backdrop-blur-sm rounded-full;
|
@apply px-2 py-0.5 text-xs text-white bg-black/50 backdrop-blur-sm rounded-full;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&-direct-play {
|
||||||
|
@apply absolute bottom-2 right-2 z-20 w-10 h-10 rounded-full bg-green-600 hover:bg-green-700 flex items-center justify-center cursor-pointer transform scale-90 hover:scale-100 transition-all;
|
||||||
|
&:hover {
|
||||||
|
@apply shadow-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
&-play-btn {
|
&-play-btn {
|
||||||
@apply flex items-center justify-center;
|
@apply flex items-center justify-center;
|
||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
|
|||||||
Reference in New Issue
Block a user