From 86b286453a49c03435d3387d2450c620e85529c0 Mon Sep 17 00:00:00 2001 From: ikmkj Date: Sat, 21 Mar 2026 23:49:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(data):=20=E7=BB=9F=E4=B8=80=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=BB=93=E6=9E=84=E5=A4=84=E7=90=86=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=9F=B3=E4=B9=90=E6=92=AD=E6=94=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 extractList 函数统一处理不同数据结构(list、records、items) - 添加 extractPageData 函数标准化分页数据处理逻辑 - 修复音乐播放器移除播放列表项时的索引问题 - 优化播放列表清空时的音频暂停和状态重置 - 调整收藏按钮图标显示逻辑(实心星和空心星对调) - 更新前端API调用路径 /user/profile 为 /user/current - 修复后端收藏歌单查询的字段映射和排序问题 - 在主布局中引入 ArrowDown 图标用于下拉功能 - 改进用户中心头像上传的认证头构建方式 - 优化用户统计数据显示的数据提取逻辑 --- .../entity/UserCollectPlaylist.java | 2 +- .../UserCollectPlaylistRepository.java | 19 ++++++------ .../service/impl/PlaylistServiceImpl.java | 10 +++--- music-qianduan/src/api/user.js | 2 +- music-qianduan/src/components/MusicPlayer.vue | 6 +++- music-qianduan/src/layouts/MainLayout.vue | 2 +- music-qianduan/src/stores/player.js | 11 ++++++- music-qianduan/src/views/Home.vue | 16 +++++++--- music-qianduan/src/views/Music.vue | 26 +++++++++++++--- music-qianduan/src/views/MusicDetail.vue | 2 +- music-qianduan/src/views/Playlist.vue | 23 ++++++++++++-- music-qianduan/src/views/Search.vue | 31 +++++++++++++++---- music-qianduan/src/views/Singer.vue | 23 ++++++++++++-- music-qianduan/src/views/UserCenter.vue | 26 +++++++++++++--- 14 files changed, 152 insertions(+), 47 deletions(-) diff --git a/music-houduan/src/main/java/com/test/musichouduan/entity/UserCollectPlaylist.java b/music-houduan/src/main/java/com/test/musichouduan/entity/UserCollectPlaylist.java index be2f520..f178b63 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/entity/UserCollectPlaylist.java +++ b/music-houduan/src/main/java/com/test/musichouduan/entity/UserCollectPlaylist.java @@ -36,6 +36,6 @@ public class UserCollectPlaylist { * 收藏时间 */ @CreationTimestamp - @Column(name = "collect_time", nullable = false, updatable = false) + @Column(name = "create_time", nullable = false, updatable = false) private LocalDateTime createTime; } diff --git a/music-houduan/src/main/java/com/test/musichouduan/repository/UserCollectPlaylistRepository.java b/music-houduan/src/main/java/com/test/musichouduan/repository/UserCollectPlaylistRepository.java index 5b660f2..b577c48 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/repository/UserCollectPlaylistRepository.java +++ b/music-houduan/src/main/java/com/test/musichouduan/repository/UserCollectPlaylistRepository.java @@ -3,19 +3,15 @@ package com.test.musichouduan.repository; import com.test.musichouduan.entity.Playlist; import com.test.musichouduan.entity.User; import com.test.musichouduan.entity.UserCollectPlaylist; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.transaction.annotation.Transactional; import java.util.Optional; /** @@ -89,7 +85,7 @@ public interface UserCollectPlaylistRepository extends JpaRepository findCollectedPlaylist(Long userId, Pageable pageable); Optional findByUserIdAndPlaylistId(Long userId, Long playlistId); diff --git a/music-houduan/src/main/java/com/test/musichouduan/service/impl/PlaylistServiceImpl.java b/music-houduan/src/main/java/com/test/musichouduan/service/impl/PlaylistServiceImpl.java index 5a19fb3..fedac2d 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/service/impl/PlaylistServiceImpl.java +++ b/music-houduan/src/main/java/com/test/musichouduan/service/impl/PlaylistServiceImpl.java @@ -483,11 +483,11 @@ public class PlaylistServiceImpl implements PlaylistService { // 获取当前登录用户 Long userId = StpUtil.getLoginIdAsLong(); - // 获取用户收藏的歌单 - Page playlistPage = userCollectPlaylistRepository.findCollectedPlaylist( - userId, - PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime")) - ); + // 获取用户收藏的歌单 + Page playlistPage = userCollectPlaylistRepository.findCollectedPlaylist( + userId, + PageRequest.of(page - 1, size) + ); // 转换为DTO List playlistDTOs = playlistPage.getContent().stream() diff --git a/music-qianduan/src/api/user.js b/music-qianduan/src/api/user.js index bbde12d..f5de8a4 100644 --- a/music-qianduan/src/api/user.js +++ b/music-qianduan/src/api/user.js @@ -39,7 +39,7 @@ export function getCurrentUser() { * @returns {Promise} 请求结果 */ export function getUserInfo() { - return api.get('/user/profile') + return api.get('/user/current') } /** diff --git a/music-qianduan/src/components/MusicPlayer.vue b/music-qianduan/src/components/MusicPlayer.vue index d2bbc6b..a1b5896 100644 --- a/music-qianduan/src/components/MusicPlayer.vue +++ b/music-qianduan/src/components/MusicPlayer.vue @@ -508,7 +508,11 @@ const toggleLike = async () => { // 播放列表相关 const togglePlaylist = () => playerStore.setShowPlaylist(!showPlaylist.value) const playFromPlaylist = (index) => playerStore.playFromPlaylist(index) -const removeFromPlaylist = (index) => playerStore.removeFromPlaylist(index) +const removeFromPlaylist = (index) => { + const music = playlist.value[index] + if (!music) return + playerStore.removeFromPlaylist(music.id) +} const clearPlaylist = () => playerStore.clearPlaylist() // 展开/收起播放器 diff --git a/music-qianduan/src/layouts/MainLayout.vue b/music-qianduan/src/layouts/MainLayout.vue index 3bf8d22..d31a2bc 100644 --- a/music-qianduan/src/layouts/MainLayout.vue +++ b/music-qianduan/src/layouts/MainLayout.vue @@ -3,7 +3,7 @@ import { ref, computed, watch } from 'vue' import { useRouter, useRoute } from 'vue-router' import { useUserStore } from '../stores/user' import { usePlayerStore } from '../stores/player' -import { Search } from '@element-plus/icons-vue' +import { Search, ArrowDown } from '@element-plus/icons-vue' import MusicPlayer from '../components/MusicPlayer.vue' const router = useRouter() diff --git a/music-qianduan/src/stores/player.js b/music-qianduan/src/stores/player.js index 6101771..07ad8c0 100644 --- a/music-qianduan/src/stores/player.js +++ b/music-qianduan/src/stores/player.js @@ -173,8 +173,17 @@ export const usePlayerStore = defineStore('player', { // 清空播放列表 clearPlaylist() { + if (this.audio) { + this.audio.pause() + this.audio.src = '' + } + this.playlist = [] - this.setCurrentMusic(null) + this.currentMusic = null + this.isPlaying = false + this.currentTime = 0 + this.duration = 0 + this.currentIndex = -1 }, // 播放上一首 diff --git a/music-qianduan/src/views/Home.vue b/music-qianduan/src/views/Home.vue index c896d65..e61f6eb 100644 --- a/music-qianduan/src/views/Home.vue +++ b/music-qianduan/src/views/Home.vue @@ -13,6 +13,14 @@ import { processImageUrl } from '../utils/imageUtils' const router = useRouter() const playerStore = usePlayerStore() +const extractList = (data) => { + if (Array.isArray(data)) return data + if (Array.isArray(data?.list)) return data.list + if (Array.isArray(data?.records)) return data.records + if (Array.isArray(data?.items)) return data.items + return [] +} + // 新歌推荐 const newSongs = ref([]) const loadingNewSongs = ref(false) @@ -35,7 +43,7 @@ const fetchLatestMusic = async () => { try { const res = await getLatestMusic(5) // 获取5首最新音乐 if (res.code === 200) { - newSongs.value = res.data.map(item => ({ + newSongs.value = extractList(res.data).map(item => ({ ...item, cover: processMusicUrl(item.cover), url: processMusicUrl(item.url) @@ -59,7 +67,7 @@ const fetchHotMusic = async () => { // 注意:这里调用的是新的 hot-music API const res = await getHotMusic(5) // 获取5首热门音乐 if (res.code === 200) { - hotSongs.value = res.data.map(item => ({ + hotSongs.value = extractList(res.data).map(item => ({ ...item, cover: processMusicUrl(item.cover), url: processMusicUrl(item.url) @@ -81,7 +89,7 @@ const fetchLatestPlaylist = async () => { try { const res = await getLatestPlaylist(5) // 获取5个最新歌单 if (res.code === 200) { - newPlaylists.value = res.data + newPlaylists.value = extractList(res.data) console.log('最新歌单数据:', newPlaylists.value) } else { ElMessage.error(res.message || '获取最新歌单失败') @@ -100,7 +108,7 @@ const fetchLatestSinger = async () => { try { const res = await getLatestSinger(5) // 获取5个最新歌手 if (res.code === 200) { - newSingers.value = res.data.map(item => ({ + newSingers.value = extractList(res.data).map(item => ({ ...item, avatar: processImageUrl(item.avatar) })) diff --git a/music-qianduan/src/views/Music.vue b/music-qianduan/src/views/Music.vue index 5f00aa5..ad6e645 100644 --- a/music-qianduan/src/views/Music.vue +++ b/music-qianduan/src/views/Music.vue @@ -11,6 +11,22 @@ import { useUserStore } from '../stores/user' const router = useRouter() const userStore = useUserStore() +const extractList = (data) => { + if (Array.isArray(data)) return data + if (Array.isArray(data?.list)) return data.list + if (Array.isArray(data?.records)) return data.records + if (Array.isArray(data?.items)) return data.items + return [] +} + +const extractPageData = (data) => { + const list = extractList(data) + return { + list, + total: Number.isFinite(Number(data?.total)) ? Number(data.total) : list.length + } +} + // 音乐列表数据 const musicList = ref([]) const loading = ref(false) @@ -119,9 +135,9 @@ const fetchMusicList = async (params = {}) => { const res = await getMusicList(page, size, keyword.value, selectedType.value) if (res.code === 200) { - // 兼容不同的数据结构,后端可能返回 list 或 records 字段 - musicList.value = res.data.list || res.data.records || [] - total.value = res.data.total || 0 + const pageData = extractPageData(res.data) + musicList.value = pageData.list + total.value = pageData.total // 确保音乐URL和封面URL正确处理 musicList.value.forEach(music => { @@ -261,7 +277,7 @@ const fetchHotMusic = async (params = {}) => { // 从缓存中获取全部50条热门音乐 const res = await getHotMusic(50) if (res.code === 200) { - hotMusicSource.value = res.data || [] + hotMusicSource.value = extractList(res.data) // 处理URL hotMusicSource.value.forEach(music => { @@ -291,7 +307,7 @@ const fetchLatestMusic = async () => { try { const res = await getLatestMusic(20) // 获取20首最新音乐 if (res.code === 200) { - latestMusicSource.value = res.data || [] + latestMusicSource.value = extractList(res.data) // 处理音乐URL和封面URL latestMusicSource.value.forEach(music => { diff --git a/music-qianduan/src/views/MusicDetail.vue b/music-qianduan/src/views/MusicDetail.vue index 57fec97..6bd47af 100644 --- a/music-qianduan/src/views/MusicDetail.vue +++ b/music-qianduan/src/views/MusicDetail.vue @@ -69,7 +69,7 @@ {{ music.collected ? `已收藏 (${music.collectCount ?? 0})` : `收藏 (${music.collectCount ?? 0})` }} diff --git a/music-qianduan/src/views/Playlist.vue b/music-qianduan/src/views/Playlist.vue index f157a28..924b789 100644 --- a/music-qianduan/src/views/Playlist.vue +++ b/music-qianduan/src/views/Playlist.vue @@ -11,6 +11,22 @@ import { getPlaylistTypeList } from '../api/playlistType' const router = useRouter() const userStore = useUserStore() +const extractList = (data) => { + if (Array.isArray(data)) return data + if (Array.isArray(data?.list)) return data.list + if (Array.isArray(data?.records)) return data.records + if (Array.isArray(data?.items)) return data.items + return [] +} + +const extractPageData = (data) => { + const list = extractList(data) + return { + list, + total: Number.isFinite(Number(data?.total)) ? Number(data.total) : list.length + } +} + // 歌单类型列表 const playlistTypes = ref([ { id: null, name: '全部' } @@ -89,8 +105,9 @@ const fetchPlaylists = async () => { ) if (res.code === 200) { - playlists.value = res.data.list || res.data.records || [] - total.value = res.data.total || 0 + const pageData = extractPageData(res.data) + playlists.value = pageData.list + total.value = pageData.total updatePlaylistStatuses(playlists.value) } else { ElMessage.error(res.message || '获取歌单列表失败') @@ -109,7 +126,7 @@ const fetchHotPlaylists = async () => { try { const res = await getHotPlaylist(8) // 获取8个热门歌单 if (res.code === 200) { - hotPlaylists.value = res.data || [] + hotPlaylists.value = extractList(res.data) updatePlaylistStatuses(hotPlaylists.value) } else { ElMessage.error(res.message || '获取热门歌单失败') diff --git a/music-qianduan/src/views/Search.vue b/music-qianduan/src/views/Search.vue index f2b606b..d23f9b8 100644 --- a/music-qianduan/src/views/Search.vue +++ b/music-qianduan/src/views/Search.vue @@ -165,6 +165,22 @@ const route = useRoute() const router = useRouter() const userStore = useUserStore() +const extractList = (data) => { + if (Array.isArray(data)) return data + if (Array.isArray(data?.list)) return data.list + if (Array.isArray(data?.records)) return data.records + if (Array.isArray(data?.items)) return data.items + return [] +} + +const extractPageData = (data) => { + const list = extractList(data) + return { + list, + total: Number.isFinite(Number(data?.total)) ? Number(data.total) : list.length + } +} + // 搜索关键词 const keyword = computed(() => route.query.keyword || '') const searchKeyword = ref(keyword.value) @@ -214,8 +230,9 @@ const fetchMusicList = async () => { try { const res = await searchMusic(keyword.value, musicPage.value, musicPageSize.value) if (res.code === 200) { - musicList.value = res.data.list || res.data.records || [] - musicTotal.value = res.data.total || 0 + const pageData = extractPageData(res.data) + musicList.value = pageData.list + musicTotal.value = pageData.total // 处理音乐URL和封面URL musicList.value = musicList.value.map(item => ({ @@ -245,8 +262,9 @@ const fetchPlaylistList = async () => { try { const res = await searchPlaylist(keyword.value, playlistPage.value, playlistPageSize.value) if (res.code === 200) { - playlistList.value = res.data.list || res.data.records || [] - playlistTotal.value = res.data.total || 0 + const pageData = extractPageData(res.data) + playlistList.value = pageData.list + playlistTotal.value = pageData.total // 处理封面URL playlistList.value = playlistList.value.map(item => ({ @@ -272,8 +290,9 @@ const fetchSingerList = async () => { try { const res = await searchSinger(keyword.value, singerPage.value, singerPageSize.value) if (res.code === 200) { - singerList.value = res.data.list || res.data.records || [] - singerTotal.value = res.data.total || 0 + const pageData = extractPageData(res.data) + singerList.value = pageData.list + singerTotal.value = pageData.total // 处理头像URL singerList.value = singerList.value.map(item => ({ diff --git a/music-qianduan/src/views/Singer.vue b/music-qianduan/src/views/Singer.vue index f89355d..d665ffd 100644 --- a/music-qianduan/src/views/Singer.vue +++ b/music-qianduan/src/views/Singer.vue @@ -12,6 +12,22 @@ import { processImageUrl } from '../utils/imageUtils' const router = useRouter() const userStore = useUserStore() +const extractList = (data) => { + if (Array.isArray(data)) return data + if (Array.isArray(data?.list)) return data.list + if (Array.isArray(data?.records)) return data.records + if (Array.isArray(data?.items)) return data.items + return [] +} + +const extractPageData = (data) => { + const list = extractList(data) + return { + list, + total: Number.isFinite(Number(data?.total)) ? Number(data.total) : list.length + } +} + // 歌手类型列表 const singerTypes = ref([{ id: null, name: '全部' }]) @@ -75,8 +91,9 @@ const fetchSingers = async () => { ) if (res.code === 200) { - singers.value = res.data.list || res.data.records || [] - total.value = res.data.total || 0 + const pageData = extractPageData(res.data) + singers.value = pageData.list + total.value = pageData.total // 处理图片URL singers.value = singers.value.map(singer => ({ @@ -101,7 +118,7 @@ const fetchHotSingers = async () => { try { const res = await getHotSinger(8) // 获取8个热门歌手 if (res.code === 200) { - hotSingers.value = res.data || [] + hotSingers.value = extractList(res.data) // 处理图片URL hotSingers.value = hotSingers.value.map(singer => ({ diff --git a/music-qianduan/src/views/UserCenter.vue b/music-qianduan/src/views/UserCenter.vue index 225bc2e..00dea92 100644 --- a/music-qianduan/src/views/UserCenter.vue +++ b/music-qianduan/src/views/UserCenter.vue @@ -12,6 +12,22 @@ import { processImageUrl } from '../utils/imageUtils' const router = useRouter() const userStore = useUserStore() +const extractList = (data) => { + if (Array.isArray(data)) return data + if (Array.isArray(data?.list)) return data.list + if (Array.isArray(data?.records)) return data.records + if (Array.isArray(data?.items)) return data.items + return [] +} + +const extractPageData = (data) => { + const list = extractList(data) + return { + list, + total: Number.isFinite(Number(data?.total)) ? Number(data.total) : list.length + } +} + // 用户信息 const userInfo = ref(null) @@ -43,7 +59,7 @@ const avatarUploadUrl = ref(`${import.meta.env.VITE_API_BASE_URL || ''}/api/user // 头像上传headers const uploadHeaders = ref({ - [localStorage.getItem('tokenName') || 'satoken']: userStore.token + [localStorage.getItem('tokenName') || 'satoken']: `${localStorage.getItem('tokenPrefix') || ''}${userStore.token || localStorage.getItem('token') || ''}` }) // 当前激活的菜单 @@ -168,25 +184,25 @@ const fetchUserStats = async () => { // 获取收藏的音乐数量 const musicRes = await getCollectedMusic() if (musicRes.code === 200) { - userStats.value.collectMusicCount = musicRes.data.total || musicRes.data.length || 0 + userStats.value.collectMusicCount = extractPageData(musicRes.data).total } // 获取收藏的歌单数量 const playlistRes = await getCollectedPlaylist() if (playlistRes.code === 200) { - userStats.value.collectPlaylistCount = playlistRes.data.total || playlistRes.data.length || 0 + userStats.value.collectPlaylistCount = extractPageData(playlistRes.data).total } // 获取收藏的歌手数量 const singerRes = await getCollectedSinger() if (singerRes.code === 200) { - userStats.value.collectSingerCount = singerRes.data.total || singerRes.data.length || 0 + userStats.value.collectSingerCount = extractPageData(singerRes.data).total } // 获取创建的歌单数量 const userPlaylistRes = await getUserPlaylist() if (userPlaylistRes.code === 200) { - userStats.value.createPlaylistCount = userPlaylistRes.data.total || userPlaylistRes.data.length || 0 + userStats.value.createPlaylistCount = extractPageData(userPlaylistRes.data).total } } catch (error) { console.error('获取用户统计数据失败:', error)