From 122110182153718a1a6bad59e3da862d9ea67174 Mon Sep 17 00:00:00 2001 From: alger Date: Wed, 4 Jun 2025 20:19:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=97=E8=A1=A8=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=A4=9A=E9=80=89=E4=B8=8B=E8=BD=BD=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=B9=E9=87=8F=E9=80=89=E6=8B=A9=E5=92=8C?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E9=9F=B3=E4=B9=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/songItemCom/CompactSongItem.vue | 1 - .../common/songItemCom/StandardSongItem.vue | 1 - src/renderer/views/music/MusicListPage.vue | 93 +++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) 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(); +};