mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-05-18 03:17:29 +08:00
✨ feat: 收藏列表添加升序降序排列
This commit is contained in:
@@ -11,5 +11,7 @@ export default {
|
|||||||
downloadSuccess: 'Download completed',
|
downloadSuccess: 'Download completed',
|
||||||
downloadFailed: 'Download failed',
|
downloadFailed: 'Download failed',
|
||||||
downloading: 'Downloading, please wait...',
|
downloading: 'Downloading, please wait...',
|
||||||
selectSongsFirst: 'Please select songs to download first'
|
selectSongsFirst: 'Please select songs to download first',
|
||||||
|
descending: 'Descending',
|
||||||
|
ascending: 'Ascending'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ export default {
|
|||||||
downloadSuccess: '下载完成',
|
downloadSuccess: '下载完成',
|
||||||
downloadFailed: '下载失败',
|
downloadFailed: '下载失败',
|
||||||
downloading: '正在下载中,请稍候...',
|
downloading: '正在下载中,请稍候...',
|
||||||
selectSongsFirst: '请先选择要下载的歌曲'
|
selectSongsFirst: '请先选择要下载的歌曲',
|
||||||
|
descending: '降',
|
||||||
|
ascending: '升'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isComponent ? favoriteSongs.length : true" class="favorite-page">
|
<div v-if="isComponent ? favoriteSongs.length : true" class="favorite-page">
|
||||||
<div class="favorite-header" :class="setAnimationClass('animate__fadeInLeft')">
|
<div class="favorite-header" :class="setAnimationClass('animate__fadeInLeft')">
|
||||||
<div class="favorite-header-left">
|
<div class="favorite-header-left">
|
||||||
@@ -6,6 +6,26 @@
|
|||||||
<div class="favorite-count">{{ t('favorite.count', { count: favoriteList.length }) }}</div>
|
<div class="favorite-count">{{ t('favorite.count', { count: favoriteList.length }) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isComponent && isElectron" class="favorite-header-right">
|
<div v-if="!isComponent && isElectron" class="favorite-header-right">
|
||||||
|
<div class="sort-controls" v-if="!isSelecting">
|
||||||
|
<div class="sort-buttons">
|
||||||
|
<div
|
||||||
|
class="sort-button"
|
||||||
|
:class="{ active: isDescending }"
|
||||||
|
@click="toggleSort(true)"
|
||||||
|
>
|
||||||
|
<i class="iconfont ri-sort-desc"></i>
|
||||||
|
{{ t('favorite.descending') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sort-button"
|
||||||
|
:class="{ active: !isDescending }"
|
||||||
|
@click="toggleSort(false)"
|
||||||
|
>
|
||||||
|
<i class="iconfont ri-sort-asc"></i>
|
||||||
|
{{ t('favorite.ascending') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<n-button
|
<n-button
|
||||||
v-if="!isSelecting"
|
v-if="!isSelecting"
|
||||||
secondary
|
secondary
|
||||||
@@ -80,60 +100,60 @@
|
|||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { useMessage } from 'naive-ui';
|
import { useMessage } from 'naive-ui';
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { getMusicDetail } from '@/api/music';
|
import { getMusicDetail } from '@/api/music';
|
||||||
import { getBilibiliProxyUrl, getBilibiliVideoDetail } from '@/api/bilibili';
|
import { getBilibiliProxyUrl, getBilibiliVideoDetail } from '@/api/bilibili';
|
||||||
import SongItem from '@/components/common/SongItem.vue';
|
import SongItem from '@/components/common/SongItem.vue';
|
||||||
import { getSongUrl } from '@/hooks/MusicListHook';
|
import { getSongUrl } from '@/hooks/MusicListHook';
|
||||||
import { usePlayerStore } from '@/store';
|
import { usePlayerStore } from '@/store';
|
||||||
import type { SongResult } from '@/type/music';
|
import type { SongResult } from '@/type/music';
|
||||||
import { isElectron, setAnimationClass, setAnimationDelay } from '@/utils';
|
import { isElectron, setAnimationClass, setAnimationDelay } from '@/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const favoriteList = computed(() => playerStore.favoriteList);
|
const favoriteList = computed(() => playerStore.favoriteList);
|
||||||
const favoriteSongs = ref<SongResult[]>([]);
|
const favoriteSongs = ref<SongResult[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const noMore = ref(false);
|
const noMore = ref(false);
|
||||||
const scrollbarRef = ref();
|
const scrollbarRef = ref();
|
||||||
|
|
||||||
// 多选相关
|
// 多选相关
|
||||||
const isSelecting = ref(false);
|
const isSelecting = ref(false);
|
||||||
const selectedSongs = ref<number[]>([]);
|
const selectedSongs = ref<number[]>([]);
|
||||||
const isDownloading = ref(false);
|
const isDownloading = ref(false);
|
||||||
|
|
||||||
// 开始多选
|
// 开始多选
|
||||||
const startSelect = () => {
|
const startSelect = () => {
|
||||||
isSelecting.value = true;
|
isSelecting.value = true;
|
||||||
selectedSongs.value = [];
|
selectedSongs.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取消多选
|
// 取消多选
|
||||||
const cancelSelect = () => {
|
const cancelSelect = () => {
|
||||||
isSelecting.value = false;
|
isSelecting.value = false;
|
||||||
selectedSongs.value = [];
|
selectedSongs.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理选择
|
// 处理选择
|
||||||
const handleSelect = (songId: number, selected: boolean) => {
|
const handleSelect = (songId: number, selected: boolean) => {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
selectedSongs.value.push(songId);
|
selectedSongs.value.push(songId);
|
||||||
} else {
|
} else {
|
||||||
selectedSongs.value = selectedSongs.value.filter((id) => id !== songId);
|
selectedSongs.value = selectedSongs.value.filter((id) => id !== songId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 批量下载
|
// 批量下载
|
||||||
const handleBatchDownload = async () => {
|
const handleBatchDownload = async () => {
|
||||||
if (isDownloading.value) {
|
if (isDownloading.value) {
|
||||||
message.warning(t('favorite.downloading'));
|
message.warning(t('favorite.downloading'));
|
||||||
return;
|
return;
|
||||||
@@ -215,29 +235,49 @@ const handleBatchDownload = async () => {
|
|||||||
message.destroyAll();
|
message.destroyAll();
|
||||||
message.error(t('favorite.downloadFailed'));
|
message.error(t('favorite.downloadFailed'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 无限滚动相关
|
// 排序相关
|
||||||
const pageSize = 100;
|
const isDescending = ref(true); // 默认倒序显示
|
||||||
const currentPage = ref(1);
|
|
||||||
|
|
||||||
const props = defineProps({
|
// 切换排序方式
|
||||||
|
const toggleSort = (descending: boolean) => {
|
||||||
|
if (isDescending.value === descending) return;
|
||||||
|
isDescending.value = descending;
|
||||||
|
currentPage.value = 1;
|
||||||
|
favoriteSongs.value = [];
|
||||||
|
noMore.value = false;
|
||||||
|
getFavoriteSongs();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 无限滚动相关
|
||||||
|
const pageSize = 100;
|
||||||
|
const currentPage = ref(1);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
isComponent: {
|
isComponent: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取当前页的收藏歌曲ID
|
||||||
|
const getCurrentPageIds = () => {
|
||||||
|
let ids = [...favoriteList.value]; // 复制一份以免修改原数组
|
||||||
|
|
||||||
|
// 根据排序方式调整顺序
|
||||||
|
if (isDescending.value) {
|
||||||
|
ids = ids.reverse(); // 倒序,最新收藏的在前面
|
||||||
|
}
|
||||||
|
|
||||||
// 获取当前页的收藏歌曲ID
|
|
||||||
const getCurrentPageIds = () => {
|
|
||||||
const startIndex = (currentPage.value - 1) * pageSize;
|
const startIndex = (currentPage.value - 1) * pageSize;
|
||||||
const endIndex = startIndex + pageSize;
|
const endIndex = startIndex + pageSize;
|
||||||
// 返回原始ID,不进行类型转换
|
// 返回原始ID,不进行类型转换
|
||||||
return favoriteList.value.slice(startIndex, endIndex);
|
return ids.slice(startIndex, endIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取收藏歌曲详情
|
// 获取收藏歌曲详情
|
||||||
const getFavoriteSongs = async () => {
|
const getFavoriteSongs = async () => {
|
||||||
if (favoriteList.value.length === 0) {
|
if (favoriteList.value.length === 0) {
|
||||||
favoriteSongs.value = [];
|
favoriteSongs.value = [];
|
||||||
return;
|
return;
|
||||||
@@ -366,10 +406,10 @@ const getFavoriteSongs = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理滚动事件
|
// 处理滚动事件
|
||||||
const handleScroll = (e: any) => {
|
const handleScroll = (e: any) => {
|
||||||
const { scrollTop, scrollHeight, offsetHeight } = e.target;
|
const { scrollTop, scrollHeight, offsetHeight } = e.target;
|
||||||
const threshold = 100; // 距离底部多少像素时加载更多
|
const threshold = 100; // 距离底部多少像素时加载更多
|
||||||
|
|
||||||
@@ -377,15 +417,15 @@ const handleScroll = (e: any) => {
|
|||||||
currentPage.value++;
|
currentPage.value++;
|
||||||
getFavoriteSongs();
|
getFavoriteSongs();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await playerStore.initializeFavoriteList();
|
await playerStore.initializeFavoriteList();
|
||||||
await getFavoriteSongs();
|
await getFavoriteSongs();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听收藏列表变化
|
// 监听收藏列表变化
|
||||||
watch(
|
watch(
|
||||||
favoriteList,
|
favoriteList,
|
||||||
() => {
|
() => {
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
@@ -393,44 +433,44 @@ watch(
|
|||||||
getFavoriteSongs();
|
getFavoriteSongs();
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePlay = () => {
|
const handlePlay = () => {
|
||||||
playerStore.setPlayList(favoriteSongs.value);
|
playerStore.setPlayList(favoriteSongs.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItemAnimationDelay = (index: number) => {
|
const getItemAnimationDelay = (index: number) => {
|
||||||
return setAnimationDelay(index, 30);
|
return setAnimationDelay(index, 30);
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const handleMore = () => {
|
const handleMore = () => {
|
||||||
router.push('/history');
|
router.push('/history');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 全选相关
|
// 全选相关
|
||||||
const isAllSelected = computed(() => {
|
const isAllSelected = computed(() => {
|
||||||
return (
|
return (
|
||||||
favoriteSongs.value.length > 0 && selectedSongs.value.length === favoriteSongs.value.length
|
favoriteSongs.value.length > 0 && selectedSongs.value.length === favoriteSongs.value.length
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const isIndeterminate = computed(() => {
|
const isIndeterminate = computed(() => {
|
||||||
return selectedSongs.value.length > 0 && selectedSongs.value.length < favoriteSongs.value.length;
|
return selectedSongs.value.length > 0 && selectedSongs.value.length < favoriteSongs.value.length;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理全选/取消全选
|
// 处理全选/取消全选
|
||||||
const handleSelectAll = (checked: boolean) => {
|
const handleSelectAll = (checked: boolean) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
selectedSongs.value = favoriteSongs.value.map((song) => song.id as number);
|
selectedSongs.value = favoriteSongs.value.map((song) => song.id as number);
|
||||||
} else {
|
} else {
|
||||||
selectedSongs.value = [];
|
selectedSongs.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.favorite-page {
|
.favorite-page {
|
||||||
@apply h-full flex flex-col pt-2;
|
@apply h-full flex flex-col pt-2;
|
||||||
@apply bg-light dark:bg-black;
|
@apply bg-light dark:bg-black;
|
||||||
|
|
||||||
@@ -451,8 +491,40 @@ const handleSelectAll = (checked: boolean) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-right {
|
&-right {
|
||||||
|
@apply flex items-center gap-3;
|
||||||
|
|
||||||
|
.sort-controls {
|
||||||
@apply flex items-center;
|
@apply flex items-center;
|
||||||
|
|
||||||
|
.sort-buttons {
|
||||||
|
@apply flex items-center bg-gray-100 dark:bg-gray-800 rounded-full overflow-hidden;
|
||||||
|
@apply border border-gray-200 dark:border-gray-700;
|
||||||
|
|
||||||
|
.sort-button {
|
||||||
|
@apply flex items-center py-1 px-3 text-sm cursor-pointer;
|
||||||
|
@apply text-gray-600 dark:text-gray-400;
|
||||||
|
@apply transition-all duration-300;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
@apply border-r border-gray-200 dark:border-gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
@apply mr-1 text-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
@apply bg-green-400 text-primary dark:text-gray-200;
|
||||||
|
@apply font-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.active) {
|
||||||
|
@apply bg-gray-200 dark:bg-gray-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.select-btn {
|
.select-btn {
|
||||||
@apply rounded-full px-4 h-8;
|
@apply rounded-full px-4 h-8;
|
||||||
@apply transition-all duration-300 ease-in-out;
|
@apply transition-all duration-300 ease-in-out;
|
||||||
@@ -524,18 +596,18 @@ const handleSelectAll = (checked: boolean) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-wrapper {
|
.loading-wrapper {
|
||||||
@apply flex justify-center items-center py-20;
|
@apply flex justify-center items-center py-20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-more-tip {
|
.no-more-tip {
|
||||||
@apply text-center py-4 text-sm;
|
@apply text-center py-4 text-sm;
|
||||||
@apply text-gray-500 dark:text-gray-400;
|
@apply text-gray-500 dark:text-gray-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
.favorite-page {
|
.favorite-page {
|
||||||
@apply p-4;
|
@apply p-4;
|
||||||
|
|
||||||
@@ -547,5 +619,5 @@ const handleSelectAll = (checked: boolean) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user