mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-28 10:57:23 +08:00
feat: 添加历史日推功能
This commit is contained in:
@@ -43,6 +43,8 @@ export default {
|
|||||||
collapse: '收起',
|
collapse: '收起',
|
||||||
songCount: '{count}首',
|
songCount: '{count}首',
|
||||||
language: '语言',
|
language: '语言',
|
||||||
|
today: '今天',
|
||||||
|
yesterday: '昨天',
|
||||||
tray: {
|
tray: {
|
||||||
show: '显示',
|
show: '显示',
|
||||||
quit: '退出',
|
quit: '退出',
|
||||||
|
|||||||
@@ -117,7 +117,11 @@ export default {
|
|||||||
cancelCollect: '取消收藏',
|
cancelCollect: '取消收藏',
|
||||||
addToPlaylist: '添加到播放列表',
|
addToPlaylist: '添加到播放列表',
|
||||||
addToPlaylistSuccess: '添加到播放列表成功',
|
addToPlaylistSuccess: '添加到播放列表成功',
|
||||||
songsAlreadyInPlaylist: '歌曲已存在于播放列表中'
|
songsAlreadyInPlaylist: '歌曲已存在于播放列表中',
|
||||||
|
historyRecommend: '历史日推',
|
||||||
|
fetchDatesFailed: '获取日期列表失败',
|
||||||
|
fetchSongsFailed: '获取歌曲列表失败',
|
||||||
|
noSongs: '暂无歌曲'
|
||||||
},
|
},
|
||||||
playlist: {
|
playlist: {
|
||||||
import: {
|
import: {
|
||||||
|
|||||||
@@ -188,3 +188,25 @@ export function subscribeAlbum(params: { t: number; id: number }) {
|
|||||||
params
|
params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取历史日推可用日期列表
|
||||||
|
*/
|
||||||
|
export function getHistoryRecommendDates() {
|
||||||
|
return request({
|
||||||
|
url: '/history/recommend/songs',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取历史日推详情数据
|
||||||
|
* @param date 日期,格式:YYYY-MM-DD
|
||||||
|
*/
|
||||||
|
export function getHistoryRecommendSongs(date: string) {
|
||||||
|
return request({
|
||||||
|
url: '/history/recommend/songs/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { date }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<div class="song-item-content-compact">
|
<div class="song-item-content-compact">
|
||||||
<div class="song-item-content-compact-wrapper">
|
<div class="song-item-content-compact-wrapper">
|
||||||
<div class="song-item-content-compact-title w-60 flex-shrink-0">
|
<div class="song-item-content-compact-title">
|
||||||
<n-ellipsis
|
<n-ellipsis
|
||||||
class="text-ellipsis"
|
class="text-ellipsis"
|
||||||
line-clamp="1"
|
line-clamp="1"
|
||||||
@@ -197,26 +197,26 @@ const formatDuration = (ms: number): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.song-item-content-compact {
|
.song-item-content-compact {
|
||||||
@apply flex-1 flex items-center gap-4;
|
@apply flex-1 flex items-center gap-2;
|
||||||
|
|
||||||
&-wrapper {
|
&-wrapper {
|
||||||
@apply flex-1 min-w-0 flex items-center;
|
@apply flex-[2] flex items-center gap-2 min-w-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
@apply text-sm cursor-pointer text-gray-900 dark:text-white flex items-center;
|
@apply flex-[2.5] min-w-0 text-sm cursor-pointer text-gray-900 dark:text-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-artist {
|
&-artist {
|
||||||
@apply w-40 text-sm text-gray-500 dark:text-gray-400 ml-2 flex items-center;
|
@apply flex-[1.5] min-w-0 text-sm text-gray-500 dark:text-gray-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-album {
|
&-album {
|
||||||
@apply w-32 flex items-center text-sm text-gray-500 dark:text-gray-400;
|
@apply flex-[1.5] min-w-0 text-sm text-gray-500 dark:text-gray-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-duration {
|
&-duration {
|
||||||
@apply w-16 flex items-center text-sm text-gray-500 dark:text-gray-400 text-right;
|
@apply w-14 flex-shrink-0 text-sm text-gray-500 dark:text-gray-400 justify-end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,17 @@ const otherRouter = [
|
|||||||
back: true
|
back: true
|
||||||
},
|
},
|
||||||
component: () => import('@/views/heatmap/index.vue')
|
component: () => import('@/views/heatmap/index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/history-recommend',
|
||||||
|
name: 'historyRecommend',
|
||||||
|
meta: {
|
||||||
|
title: '历史日推',
|
||||||
|
keepAlive: true,
|
||||||
|
showInMenu: false,
|
||||||
|
back: true
|
||||||
|
},
|
||||||
|
component: () => import('@/views/music/HistoryRecommend.vue')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
export default otherRouter;
|
export default otherRouter;
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
return collectedAlbumIds.value.has(albumId);
|
return collectedAlbumIds.value.has(albumId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 判断用户是否为VIP
|
||||||
|
const isVip = computed(() => {
|
||||||
|
if (!user.value) return false;
|
||||||
|
// vipType: 0 非VIP, 11 VIP
|
||||||
|
return user.value.vipType && user.value.vipType !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
const initializeUser = async () => {
|
const initializeUser = async () => {
|
||||||
const savedUser = getLocalStorageItem<UserData | null>('user', null);
|
const savedUser = getLocalStorageItem<UserData | null>('user', null);
|
||||||
@@ -184,6 +191,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
collectedAlbumIds,
|
collectedAlbumIds,
|
||||||
playList,
|
playList,
|
||||||
albumList,
|
albumList,
|
||||||
|
isVip,
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
setUser,
|
setUser,
|
||||||
|
|||||||
@@ -0,0 +1,379 @@
|
|||||||
|
<template>
|
||||||
|
<div class="history-recommend-page">
|
||||||
|
<!-- 头部标题和操作按钮 -->
|
||||||
|
<div class="music-header h-12 flex items-center justify-between">
|
||||||
|
<n-ellipsis :line-clamp="1" class="flex-shrink-0 mr-3">
|
||||||
|
<div class="music-title">
|
||||||
|
{{ t('comp.musicList.historyRecommend') }}
|
||||||
|
</div>
|
||||||
|
</n-ellipsis>
|
||||||
|
|
||||||
|
<!-- 操作按钮组 -->
|
||||||
|
<div class="flex-grow flex-1 flex items-center justify-end gap-2">
|
||||||
|
<n-tooltip placement="bottom" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="action-button hover-green" @click="handlePlayAll">
|
||||||
|
<i class="icon iconfont ri-play-fill"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ t('comp.musicList.playAll') }}
|
||||||
|
</n-tooltip>
|
||||||
|
|
||||||
|
<n-tooltip placement="bottom" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="action-button hover-green" @click="addToPlaylist">
|
||||||
|
<i class="icon iconfont ri-add-line"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ t('comp.musicList.addToPlaylist') }}
|
||||||
|
</n-tooltip>
|
||||||
|
|
||||||
|
<!-- 布局切换按钮 -->
|
||||||
|
<div class="layout-toggle" v-if="!isMobile">
|
||||||
|
<n-tooltip placement="bottom" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="toggle-button hover-green" @click="toggleLayout">
|
||||||
|
<i
|
||||||
|
class="icon iconfont"
|
||||||
|
:class="isCompactLayout ? 'ri-list-check-2' : 'ri-grid-line'"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{
|
||||||
|
isCompactLayout
|
||||||
|
? t('comp.musicList.switchToNormal')
|
||||||
|
: t('comp.musicList.switchToCompact')
|
||||||
|
}}
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 日期选择标签 -->
|
||||||
|
<div v-if="availableDates.length > 0" class="date-tabs-wrapper">
|
||||||
|
<n-tabs
|
||||||
|
v-model:value="selectedDate"
|
||||||
|
type="segment"
|
||||||
|
animated
|
||||||
|
size="large"
|
||||||
|
@update:value="handleDateChange"
|
||||||
|
>
|
||||||
|
<n-tab
|
||||||
|
v-for="date in displayedDates"
|
||||||
|
:key="date"
|
||||||
|
:name="date"
|
||||||
|
:tab="formatDate(date)"
|
||||||
|
></n-tab>
|
||||||
|
</n-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 歌曲列表内容 -->
|
||||||
|
<div class="music-content">
|
||||||
|
<n-spin :show="loadingDates || loadingSongs">
|
||||||
|
<!-- 歌曲列表 -->
|
||||||
|
<div v-if="songs.length > 0" class="music-list-container">
|
||||||
|
<div class="music-list">
|
||||||
|
<div class="music-list-content">
|
||||||
|
<!-- 使用虚拟列表 -->
|
||||||
|
<n-virtual-list
|
||||||
|
class="song-virtual-list"
|
||||||
|
style="max-height: calc(100vh - 200px)"
|
||||||
|
:items="songs"
|
||||||
|
:item-size="isCompactLayout ? 50 : 70"
|
||||||
|
item-resizable
|
||||||
|
key-field="id"
|
||||||
|
>
|
||||||
|
<template #default="{ item, index }">
|
||||||
|
<div>
|
||||||
|
<div class="double-item">
|
||||||
|
<song-item
|
||||||
|
:index="index"
|
||||||
|
:compact="isCompactLayout"
|
||||||
|
:item="formatSong(item)"
|
||||||
|
@play="handlePlay"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="index === songs.length - 1" class="h-36"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-virtual-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else-if="!loadingSongs && selectedDate" class="empty-state">
|
||||||
|
<i class="icon iconfont ri-disc-line"></i>
|
||||||
|
<p>{{ t('comp.musicList.noSongs') }}</p>
|
||||||
|
</div>
|
||||||
|
</n-spin>
|
||||||
|
</div>
|
||||||
|
<play-bottom />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMessage } from 'naive-ui';
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { getHistoryRecommendDates, getHistoryRecommendSongs } from '@/api/music';
|
||||||
|
import PlayBottom from '@/components/common/PlayBottom.vue';
|
||||||
|
import SongItem from '@/components/common/SongItem.vue';
|
||||||
|
import { usePlayerStore } from '@/store';
|
||||||
|
import type { SongResult } from '@/types/music';
|
||||||
|
import { isMobile } from '@/utils';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const message = useMessage();
|
||||||
|
const playerStore = usePlayerStore();
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
const availableDates = ref<string[]>([]);
|
||||||
|
const selectedDate = ref<string>('');
|
||||||
|
const songs = ref<SongResult[]>([]);
|
||||||
|
const loadingDates = ref(false);
|
||||||
|
const loadingSongs = ref(false);
|
||||||
|
const isCompactLayout = ref(
|
||||||
|
isMobile.value ? false : localStorage.getItem('musicListLayout') === 'compact'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 只显示最近的10个日期
|
||||||
|
const displayedDates = computed(() => {
|
||||||
|
return availableDates.value.slice(0, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 格式化日期显示
|
||||||
|
const formatDate = (dateStr: string) => {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const today = new Date();
|
||||||
|
const yesterday = new Date(today);
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
|
||||||
|
// 判断是否是今天或昨天
|
||||||
|
if (date.toDateString() === today.toDateString()) {
|
||||||
|
return t('common.today');
|
||||||
|
} else if (date.toDateString() === yesterday.toDateString()) {
|
||||||
|
return t('common.yesterday');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化为 MM月DD日
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const day = date.getDate();
|
||||||
|
return `${month}月${day}日`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化歌曲数据
|
||||||
|
const formatSong = (item: any) => {
|
||||||
|
if (!item) return null;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
picUrl: item.al?.picUrl || item.album?.picUrl || item.picUrl,
|
||||||
|
song: {
|
||||||
|
artists: item.ar || item.artists || [],
|
||||||
|
name: item.al?.name || item.album?.name || item.name,
|
||||||
|
id: item.al?.id || item.album?.id || item.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取可用日期列表
|
||||||
|
const fetchAvailableDates = async () => {
|
||||||
|
try {
|
||||||
|
loadingDates.value = true;
|
||||||
|
const { data } = await getHistoryRecommendDates();
|
||||||
|
if (data?.data?.dates) {
|
||||||
|
availableDates.value = data.data.dates;
|
||||||
|
// 默认选择第一个日期(最近的日期)
|
||||||
|
if (availableDates.value.length > 0) {
|
||||||
|
selectedDate.value = availableDates.value[0];
|
||||||
|
await fetchSongsByDate(selectedDate.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取历史日推日期列表失败:', error);
|
||||||
|
message.error(t('comp.musicList.fetchDatesFailed'));
|
||||||
|
} finally {
|
||||||
|
loadingDates.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据日期获取歌曲列表
|
||||||
|
const fetchSongsByDate = async (date: string) => {
|
||||||
|
try {
|
||||||
|
loadingSongs.value = true;
|
||||||
|
const { data } = await getHistoryRecommendSongs(date);
|
||||||
|
if (data?.data?.songs) {
|
||||||
|
songs.value = data.data.songs;
|
||||||
|
} else {
|
||||||
|
songs.value = [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取历史日推歌曲失败:', error);
|
||||||
|
message.error(t('comp.musicList.fetchSongsFailed'));
|
||||||
|
songs.value = [];
|
||||||
|
} finally {
|
||||||
|
loadingSongs.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理日期变化
|
||||||
|
const handleDateChange = async (date: string) => {
|
||||||
|
selectedDate.value = date;
|
||||||
|
await fetchSongsByDate(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换布局
|
||||||
|
const toggleLayout = () => {
|
||||||
|
isCompactLayout.value = !isCompactLayout.value;
|
||||||
|
localStorage.setItem('musicListLayout', isCompactLayout.value ? 'compact' : 'normal');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加到播放列表末尾
|
||||||
|
const addToPlaylist = () => {
|
||||||
|
if (songs.value.length === 0) return;
|
||||||
|
|
||||||
|
// 获取当前播放列表
|
||||||
|
const currentList = playerStore.playList;
|
||||||
|
|
||||||
|
// 添加歌曲到播放列表(避免重复添加)
|
||||||
|
const newSongs = songs.value.filter((song) => !currentList.some((item) => item.id === song.id));
|
||||||
|
|
||||||
|
if (newSongs.length === 0) {
|
||||||
|
message.info(t('comp.musicList.songsAlreadyInPlaylist'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并到当前播放列表末尾
|
||||||
|
const newList = [...currentList, ...newSongs.map(formatSong)];
|
||||||
|
playerStore.setPlayList(newList);
|
||||||
|
|
||||||
|
message.success(t('comp.musicList.addToPlaylistSuccess', { count: newSongs.length }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 播放单首歌曲
|
||||||
|
const handlePlay = () => {
|
||||||
|
if (songs.value.length === 0) return;
|
||||||
|
playerStore.setPlayList(songs.value.map(formatSong));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 播放全部
|
||||||
|
const handlePlayAll = () => {
|
||||||
|
if (songs.value.length === 0) return;
|
||||||
|
playerStore.setPlayList(songs.value.map(formatSong));
|
||||||
|
playerStore.setPlay(formatSong(songs.value[0]));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组件挂载时获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
fetchAvailableDates();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.history-recommend-page {
|
||||||
|
@apply h-full bg-light-100 dark:bg-dark-100 px-4 mr-2 rounded-2xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.music {
|
||||||
|
&-header {
|
||||||
|
@apply h-12 flex items-center justify-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
@apply text-xl font-bold text-gray-900 dark:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
@apply h-[calc(100%-60px)];
|
||||||
|
}
|
||||||
|
|
||||||
|
&-list {
|
||||||
|
@apply flex-grow min-h-0;
|
||||||
|
&-container {
|
||||||
|
@apply flex-grow min-h-0 flex flex-col relative w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
@apply min-h-[calc(80vh-60px)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-tabs-wrapper {
|
||||||
|
@apply px-0 mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
@apply w-8 h-8 rounded-full flex items-center justify-center cursor-pointer hover:bg-light-300 dark:hover:bg-dark-300 transition-colors text-gray-500 dark:text-gray-400;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@apply text-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hover-green:hover {
|
||||||
|
.icon {
|
||||||
|
@apply text-green-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 虚拟列表样式 */
|
||||||
|
.song-virtual-list {
|
||||||
|
@apply w-full;
|
||||||
|
:deep(.n-virtual-list__scroll) {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-gray-400 dark:bg-gray-600 rounded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.double-item {
|
||||||
|
@apply w-full mb-2 bg-light-200 bg-opacity-30 dark:bg-dark-200 dark:bg-opacity-20 rounded-3xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
@apply flex flex-col items-center justify-center h-full text-gray-400 dark:text-gray-600 py-20;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@apply text-6xl mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
@apply text-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-tabs-rail) {
|
||||||
|
@apply rounded-xl overflow-hidden !important;
|
||||||
|
.n-tabs-capsule {
|
||||||
|
@apply rounded-xl !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-tabs-wrapper {
|
||||||
|
:deep(.n-tabs-rail) {
|
||||||
|
@apply rounded-xl overflow-hidden bg-white dark:bg-dark-300 !important;
|
||||||
|
.n-tabs-capsule {
|
||||||
|
@apply rounded-xl bg-green-500 dark:bg-green-600 !important;
|
||||||
|
}
|
||||||
|
.n-tabs-tab--active {
|
||||||
|
@apply text-white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-toggle {
|
||||||
|
.toggle-button {
|
||||||
|
@apply w-8 h-8 rounded-full flex items-center justify-center cursor-pointer hover:bg-light-300 dark:hover:bg-dark-300 transition-colors;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@apply text-lg text-gray-500 dark:text-gray-400 transition-colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -152,6 +152,15 @@
|
|||||||
:class="setAnimationClass('animate__fadeIn')"
|
:class="setAnimationClass('animate__fadeIn')"
|
||||||
object-fit="cover"
|
object-fit="cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div v-if="isDailyRecommend && userStore.isVip" class="history-recommend-btn">
|
||||||
|
<n-button tertiary round type="primary" size="small" @click="goToHistoryRecommend">
|
||||||
|
<template #icon>
|
||||||
|
<i class="icon iconfont ri-history-line"></i>
|
||||||
|
</template>
|
||||||
|
{{ t('comp.musicList.historyRecommend') }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 歌单显示创建者,专辑显示艺术家 -->
|
<!-- 歌单显示创建者,专辑显示艺术家 -->
|
||||||
<div v-if="isAlbum && listInfo?.artist" class="creator-info">
|
<div v-if="isAlbum && listInfo?.artist" class="creator-info">
|
||||||
@@ -229,7 +238,7 @@ import { useMessage } from 'naive-ui';
|
|||||||
import PinyinMatch from 'pinyin-match';
|
import PinyinMatch from 'pinyin-match';
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getMusicDetail,
|
getMusicDetail,
|
||||||
@@ -249,6 +258,7 @@ import { getLoginErrorMessage, hasPermission } from '@/utils/auth';
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
const musicStore = useMusicStore();
|
const musicStore = useMusicStore();
|
||||||
const recommendStore = useRecommendStore();
|
const recommendStore = useRecommendStore();
|
||||||
@@ -1026,6 +1036,11 @@ const handleBatchDownload = async () => {
|
|||||||
await batchDownloadMusic(selectedSongsList);
|
await batchDownloadMusic(selectedSongsList);
|
||||||
cancelSelect();
|
cancelSelect();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 跳转到历史日推页面
|
||||||
|
const goToHistoryRecommend = () => {
|
||||||
|
router.push({ name: 'historyRecommend' });
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -1057,12 +1072,19 @@ const handleBatchDownload = async () => {
|
|||||||
@apply w-[25%] flex-shrink-0 pr-8 flex flex-col;
|
@apply w-[25%] flex-shrink-0 pr-8 flex flex-col;
|
||||||
|
|
||||||
.music-cover {
|
.music-cover {
|
||||||
@apply w-full aspect-square rounded-2xl overflow-hidden mb-4 min-h-[250px];
|
@apply w-full aspect-square rounded-2xl overflow-hidden mb-4 min-h-[250px] relative;
|
||||||
.cover-img {
|
.cover-img {
|
||||||
@apply w-full h-full object-cover;
|
@apply w-full h-full object-cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-recommend-btn {
|
||||||
|
@apply mb-4 absolute bottom-1 right-4 z-10;
|
||||||
|
:deep(.n-button) {
|
||||||
|
@apply w-full bg-black bg-opacity-30 text-green-400 hover:bg-opacity-50 hover:text-green-500 transition-colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.creator-info {
|
.creator-info {
|
||||||
@apply flex items-center mb-4;
|
@apply flex items-center mb-4;
|
||||||
.creator-name {
|
.creator-name {
|
||||||
|
|||||||
Reference in New Issue
Block a user