mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-24 16:27:23 +08:00
✨ feat: 优化列表渲染
This commit is contained in:
@@ -10,77 +10,69 @@
|
||||
>
|
||||
<div class="music-page">
|
||||
<div class="music-close">
|
||||
<i class="icon ri-layout-column-line" @click="doubleDisply = !doubleDisply"></i>
|
||||
<i class="icon iconfont icon-icon_error" @click="close"></i>
|
||||
</div>
|
||||
<div class="music-title text-el">{{ name }}</div>
|
||||
<!-- 歌单歌曲列表 -->
|
||||
<div class="music-list">
|
||||
<n-scrollbar @scroll="handleScroll">
|
||||
<div
|
||||
v-loading="loading || !songList.length"
|
||||
class="music-list-content"
|
||||
:class="{ 'double-list': doubleDisply }"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in displayedSongs"
|
||||
:key="item.id"
|
||||
class="double-item"
|
||||
:class="setAnimationClass('animate__bounceInUp')"
|
||||
:style="setAnimationDelay(index, 5)"
|
||||
>
|
||||
<song-item :item="formatDetail(item)" @play="handlePlay" />
|
||||
</div>
|
||||
<div v-if="isLoadingMore" class="loading-more">加载更多...</div>
|
||||
</div>
|
||||
<play-bottom />
|
||||
</n-scrollbar>
|
||||
|
||||
<!-- <n-virtual-list :item-size="42" :items="displayedSongs" item-resizable @scroll="handleScroll">
|
||||
<template #default="{ item, index }">
|
||||
<div :key="item.id" class="double-item">
|
||||
<song-item :item="formatDetail(item)" @play="handlePlay" />
|
||||
</div>
|
||||
<div v-loading="loading" class="music-list">
|
||||
<n-virtual-list
|
||||
v-if="displayedSongs.length"
|
||||
ref="virtualListRef"
|
||||
:items="displayedSongs"
|
||||
:item-size="60"
|
||||
:keep-alive="true"
|
||||
:min-size="5"
|
||||
:style="{ height: listHeight }"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<song-item :item="formatDetail(item)" @play="handlePlay" />
|
||||
</template>
|
||||
</n-virtual-list>
|
||||
<play-bottom /> -->
|
||||
<div v-else-if="loading" class="loading-more">加载中...</div>
|
||||
<play-bottom />
|
||||
</div>
|
||||
</div>
|
||||
</n-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 导入 NVirtualListInst 类型
|
||||
import type { VirtualListInst } from 'naive-ui';
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
import { getMusicDetail } from '@/api/music';
|
||||
import SongItem from '@/components/common/SongItem.vue';
|
||||
import { isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
|
||||
import { isMobile } from '@/utils';
|
||||
|
||||
import PlayBottom from './common/PlayBottom.vue';
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const {
|
||||
songList,
|
||||
loading = false,
|
||||
listInfo,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
show: boolean;
|
||||
name: string;
|
||||
songList: any[];
|
||||
loading?: boolean;
|
||||
listInfo?: any;
|
||||
listInfo?: {
|
||||
trackIds: { id: number }[];
|
||||
[key: string]: any;
|
||||
};
|
||||
}>();
|
||||
const emit = defineEmits(['update:show', 'update:loading']);
|
||||
|
||||
const page = ref(0);
|
||||
const pageSize = 20;
|
||||
const total = ref(0);
|
||||
const isLoadingMore = ref(false);
|
||||
const displayedSongs = ref<any[]>([]);
|
||||
|
||||
// 双排显示开关
|
||||
const doubleDisply = ref(false);
|
||||
// 计算总数
|
||||
const total = computed(() => {
|
||||
if (props.listInfo?.trackIds) {
|
||||
return props.listInfo.trackIds.length;
|
||||
}
|
||||
return props.songList.length;
|
||||
});
|
||||
|
||||
const formatDetail = computed(() => (detail: any) => {
|
||||
const song = {
|
||||
@@ -95,7 +87,7 @@ const formatDetail = computed(() => (detail: any) => {
|
||||
});
|
||||
|
||||
const handlePlay = () => {
|
||||
const tracks = songList || [];
|
||||
const tracks = props.songList || [];
|
||||
store.commit(
|
||||
'setPlayList',
|
||||
tracks.map((item) => ({
|
||||
@@ -112,40 +104,70 @@ const close = () => {
|
||||
emit('update:show', false);
|
||||
};
|
||||
|
||||
// 优化加载更多歌曲的函数
|
||||
const loadMoreSongs = async () => {
|
||||
if (displayedSongs.value.length >= total.value) return;
|
||||
if (isLoadingMore.value || displayedSongs.value.length >= total.value) return;
|
||||
|
||||
isLoadingMore.value = true;
|
||||
try {
|
||||
const trackIds = listInfo.trackIds
|
||||
.slice(page.value * pageSize, (page.value + 1) * pageSize)
|
||||
.map((item: any) => item.id);
|
||||
const reslist = await getMusicDetail(trackIds);
|
||||
// displayedSongs.value = displayedSongs.value.concat(reslist.data.songs);
|
||||
displayedSongs.value = JSON.parse(JSON.stringify([...displayedSongs.value, ...reslist.data.songs]));
|
||||
page.value++;
|
||||
if (props.listInfo?.trackIds) {
|
||||
// 如果有 trackIds,需要分批请求歌曲详情
|
||||
const start = page.value * pageSize;
|
||||
const end = Math.min((page.value + 1) * pageSize, total.value);
|
||||
const trackIds = props.listInfo.trackIds.slice(start, end).map((item) => item.id);
|
||||
|
||||
if (trackIds.length > 0) {
|
||||
const { data } = await getMusicDetail(trackIds);
|
||||
displayedSongs.value = [...displayedSongs.value, ...data.songs];
|
||||
page.value++;
|
||||
}
|
||||
} else {
|
||||
// 如果没有 trackIds,直接使用 songList 分页
|
||||
const start = page.value * pageSize;
|
||||
const end = Math.min((page.value + 1) * pageSize, props.songList.length);
|
||||
const newSongs = props.songList.slice(start, end);
|
||||
displayedSongs.value = [...displayedSongs.value, ...newSongs];
|
||||
page.value++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('error', error);
|
||||
console.error('加载歌曲失败:', error);
|
||||
} finally {
|
||||
isLoadingMore.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = (e: any) => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||
if (scrollTop + clientHeight >= scrollHeight - 50 && !isLoadingMore.value) {
|
||||
// 添加虚拟列表的引用
|
||||
const virtualListRef = ref<VirtualListInst | null>(null);
|
||||
|
||||
// 修改滚动处理函数
|
||||
const handleScroll = (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (!target) return;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = target;
|
||||
if (scrollHeight - scrollTop - clientHeight < 100 && !isLoadingMore.value) {
|
||||
loadMoreSongs();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听 songList 变化,重置分页状态
|
||||
watch(
|
||||
() => songList,
|
||||
() => props.songList,
|
||||
(newSongs) => {
|
||||
displayedSongs.value = JSON.parse(JSON.stringify(newSongs));
|
||||
total.value = listInfo ? listInfo.trackIds.length : displayedSongs.value.length;
|
||||
page.value = 0;
|
||||
displayedSongs.value = newSongs.slice(0, pageSize);
|
||||
if (newSongs.length > pageSize) {
|
||||
page.value = 1;
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 添加计算属性来处理列表高度
|
||||
const listHeight = computed(() => {
|
||||
const baseHeight = '100%'; // 减去标题高度
|
||||
return store.state.isPlay ? `calc(100% - 90px)` : baseHeight; // 112px 是 PlayBottom 的高度
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -168,9 +190,13 @@ watch(
|
||||
|
||||
&-list {
|
||||
height: calc(100% - 60px);
|
||||
position: relative; // 添加相对定位
|
||||
|
||||
&-content {
|
||||
min-height: 400px;
|
||||
:deep(.n-virtual-list__scroll) {
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,14 +212,20 @@ watch(
|
||||
}
|
||||
|
||||
.double-list {
|
||||
@apply flex flex-wrap gap-5;
|
||||
|
||||
.double-item {
|
||||
width: calc(50% - 10px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.song-item {
|
||||
background-color: #191919;
|
||||
}
|
||||
}
|
||||
|
||||
// 确保 PlayBottom 不会影响滚动区域
|
||||
:deep(.bottom) {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import directives from './directive';
|
||||
const app = createApp(App);
|
||||
|
||||
Object.keys(directives).forEach((key: string) => {
|
||||
app.directive(key, directives[key]);
|
||||
app.directive(key, directives[key as keyof typeof directives]);
|
||||
});
|
||||
app.use(router);
|
||||
app.use(store);
|
||||
|
||||
@@ -96,7 +96,7 @@ const handleScroll = (e: any) => {
|
||||
// 监听窗口大小变化,调整每行显示数量
|
||||
const updateItemsPerRow = () => {
|
||||
const width = window.innerWidth;
|
||||
if (width > 1800) ITEMS_PER_ROW.value = 10;
|
||||
if (width > 1800) ITEMS_PER_ROW.value = 8;
|
||||
else if (width > 1200) ITEMS_PER_ROW.value = 8;
|
||||
else if (width > 768) ITEMS_PER_ROW.value = 6;
|
||||
else ITEMS_PER_ROW.value = 5;
|
||||
|
||||
@@ -57,12 +57,18 @@ onActivated(() => {
|
||||
|
||||
const isShowList = ref(false);
|
||||
const list = ref<Playlist>();
|
||||
const listLoading = ref(false);
|
||||
// 展示歌单
|
||||
const showPlaylist = async (id: number) => {
|
||||
const showPlaylist = async (id: number, name: string) => {
|
||||
isShowList.value = true;
|
||||
list.value = {};
|
||||
listLoading.value = true;
|
||||
|
||||
list.value = {
|
||||
name,
|
||||
} as Playlist;
|
||||
const { data } = await getListDetail(id);
|
||||
list.value = data.playlist;
|
||||
listLoading.value = false;
|
||||
};
|
||||
|
||||
const handlePlay = () => {
|
||||
@@ -103,7 +109,12 @@ const handlePlay = () => {
|
||||
<div class="play-list" :class="setAnimationClass('animate__fadeInLeft')">
|
||||
<div class="title">创建的歌单</div>
|
||||
<n-scrollbar>
|
||||
<div v-for="(item, index) in playList" :key="index" class="play-list-item" @click="showPlaylist(item.id)">
|
||||
<div
|
||||
v-for="(item, index) in playList"
|
||||
:key="index"
|
||||
class="play-list-item"
|
||||
@click="showPlaylist(item.id, item.name)"
|
||||
>
|
||||
<n-image :src="getImgUrl(item.coverImgUrl, '50y50')" class="play-list-item-img" lazy preview-disabled />
|
||||
<div class="play-list-item-info">
|
||||
<div class="play-list-item-name">{{ item.name }}</div>
|
||||
@@ -133,7 +144,13 @@ const handlePlay = () => {
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<music-list v-model:show="isShowList" :name="list?.name || ''" :song-list="list?.tracks || []" :list-info="list" />
|
||||
<music-list
|
||||
v-model:show="isShowList"
|
||||
:name="list?.name || ''"
|
||||
:song-list="list?.tracks || []"
|
||||
:list-info="list"
|
||||
:loading="listLoading"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user