🎈 perf: 优化加载 升级vue3.5 electron32等多个包 添加v-loading指令

This commit is contained in:
alger
2024-09-04 15:20:43 +08:00
parent a4eda61a86
commit b6a5461a1d
14 changed files with 268 additions and 86 deletions
+1 -2
View File
@@ -1,10 +1,10 @@
/* eslint-disable */ /* eslint-disable */
/* prettier-ignore */
// @ts-nocheck // @ts-nocheck
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
export {} export {}
/* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
MPop: typeof import('./src/components/common/MPop.vue')['default'] MPop: typeof import('./src/components/common/MPop.vue')['default']
@@ -23,7 +23,6 @@ declare module 'vue' {
NPopover: typeof import('naive-ui')['NPopover'] NPopover: typeof import('naive-ui')['NPopover']
NScrollbar: typeof import('naive-ui')['NScrollbar'] NScrollbar: typeof import('naive-ui')['NScrollbar']
NSlider: typeof import('naive-ui')['NSlider'] NSlider: typeof import('naive-ui')['NSlider']
NSpin: typeof import('naive-ui')['NSpin']
NSwitch: typeof import('naive-ui')['NSwitch'] NSwitch: typeof import('naive-ui')['NSwitch']
NTooltip: typeof import('naive-ui')['NTooltip'] NTooltip: typeof import('naive-ui')['NTooltip']
PlayBottom: typeof import('./src/components/common/PlayBottom.vue')['default'] PlayBottom: typeof import('./src/components/common/PlayBottom.vue')['default']
+23 -23
View File
@@ -1,6 +1,6 @@
{ {
"name": "alger-music", "name": "alger-music",
"version": "1.5.1", "version": "1.6.0",
"description": "这是一个用于音乐播放的应用程序。", "description": "这是一个用于音乐播放的应用程序。",
"author": "Alger <algerkc@qq.com>", "author": "Alger <algerkc@qq.com>",
"main": "app.js", "main": "app.js",
@@ -21,16 +21,16 @@
"@tailwindcss/postcss7-compat": "^2.2.4", "@tailwindcss/postcss7-compat": "^2.2.4",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^5.1.3",
"@vue/compiler-sfc": "^3.3.4", "@vue/compiler-sfc": "^3.5.0",
"@vue/eslint-config-typescript": "^12.0.0", "@vue/eslint-config-typescript": "^13.0.0",
"@vue/runtime-core": "^3.3.4", "@vue/runtime-core": "^3.5.0",
"@vueuse/core": "^10.7.1", "@vueuse/core": "^11.0.3",
"@vueuse/electron": "^10.9.0", "@vueuse/electron": "^11.0.3",
"autoprefixer": "^9.8.6", "autoprefixer": "^10.4.20",
"axios": "^0.21.1", "axios": "^1.7.7",
"electron": "^30.0.0", "electron": "^32.0.1",
"electron-builder": "^24.13.0", "electron-builder": "^25.0.5",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.0.0",
@@ -40,22 +40,22 @@
"eslint-plugin-vue": "^9.21.1", "eslint-plugin-vue": "^9.21.1",
"eslint-plugin-vue-scoped-css": "^2.7.2", "eslint-plugin-vue-scoped-css": "^2.7.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"naive-ui": "^2.38.2", "naive-ui": "^2.39.0",
"postcss": "^7.0.36", "postcss": "^8.4.44",
"prettier": "^3.2.5", "prettier": "^3.3.3",
"remixicon": "^4.2.0", "remixicon": "^4.2.0",
"sass": "^1.35.2", "sass": "^1.78.0",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.4", "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.4",
"typescript": "^4.3.2", "typescript": "^5.5.4",
"unplugin-auto-import": "^0.17.2", "unplugin-auto-import": "^0.18.2",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.27.4",
"vfonts": "^0.1.0", "vfonts": "^0.1.0",
"vite": "^4.4.7", "vite": "^5.4.3",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "1.0.0-beta.5", "vite-plugin-vue-devtools": "7.4.0",
"vue": "^3.3.4", "vue": "^3.5.0",
"vue-router": "^4.2.4", "vue-router": "^4.4.3",
"vue-tsc": "^0.0.24", "vue-tsc": "^2.1.4",
"vuex": "^4.1.0" "vuex": "^4.1.0"
} }
} }
+14 -25
View File
@@ -9,20 +9,18 @@
<i class="iconfont icon-icon_error music-close" @click="close"></i> <i class="iconfont icon-icon_error music-close" @click="close"></i>
<div class="music-title text-el">{{ name }}</div> <div class="music-title text-el">{{ name }}</div>
<!-- 歌单歌曲列表 --> <!-- 歌单歌曲列表 -->
<div :show="loading" class="music-list"> <div class="music-list">
<n-scrollbar> <n-scrollbar>
<n-spin :show="loading"> <div v-loading="loading || !songList.length" class="music-list-content">
<div class="music-list-content"> <div
<div v-for="(item, index) in songList"
v-for="(item, index) in songList" :key="item.id"
:key="item.id" :class="setAnimationClass('animate__bounceInLeft')"
:class="setAnimationClass('animate__bounceInUp')" :style="setAnimationDelay(index, 50)"
:style="setAnimationDelay(index, 50)" >
> <song-item :item="formatDetail(item)" @play="handlePlay" />
<song-item :item="formatDetail(item)" @play="handlePlay" />
</div>
</div> </div>
</n-spin> </div>
<play-bottom /> <play-bottom />
</n-scrollbar> </n-scrollbar>
</div> </div>
@@ -38,24 +36,15 @@ import { isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
import PlayBottom from './common/PlayBottom.vue'; import PlayBottom from './common/PlayBottom.vue';
const loading = ref(true);
const store = useStore(); const store = useStore();
const props = defineProps<{ const { songList, loading = false } = defineProps<{
show: boolean; show: boolean;
name: string; name: string;
songList: any[]; songList: any[];
loading?: boolean;
}>(); }>();
const emit = defineEmits(['update:show']); const emit = defineEmits(['update:show', 'update:loading']);
watch(
() => props.songList,
(val) => {
loading.value = !(val && val.length);
},
{ immediate: true },
);
const formatDetail = computed(() => (detail: any) => { const formatDetail = computed(() => (detail: any) => {
const song = { const song = {
@@ -70,7 +59,7 @@ const formatDetail = computed(() => (detail: any) => {
}); });
const handlePlay = () => { const handlePlay = () => {
const tracks = props.songList || []; const tracks = songList || [];
store.commit('setPlayList', tracks); store.commit('setPlayList', tracks);
}; };
+20 -3
View File
@@ -4,6 +4,7 @@
<div class="recommend-singer"> <div class="recommend-singer">
<div class="recommend-singer-list"> <div class="recommend-singer-list">
<div <div
v-if="dayRecommendData"
class="recommend-singer-item relative" class="recommend-singer-item relative"
:class="setAnimationClass('animate__backInRight')" :class="setAnimationClass('animate__backInRight')"
:style="setAnimationDelay(0, 100)" :style="setAnimationDelay(0, 100)"
@@ -27,7 +28,7 @@
</div> </div>
</div> </div>
<div <div
v-for="(item, index) in hotSingerData?.artists.slice(0, 4)" v-for="(item, index) in hotSingerData?.artists"
:key="item.id" :key="item.id"
class="recommend-singer-item relative" class="recommend-singer-item relative"
:class="setAnimationClass('animate__backInRight')" :class="setAnimationClass('animate__backInRight')"
@@ -59,6 +60,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useStore } from 'vuex';
import { getDayRecommend, getHotSinger } from '@/api/home'; import { getDayRecommend, getHotSinger } from '@/api/home';
import router from '@/router'; import router from '@/router';
@@ -66,6 +68,8 @@ import { IDayRecommend } from '@/type/day_recommend';
import type { IHotSinger } from '@/type/singer'; import type { IHotSinger } from '@/type/singer';
import { getImgUrl, setAnimationClass, setAnimationDelay, setBackgroundImg } from '@/utils'; import { getImgUrl, setAnimationClass, setAnimationDelay, setBackgroundImg } from '@/utils';
const store = useStore();
// 歌手信息 // 歌手信息
const hotSingerData = ref<IHotSinger>(); const hotSingerData = ref<IHotSinger>();
const dayRecommendData = ref<IDayRecommend>(); const dayRecommendData = ref<IDayRecommend>();
@@ -82,18 +86,24 @@ const showMusic = ref(false);
// }; // };
// 页面初始化 // 页面初始化
onMounted(async () => { onMounted(async () => {
await loadData();
});
const loadData = async () => {
try { try {
const [{ data: singerData }, { data: dayRecommend }] = await Promise.all([ const [{ data: singerData }, { data: dayRecommend }] = await Promise.all([
getHotSinger({ offset: 0, limit: 5 }), getHotSinger({ offset: 0, limit: 5 }),
getDayRecommend(), getDayRecommend(),
]); ]);
if (dayRecommend.data) {
singerData.artists = singerData.artists.slice(0, 4);
}
hotSingerData.value = singerData; hotSingerData.value = singerData;
dayRecommendData.value = dayRecommend.data; dayRecommendData.value = dayRecommend.data;
} catch (error) { } catch (error) {
console.error('error', error); console.error('error', error);
} }
}); };
const toSearchSinger = (keyword: string) => { const toSearchSinger = (keyword: string) => {
router.push({ router.push({
@@ -103,6 +113,13 @@ const toSearchSinger = (keyword: string) => {
}, },
}); });
}; };
// 监听登录状态
watchEffect(() => {
if (store.state.user) {
loadData();
}
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
+9 -1
View File
@@ -1,7 +1,12 @@
<template> <template>
<div class="recommend-music"> <div class="recommend-music">
<div class="title" :class="setAnimationClass('animate__fadeInLeft')">本周最热音乐</div> <div class="title" :class="setAnimationClass('animate__fadeInLeft')">本周最热音乐</div>
<div v-show="recommendMusic?.result" class="recommend-music-list" :class="setAnimationClass('animate__bounceInUp')"> <div
v-show="recommendMusic?.result"
v-loading="loading"
class="recommend-music-list"
:class="setAnimationClass('animate__bounceInUp')"
>
<!-- 推荐音乐列表 --> <!-- 推荐音乐列表 -->
<template v-for="(item, index) in recommendMusic?.result" :key="item.id"> <template v-for="(item, index) in recommendMusic?.result" :key="item.id">
<div :class="setAnimationClass('animate__bounceInUp')" :style="setAnimationDelay(index, 100)"> <div :class="setAnimationClass('animate__bounceInUp')" :style="setAnimationDelay(index, 100)">
@@ -24,11 +29,14 @@ import SongItem from './common/SongItem.vue';
const store = useStore(); const store = useStore();
// 推荐歌曲 // 推荐歌曲
const recommendMusic = ref<IRecommendMusic>(); const recommendMusic = ref<IRecommendMusic>();
const loading = ref(false);
// 加载推荐歌曲 // 加载推荐歌曲
const loadRecommendMusic = async () => { const loadRecommendMusic = async () => {
loading.value = true;
const { data } = await getRecommendMusic({ limit: 10 }); const { data } = await getRecommendMusic({ limit: 10 });
recommendMusic.value = data; recommendMusic.value = data;
loading.value = false;
}; };
// 页面初始化 // 页面初始化
+7
View File
@@ -0,0 +1,7 @@
import { vLoading } from './loading/index';
const directives = {
loading: vLoading,
};
export default directives;
+40
View File
@@ -0,0 +1,40 @@
import { createVNode, render, VNode } from 'vue';
import Loading from './index.vue';
const vnode: VNode = createVNode(Loading) as VNode;
export const vLoading = {
// 在绑定元素的父组件 及他自己的所有子节点都挂载完成后调用
mounted: (el: HTMLElement, binding: any) => {
render(vnode, el);
},
// 在绑定元素的父组件 及他自己的所有子节点都更新后调用
updated: (el: HTMLElement, binding: any) => {
if (binding.value) {
vnode?.component?.exposed.show();
} else {
vnode?.component?.exposed.hide();
}
// 动态添加删除自定义class: loading-parent
formatterClass(el, binding);
},
// 绑定元素的父组件卸载后调用
unmounted: () => {
vnode?.component?.exposed.hide();
},
};
function formatterClass(el: HTMLElement, binding: any) {
const classStr = el.getAttribute('class');
const tagetClass: number = classStr?.indexOf('loading-parent') as number;
if (binding.value) {
if (tagetClass === -1) {
el.setAttribute('class', `${classStr} loading-parent`);
}
} else if (tagetClass > -1) {
const classArray: Array<string> = classStr?.split('') as string[];
classArray.splice(tagetClass - 1, tagetClass + 15);
el.setAttribute('class', classArray?.join(''));
}
}
+92
View File
@@ -0,0 +1,92 @@
<!-- -->
<template>
<div v-if="isShow" class="loading-box">
<div class="mask" :style="{ background: maskBackground }"></div>
<div class="loading-content-box">
<n-spin size="small" />
<div :style="{ color: textColor }" class="tip">{{ tip }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { NSpin } from 'naive-ui';
import { ref } from 'vue';
defineProps({
tip: {
type: String,
default() {
return '加载中...';
},
},
maskBackground: {
type: String,
default() {
return 'rgba(0, 0, 0, 0.8)';
},
},
loadingColor: {
type: String,
default() {
return 'rgba(255, 255, 255, 1)';
},
},
textColor: {
type: String,
default() {
return 'rgba(255, 255, 255, 1)';
},
},
});
const isShow = ref(false);
const show = () => {
isShow.value = true;
};
const hide = () => {
isShow.value = false;
};
defineExpose({
show,
hide,
isShow,
});
</script>
<style lang="scss" scoped>
.loading-box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 9999;
.n-spin {
// color: #ccc;
}
.mask {
width: 100%;
height: 100%;
}
.loading-content-box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.tip {
font-size: 14px;
margin-top: 8px;
}
}
</style>
+5
View File
@@ -10,8 +10,13 @@ import router from '@/router';
import store from '@/store'; import store from '@/store';
import App from './App.vue'; import App from './App.vue';
import directives from './directive';
const app = createApp(App); const app = createApp(App);
Object.keys(directives).forEach((key: string) => {
app.directive(key, directives[key]);
});
app.use(router); app.use(router);
app.use(store); app.use(store);
app.mount('#app'); app.mount('#app');
+9 -1
View File
@@ -16,19 +16,24 @@ const showMusic = ref(false);
const recommendItem = ref<IRecommendItem | null>(); const recommendItem = ref<IRecommendItem | null>();
const listDetail = ref<IListDetail | null>(); const listDetail = ref<IListDetail | null>();
const listLoading = ref(true);
const selectRecommendItem = async (item: IRecommendItem) => { const selectRecommendItem = async (item: IRecommendItem) => {
listLoading.value = true;
recommendItem.value = null; recommendItem.value = null;
listDetail.value = null; listDetail.value = null;
showMusic.value = true; showMusic.value = true;
recommendItem.value = item; recommendItem.value = item;
const { data } = await getListDetail(item.id); const { data } = await getListDetail(item.id);
listDetail.value = data; listDetail.value = data;
listLoading.value = false;
}; };
const route = useRoute(); const route = useRoute();
const listTitle = ref(route.query.type || '歌单列表'); const listTitle = ref(route.query.type || '歌单列表');
const loading = ref(false);
const loadList = async (type: string) => { const loadList = async (type: string) => {
loading.value = true;
const params = { const params = {
cat: type || '', cat: type || '',
limit: 30, limit: 30,
@@ -36,6 +41,7 @@ const loadList = async (type: string) => {
}; };
const { data } = await getListByCat(params); const { data } = await getListByCat(params);
recommendList.value = data.playlists; recommendList.value = data.playlists;
loading.value = false;
}; };
if (route.query.type) { if (route.query.type) {
@@ -51,6 +57,7 @@ watch(
async (newParams) => { async (newParams) => {
if (newParams.type) { if (newParams.type) {
recommendList.value = null; recommendList.value = null;
listTitle.value = newParams.type || '歌单列表';
loadList(newParams.type as string); loadList(newParams.type as string);
} }
}, },
@@ -62,7 +69,7 @@ watch(
<div class="recommend-title" :class="setAnimationClass('animate__bounceInLeft')">{{ listTitle }}</div> <div class="recommend-title" :class="setAnimationClass('animate__bounceInLeft')">{{ listTitle }}</div>
<!-- 歌单列表 --> <!-- 歌单列表 -->
<n-scrollbar class="recommend" :size="100" @click="showMusic = false"> <n-scrollbar class="recommend" :size="100" @click="showMusic = false">
<div v-if="recommendList" class="recommend-list"> <div v-loading="loading" class="recommend-list">
<div <div
v-for="(item, index) in recommendList" v-for="(item, index) in recommendList"
:key="item.id" :key="item.id"
@@ -91,6 +98,7 @@ watch(
</n-scrollbar> </n-scrollbar>
<music-list <music-list
v-model:show="showMusic" v-model:show="showMusic"
v-model:loading="listLoading"
:name="recommendItem?.name || ''" :name="recommendItem?.name || ''"
:song-list="listDetail?.playlist.tracks || []" :song-list="listDetail?.playlist.tracks || []"
/> />
+8
View File
@@ -70,6 +70,14 @@ const timerIsQr = (key: string) => {
return timer; return timer;
}; };
// 离开页面时
onBeforeUnmount(() => {
if (timerRef.value) {
clearInterval(timerRef.value);
timerRef.value = null;
}
});
// 是否扫码登陆 // 是否扫码登陆
const isQr = ref(!isMobile.value); const isQr = ref(!isMobile.value);
const chooseQr = () => { const chooseQr = () => {
+8 -2
View File
@@ -4,7 +4,7 @@
<h2>推荐MV</h2> <h2>推荐MV</h2>
</div> </div>
<n-scrollbar :size="100"> <n-scrollbar :size="100">
<div class="mv-list-content" :class="setAnimationClass('animate__bounceInLeft')"> <div v-loading="loading" class="mv-list-content" :class="setAnimationClass('animate__bounceInLeft')">
<div <div
v-for="(item, index) in mvList" v-for="(item, index) in mvList"
:key="item.id" :key="item.id"
@@ -32,7 +32,7 @@
</n-scrollbar> </n-scrollbar>
<n-drawer :show="showMv" height="100vh" placement="bottom" :z-index="999999999"> <n-drawer :show="showMv" height="100vh" placement="bottom" :z-index="999999999">
<div class="mv-detail"> <div v-loading="mvLoading" class="mv-detail">
<video :src="playMvUrl" controls autoplay></video> <video :src="playMvUrl" controls autoplay></video>
<div class="mv-detail-title"> <div class="mv-detail-title">
<div class="title">{{ playMvItem?.name }}</div> <div class="title">{{ playMvItem?.name }}</div>
@@ -62,19 +62,25 @@ const mvList = ref<Array<IMvItem>>([]);
const playMvItem = ref<IMvItem>(); const playMvItem = ref<IMvItem>();
const playMvUrl = ref<string>(); const playMvUrl = ref<string>();
const store = useStore(); const store = useStore();
const loading = ref(false);
onMounted(async () => { onMounted(async () => {
loading.value = true;
const res = await getTopMv(30); const res = await getTopMv(30);
mvList.value = res.data.data; mvList.value = res.data.data;
loading.value = false;
}); });
const mvLoading = ref(false);
const handleShowMv = async (item: IMvItem) => { const handleShowMv = async (item: IMvItem) => {
mvLoading.value = true;
store.commit('setIsPlay', false); store.commit('setIsPlay', false);
store.commit('setPlayMusic', false); store.commit('setPlayMusic', false);
showMv.value = true; showMv.value = true;
const res = await getMvUrl(item.id); const res = await getMvUrl(item.id);
playMvItem.value = item; playMvItem.value = item;
playMvUrl.value = res.data.data.url; playMvUrl.value = res.data.data.url;
mvLoading.value = false;
}; };
const close = () => { const close = () => {
+22 -24
View File
@@ -29,32 +29,30 @@
:native-scrollbar="false" :native-scrollbar="false"
> >
<div class="title">{{ hotKeyword }}</div> <div class="title">{{ hotKeyword }}</div>
<n-spin :show="searchDetailLoading"> <div v-loading="searchDetailLoading" class="search-list-box">
<div class="search-list-box"> <template v-if="searchDetail">
<template v-if="searchDetail"> <div
<div v-for="(item, index) in searchDetail?.songs"
v-for="(item, index) in searchDetail?.songs" :key="item.id"
:key="item.id" :class="setAnimationClass('animate__bounceInRight')"
:class="setAnimationClass('animate__bounceInRight')" :style="setAnimationDelay(index, 50)"
:style="setAnimationDelay(index, 50)" >
> <song-item :item="item" @play="handlePlay" />
<song-item :item="item" @play="handlePlay" /> </div>
</div> <template v-for="(list, key) in searchDetail">
<template v-for="(list, key) in searchDetail"> <template v-if="key.toString() !== 'songs'">
<template v-if="key.toString() !== 'songs'"> <div
<div v-for="(item, index) in list"
v-for="(item, index) in list" :key="item.id"
:key="item.id" :class="setAnimationClass('animate__bounceInRight')"
:class="setAnimationClass('animate__bounceInRight')" :style="setAnimationDelay(index, 50)"
:style="setAnimationDelay(index, 50)" >
> <SearchItem :item="item" />
<SearchItem :item="item" /> </div>
</div>
</template>
</template> </template>
</template> </template>
</div> </template>
</n-spin> </div>
</n-layout> </n-layout>
</div> </div>
</template> </template>
+10 -5
View File
@@ -21,6 +21,7 @@ const router = useRouter();
const userDetail = ref<IUserDetail>(); const userDetail = ref<IUserDetail>();
const playList = ref<any[]>([]); const playList = ref<any[]>([]);
const recordList = ref(); const recordList = ref();
const infoLoading = ref(false);
const user = computed(() => store.state.user); const user = computed(() => store.state.user);
@@ -29,6 +30,7 @@ const loadPage = async () => {
router.push('/login'); router.push('/login');
return; return;
} }
infoLoading.value = true;
const { data: userData } = await getUserDetail(user.value.userId); const { data: userData } = await getUserDetail(user.value.userId);
userDetail.value = userData; userDetail.value = userData;
@@ -36,9 +38,12 @@ const loadPage = async () => {
const { data: playlistData } = await getUserPlaylist(user.value.userId); const { data: playlistData } = await getUserPlaylist(user.value.userId);
playList.value = playlistData.playlist; playList.value = playlistData.playlist;
getUserRecord(user.value.userId).then(({ data: recordData }) => { // getUserRecord(user.value.userId).then(({ data: recordData }) => {
recordList.value = recordData.allData; // recordList.value = recordData.allData;
}); // });
const { data: recordData } = await getUserRecord(user.value.userId);
recordList.value = recordData.allData;
infoLoading.value = false;
}; };
onActivated(() => { onActivated(() => {
@@ -109,7 +114,7 @@ const handlePlay = () => {
<div class="play-list" :class="setAnimationClass('animate__fadeInLeft')"> <div class="play-list" :class="setAnimationClass('animate__fadeInLeft')">
<div class=" ">创建的歌单</div> <div class=" ">创建的歌单</div>
<n-scrollbar> <n-scrollbar v-loading="infoLoading">
<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)">
<n-image :src="getImgUrl(item.coverImgUrl, '50y50')" class="play-list-item-img" lazy preview-disabled /> <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-info">
@@ -125,7 +130,7 @@ const handlePlay = () => {
<div v-if="!isMobile" class="right" :class="setAnimationClass('animate__fadeInRight')"> <div v-if="!isMobile" class="right" :class="setAnimationClass('animate__fadeInRight')">
<div class="title">听歌排行</div> <div class="title">听歌排行</div>
<div class="record-list"> <div class="record-list">
<n-scrollbar> <n-scrollbar v-loading="infoLoading">
<div <div
v-for="(item, index) in recordList" v-for="(item, index) in recordList"
:key="item.song.id" :key="item.song.id"