diff --git a/src/renderer/components/common/songItemCom/CompactSongItem.vue b/src/renderer/components/common/songItemCom/CompactSongItem.vue
index deef9ba..4b6e6e7 100644
--- a/src/renderer/components/common/songItemCom/CompactSongItem.vue
+++ b/src/renderer/components/common/songItemCom/CompactSongItem.vue
@@ -126,7 +126,6 @@ const artists = computed(() => baseItem.value?.artists || []);
// 包装方法,避免直接访问可能为undefined的ref
const onToggleSelect = () => {
baseItem.value?.toggleSelect();
- emit('select', props.item.id, !props.selected);
};
const onArtistClick = (id: number) => baseItem.value?.handleArtistClick(id);
const onToggleFavorite = (event: Event) => {
diff --git a/src/renderer/components/common/songItemCom/StandardSongItem.vue b/src/renderer/components/common/songItemCom/StandardSongItem.vue
index e75c4cf..d596850 100644
--- a/src/renderer/components/common/songItemCom/StandardSongItem.vue
+++ b/src/renderer/components/common/songItemCom/StandardSongItem.vue
@@ -131,7 +131,6 @@ const artists = computed(() => baseItem.value?.artists || []);
// 包装方法,避免直接访问可能为undefined的ref
const onToggleSelect = () => {
baseItem.value?.toggleSelect();
- emit('select', props.item.id, !props.selected);
};
const onImageLoad = (event: Event) => baseItem.value?.imageLoad(event);
const onArtistClick = (id: number) => baseItem.value?.handleArtistClick(id);
diff --git a/src/renderer/views/music/MusicListPage.vue b/src/renderer/views/music/MusicListPage.vue
index 15a11b2..9fbe969 100644
--- a/src/renderer/views/music/MusicListPage.vue
+++ b/src/renderer/views/music/MusicListPage.vue
@@ -36,6 +36,48 @@
{{ t('comp.musicList.addToPlaylist') }}
+
+
+
+
+
+
+
+
+
+ {{ t('favorite.batchDownload')}}
+
+
+
+ {{ t('common.selectAll') }}
+
+
+
+
+
+
+
+ {{ t('favorite.download', { count: selectedSongs.length }) }}
+
+
+
+
+
+
+
+ {{ t('common.cancel') }}
+
+
+
+
@@ -132,8 +174,11 @@
:compact="isCompactLayout"
:item="formatSong(item)"
:can-remove="canRemove"
+ :selectable="isSelecting"
+ :selected="selectedSongs.includes(item.id as number)"
@play="handlePlay"
@remove-song="handleRemoveSong"
+ @select="(id, selected) => handleSelect(id, selected)"
/>
@@ -166,6 +211,7 @@ import PlayBottom from '@/components/common/PlayBottom.vue';
import { useMusicStore, usePlayerStore } from '@/store';
import { SongResult } from '@/type/music';
import { getImgUrl, isMobile, setAnimationClass } from '@/utils';
+import { useDownload } from '@/hooks/useDownload';
const { t } = useI18n();
const route = useRoute();
@@ -838,6 +884,53 @@ const addToPlaylist = () => {
message.success(t('comp.musicList.addToPlaylistSuccess', { count: newSongs.length }));
};
+
+// 多选下载相关状态和方法
+const isSelecting = ref(false);
+const selectedSongs = ref([]);
+const { isDownloading, batchDownloadMusic } = useDownload();
+
+const startSelect = () => {
+ isSelecting.value = true;
+ selectedSongs.value = [];
+};
+const cancelSelect = () => {
+ isSelecting.value = false;
+ selectedSongs.value = [];
+};
+const handleSelect = (songId: number, selected: boolean) => {
+ if (selected) {
+ selectedSongs.value.push(songId);
+ } else {
+ selectedSongs.value = selectedSongs.value.filter((id) => id !== songId);
+ }
+};
+const isAllSelected = computed(() => {
+ return (
+ filteredSongs.value.length > 0 &&
+ selectedSongs.value.length === filteredSongs.value.length
+ );
+});
+const isIndeterminate = computed(() => {
+ return (
+ selectedSongs.value.length > 0 &&
+ selectedSongs.value.length < filteredSongs.value.length
+ );
+});
+const handleSelectAll = (checked: boolean) => {
+ if (checked) {
+ selectedSongs.value = filteredSongs.value.map((song) => song.id as number);
+ } else {
+ selectedSongs.value = [];
+ }
+};
+const handleBatchDownload = async () => {
+ const selectedSongsList = selectedSongs.value
+ .map((songId) => filteredSongs.value.find((s) => s.id === songId))
+ .filter((song) => song) as SongResult[];
+ await batchDownloadMusic(selectedSongsList);
+ cancelSelect();
+};