From ad7b504eef445a0f2b2db06ef1368393030127bc Mon Sep 17 00:00:00 2001 From: alger Date: Fri, 23 May 2025 19:39:46 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=AD=8C=E6=9B=B2=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E7=BB=84=E4=BB=B6=E5=92=8C=E5=A4=9A=E7=A7=8D?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=EF=BC=8C=E4=BC=98=E5=8C=96=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=8A=BD=E5=B1=89=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/PlaylistDrawer.vue | 1 + src/renderer/components/common/SongItem.vue | 809 +----------------- .../common/songItemCom/BaseSongItem.vue | 118 +++ .../common/songItemCom/CompactSongItem.vue | 244 ++++++ .../common/songItemCom/ListSongItem.vue | 181 ++++ .../common/songItemCom/MiniSongItem.vue | 184 ++++ .../common/songItemCom/SongItemDropdown.vue | 251 ++++++ .../common/songItemCom/StandardSongItem.vue | 204 +++++ ...ayListDrawer.vue => PlayingListDrawer.vue} | 0 src/renderer/hooks/useSongItem.ts | 188 ++++ src/renderer/layout/AppLayout.vue | 28 +- 11 files changed, 1410 insertions(+), 798 deletions(-) create mode 100644 src/renderer/components/common/songItemCom/BaseSongItem.vue create mode 100644 src/renderer/components/common/songItemCom/CompactSongItem.vue create mode 100644 src/renderer/components/common/songItemCom/ListSongItem.vue create mode 100644 src/renderer/components/common/songItemCom/MiniSongItem.vue create mode 100644 src/renderer/components/common/songItemCom/SongItemDropdown.vue create mode 100644 src/renderer/components/common/songItemCom/StandardSongItem.vue rename src/renderer/components/player/{PlayListDrawer.vue => PlayingListDrawer.vue} (100%) create mode 100644 src/renderer/hooks/useSongItem.ts diff --git a/src/renderer/components/common/PlaylistDrawer.vue b/src/renderer/components/common/PlaylistDrawer.vue index a33b03d..e83193c 100644 --- a/src/renderer/components/common/PlaylistDrawer.vue +++ b/src/renderer/components/common/PlaylistDrawer.vue @@ -4,6 +4,7 @@ :width="400" placement="right" @update:show="$emit('update:modelValue', $event)" + :unstable-show-mask="false" > diff --git a/src/renderer/components/common/SongItem.vue b/src/renderer/components/common/SongItem.vue index 4e42760..1fa8a9a 100644 --- a/src/renderer/components/common/SongItem.vue +++ b/src/renderer/components/common/SongItem.vue @@ -1,150 +1,27 @@ - - diff --git a/src/renderer/components/common/songItemCom/BaseSongItem.vue b/src/renderer/components/common/songItemCom/BaseSongItem.vue new file mode 100644 index 0000000..2441372 --- /dev/null +++ b/src/renderer/components/common/songItemCom/BaseSongItem.vue @@ -0,0 +1,118 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/components/common/songItemCom/CompactSongItem.vue b/src/renderer/components/common/songItemCom/CompactSongItem.vue new file mode 100644 index 0000000..552f574 --- /dev/null +++ b/src/renderer/components/common/songItemCom/CompactSongItem.vue @@ -0,0 +1,244 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/components/common/songItemCom/ListSongItem.vue b/src/renderer/components/common/songItemCom/ListSongItem.vue new file mode 100644 index 0000000..e65c39f --- /dev/null +++ b/src/renderer/components/common/songItemCom/ListSongItem.vue @@ -0,0 +1,181 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/components/common/songItemCom/MiniSongItem.vue b/src/renderer/components/common/songItemCom/MiniSongItem.vue new file mode 100644 index 0000000..14b491b --- /dev/null +++ b/src/renderer/components/common/songItemCom/MiniSongItem.vue @@ -0,0 +1,184 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/components/common/songItemCom/SongItemDropdown.vue b/src/renderer/components/common/songItemCom/SongItemDropdown.vue new file mode 100644 index 0000000..3977d83 --- /dev/null +++ b/src/renderer/components/common/songItemCom/SongItemDropdown.vue @@ -0,0 +1,251 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/components/common/songItemCom/StandardSongItem.vue b/src/renderer/components/common/songItemCom/StandardSongItem.vue new file mode 100644 index 0000000..c87442b --- /dev/null +++ b/src/renderer/components/common/songItemCom/StandardSongItem.vue @@ -0,0 +1,204 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/components/player/PlayListDrawer.vue b/src/renderer/components/player/PlayingListDrawer.vue similarity index 100% rename from src/renderer/components/player/PlayListDrawer.vue rename to src/renderer/components/player/PlayingListDrawer.vue diff --git a/src/renderer/hooks/useSongItem.ts b/src/renderer/hooks/useSongItem.ts new file mode 100644 index 0000000..aba5998 --- /dev/null +++ b/src/renderer/hooks/useSongItem.ts @@ -0,0 +1,188 @@ +import { usePlayerStore } from '@/store'; +import type { SongResult } from '@/type/music'; +import { computed, ref } from 'vue'; +import { getImgUrl } from '@/utils'; +import { getImageBackground } from '@/utils/linearColor'; +import { useMessage, useDialog } from 'naive-ui'; +import { useI18n } from 'vue-i18n'; +import { useDownload } from './useDownload'; +import { useArtist } from './useArtist'; + +export function useSongItem(props: { + item: SongResult; + canRemove?: boolean; +}) { + const { t } = useI18n(); + const playerStore = usePlayerStore(); + const message = useMessage(); + const dialog = useDialog(); + const { downloadMusic } = useDownload(); + const { navigateToArtist } = useArtist(); + + // 状态变量 + const showDropdown = ref(false); + const dropdownX = ref(0); + const dropdownY = ref(0); + const isHovering = ref(false); + + // 计算属性 + const play = computed(() => playerStore.isPlay); + const playMusic = computed(() => playerStore.playMusic); + const playLoading = computed( + () => playMusic.value.id === props.item.id && playMusic.value.playLoading + ); + const isPlaying = computed(() => playMusic.value.id === props.item.id); + + // 收藏与不喜欢状态 + const isFavorite = computed(() => { + const numericId = typeof props.item.id === 'string' ? parseInt(props.item.id, 10) : props.item.id; + return playerStore.favoriteList.includes(numericId); + }); + + const isDislike = computed(() => { + const numericId = typeof props.item.id === 'string' ? parseInt(props.item.id, 10) : props.item.id; + return playerStore.dislikeList.includes(numericId); + }); + + // 获取艺术家列表 + const artists = computed(() => { + return (props.item.ar || props.item.song?.artists)?.slice(0, 4) || []; + }); + + // 处理图片加载 + const handleImageLoad = async (imageElement: HTMLImageElement) => { + if (!imageElement) return; + + const { backgroundColor } = await getImageBackground(imageElement); + // eslint-disable-next-line vue/no-mutating-props + props.item.backgroundColor = backgroundColor; + }; + + // 播放音乐 + const playMusicEvent = async (item: SongResult) => { + try { + const result = await playerStore.setPlay(item); + if (!result) { + throw new Error('播放失败'); + } + return true; + } catch (error) { + console.error('播放出错:', error); + return false; + } + }; + + // 切换收藏状态 + const toggleFavorite = async (e: Event) => { + e.stopPropagation(); + const numericId = typeof props.item.id === 'string' ? parseInt(props.item.id, 10) : props.item.id; + + if (isFavorite.value) { + playerStore.removeFromFavorite(numericId); + } else { + playerStore.addToFavorite(numericId); + } + }; + + // 切换不喜欢状态 + const toggleDislike = async (e: Event) => { + e.stopPropagation(); + if (isDislike.value) { + playerStore.removeFromDislikeList(props.item.id); + return; + } + + dialog.warning({ + title: t('songItem.dialog.dislike.title'), + content: t('songItem.dialog.dislike.content'), + positiveText: t('songItem.dialog.dislike.positiveText'), + negativeText: t('songItem.dialog.dislike.negativeText'), + onPositiveClick: () => { + playerStore.addToDislikeList(props.item.id); + } + }); + }; + + // 添加到下一首播放 + const handlePlayNext = () => { + playerStore.addToNextPlay(props.item); + message.success(t('songItem.message.addedToNextPlay')); + }; + + // 获取歌曲时长 + const getDuration = (item: SongResult): number => { + if (item.duration) return item.duration; + if (typeof item.dt === 'number') return item.dt; + return 0; + }; + + // 格式化时长 + const formatDuration = (ms: number): string => { + if (!ms) return '--:--'; + const totalSeconds = Math.floor(ms / 1000); + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; + }; + + // 处理右键菜单 + const handleContextMenu = (e: MouseEvent) => { + e.preventDefault(); + showDropdown.value = true; + dropdownX.value = e.clientX; + dropdownY.value = e.clientY; + }; + + // 处理菜单点击 + const handleMenuClick = (e: MouseEvent) => { + e.preventDefault(); + showDropdown.value = true; + dropdownX.value = e.clientX; + dropdownY.value = e.clientY; + }; + + // 处理艺术家点击 + const handleArtistClick = (id: number) => { + navigateToArtist(id); + }; + + // 鼠标悬停处理 + const handleMouseEnter = () => { + isHovering.value = true; + }; + + const handleMouseLeave = () => { + isHovering.value = false; + }; + + return { + t, + play, + playMusic, + playLoading, + isPlaying, + isFavorite, + isDislike, + artists, + showDropdown, + dropdownX, + dropdownY, + isHovering, + playerStore, + message, + getImgUrl, + handleImageLoad, + playMusicEvent, + toggleFavorite, + toggleDislike, + handlePlayNext, + getDuration, + formatDuration, + handleContextMenu, + handleMenuClick, + handleArtistClick, + handleMouseEnter, + handleMouseLeave, + downloadMusic + }; +} \ No newline at end of file diff --git a/src/renderer/layout/AppLayout.vue b/src/renderer/layout/AppLayout.vue index dbdb175..150084c 100644 --- a/src/renderer/layout/AppLayout.vue +++ b/src/renderer/layout/AppLayout.vue @@ -37,22 +37,22 @@ :style="isMobile && playerStore.musicFull ? 'bottom: 0;' : ''" /> - - - - + + + + @@ -92,7 +92,7 @@ const PlayBar = defineAsyncComponent(() => import('@/components/player/PlayBar.v const MobilePlayBar = defineAsyncComponent(() => import('@/components/player/MobilePlayBar.vue')); const SearchBar = defineAsyncComponent(() => import('./components/SearchBar.vue')); const TitleBar = defineAsyncComponent(() => import('./components/TitleBar.vue')); -const PlayListDrawer = defineAsyncComponent(() => import('@/components/player/PlayListDrawer.vue')); +const PlayingListDrawer = defineAsyncComponent(() => import('@/components/player/PlayingListDrawer.vue')); const PlaylistDrawer = defineAsyncComponent(() => import('@/components/common/PlaylistDrawer.vue')); const playerStore = usePlayerStore(); @@ -112,9 +112,9 @@ const showPlaylistDrawer = ref(false); const currentSongId = ref(); // 提供一个方法来打开歌单抽屉 -const openPlaylistDrawer = (songId: number) => { +const openPlaylistDrawer = (songId: number, isOpen: boolean = true) => { currentSongId.value = songId; - showPlaylistDrawer.value = true; + showPlaylistDrawer.value = isOpen; }; // 将方法提供给全局