mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-28 19:07:23 +08:00
修改左侧导航栏, 添加歌单页面
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="//at.alicdn.com/t/font_2685283_qczwwfdwv9.css"
|
href="//at.alicdn.com/t/font_2685283_9pkmebnv585.css"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="./public/css/animate.css" />
|
<link rel="stylesheet" href="./public/css/animate.css" />
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import request from "@/utils/request";
|
||||||
|
import { IList, IRecommendList } from "@/type/list";
|
||||||
|
import type { IListDetail } from "@/type/listDetail";
|
||||||
|
|
||||||
|
interface IListByTagParams {
|
||||||
|
tag: string;
|
||||||
|
before: number;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据tag 获取歌单列表
|
||||||
|
export function getListByTag(params: IListByTagParams) {
|
||||||
|
return request.get<IList>("/top/playlist/highquality", { params: params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取推荐歌单
|
||||||
|
export function getRecommendList(limit: number = 30) {
|
||||||
|
return request.get<IRecommendList>("/personalized", { params: { limit } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取歌单详情
|
||||||
|
export function getListDetail(id: number | string) {
|
||||||
|
return request.get<IListDetail>("/playlist/detail", { params: { id } });
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ const isPlaying = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 播放音乐 设置音乐详情 打开音乐底栏
|
// 播放音乐 设置音乐详情 打开音乐底栏
|
||||||
const playMusicEvent = (item: SongResult) => {
|
const playMusicEvent = (item: any) => {
|
||||||
store.commit("setPlay", item);
|
store.commit("setPlay", item);
|
||||||
store.commit("setIsPlay", true);
|
store.commit("setIsPlay", true);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,9 +9,12 @@
|
|||||||
<search-bar />
|
<search-bar />
|
||||||
<!-- 主页面路由 -->
|
<!-- 主页面路由 -->
|
||||||
<n-layout class="main-content bg-black" :native-scrollbar="false">
|
<n-layout class="main-content bg-black" :native-scrollbar="false">
|
||||||
<keep-alive>
|
<router-view class="main-page" v-slot="{ Component }">
|
||||||
<router-view class="main-page"></router-view>
|
<keep-alive>
|
||||||
</keep-alive>
|
<component :is="Component" v-if="$route.meta.keepAlive" />
|
||||||
|
</keep-alive>
|
||||||
|
<component :is="Component" v-if="!$route.meta.keepAlive" />
|
||||||
|
</router-view>
|
||||||
</n-layout>
|
</n-layout>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,8 +60,7 @@ const menus = store.state.menus;
|
|||||||
height: 834px;
|
height: 834px;
|
||||||
}
|
}
|
||||||
&-page {
|
&-page {
|
||||||
padding: 20px 0;
|
margin: 20px 0;
|
||||||
padding-bottom: 80px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="app-menu-list">
|
<div class="app-menu-list">
|
||||||
<div class="app-menu-item" v-for="(item,index) in menus" :key="index">
|
<div class="app-menu-item" v-for="(item,index) in menus">
|
||||||
<router-link class="app-menu-item-link" :to="item.href">
|
<router-link class="app-menu-item-link" :to="item.path">
|
||||||
<i class="iconfont app-menu-item-icon" :style="iconStyle" :class="item.icon"></i>
|
<i
|
||||||
<span v-if="isText" class="app-menu-item-text ml-3">{{ item.text }}</span>
|
class="iconfont app-menu-item-icon"
|
||||||
|
:style="iconStyle(index)"
|
||||||
|
:class="item.mate.icon"
|
||||||
|
></i>
|
||||||
|
<span v-if="isText" class="app-menu-item-text ml-3">{{ item.mate.title }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -20,15 +24,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from "@vue/runtime-core";
|
import { computed, onMounted, ref, watch } from "@vue/runtime-core";
|
||||||
import type { PropType } from "vue";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
interface AppMenuItem {
|
|
||||||
href: string;
|
|
||||||
icon: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isText: {
|
isText: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -42,20 +39,32 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '#aaa'
|
default: '#aaa'
|
||||||
},
|
},
|
||||||
|
selectColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#10B981'
|
||||||
|
},
|
||||||
menus: {
|
menus: {
|
||||||
type: Array as PropType<AppMenuItem[]>,
|
type: Array as any,
|
||||||
default: []
|
default: []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let iconStyle = ref({})
|
const route = useRoute();
|
||||||
onMounted(() => {
|
const path = ref(route.path);
|
||||||
// 初始化
|
watch(() => route.path, async newParams => {
|
||||||
iconStyle.value = {
|
console.log(newParams);
|
||||||
fontSize: props.size,
|
|
||||||
color: props.color
|
path.value = newParams
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const iconStyle = (index: any) => {
|
||||||
|
let style = {
|
||||||
|
fontSize: props.size,
|
||||||
|
color: path.value === props.menus[index].path ? props.selectColor : props.color
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -73,7 +82,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-menu-item-icon:hover {
|
.app-menu-item-icon:hover {
|
||||||
color: #fff !important;
|
color: #10b981 !important;
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
transition: 0.2s ease-in-out;
|
transition: 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
const layoutRouter = [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
name: "home",
|
||||||
|
mate: {
|
||||||
|
keepAlive: true,
|
||||||
|
title: "首页",
|
||||||
|
icon: "icon-Home",
|
||||||
|
},
|
||||||
|
component: () => import("@/views/home/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/search",
|
||||||
|
name: "search",
|
||||||
|
mate: {
|
||||||
|
title: "搜索",
|
||||||
|
keepAlive: true,
|
||||||
|
icon: "icon-Search",
|
||||||
|
},
|
||||||
|
component: () => import("@/views/search/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/list",
|
||||||
|
name: "list",
|
||||||
|
mate: {
|
||||||
|
title: "歌单",
|
||||||
|
keepAlive: true,
|
||||||
|
icon: "icon-Paper",
|
||||||
|
},
|
||||||
|
component: () => import("@/views/list/index.vue"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default layoutRouter;
|
||||||
+2
-13
@@ -1,23 +1,12 @@
|
|||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
import AppLayout from "@/layout/AppLayout.vue";
|
import AppLayout from "@/layout/AppLayout.vue";
|
||||||
const layoutRouter = [
|
import homeRouter from "@/router/home";
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
name: "home",
|
|
||||||
component: () => import("@/views/home/index.vue"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/search",
|
|
||||||
name: "search",
|
|
||||||
component: () => import("@/views/search/index.vue"),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
component: AppLayout,
|
component: AppLayout,
|
||||||
children: layoutRouter,
|
children: homeRouter,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+2
-18
@@ -1,25 +1,9 @@
|
|||||||
import { createStore } from "vuex";
|
import { createStore } from "vuex";
|
||||||
import { SongResult } from "@/type/music";
|
import { SongResult } from "@/type/music";
|
||||||
import { getMusicUrl } from "@/api/music";
|
import { getMusicUrl } from "@/api/music";
|
||||||
|
import homeRouter from "@/router/home";
|
||||||
let state = {
|
let state = {
|
||||||
menus: [
|
menus: homeRouter,
|
||||||
{
|
|
||||||
href: "/",
|
|
||||||
icon: "icon-homefill",
|
|
||||||
text: "hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "/search",
|
|
||||||
icon: "icon-peoplefill",
|
|
||||||
text: "hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "/",
|
|
||||||
icon: "icon-videofill",
|
|
||||||
text: "hello",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
play: false,
|
play: false,
|
||||||
isPlay: false,
|
isPlay: false,
|
||||||
playMusic: {} as SongResult,
|
playMusic: {} as SongResult,
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
export interface IList {
|
||||||
|
playlists: Playlist[];
|
||||||
|
code: number;
|
||||||
|
more: boolean;
|
||||||
|
lasttime: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Playlist {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
trackNumberUpdateTime: number;
|
||||||
|
status: number;
|
||||||
|
userId: number;
|
||||||
|
createTime: number;
|
||||||
|
updateTime: number;
|
||||||
|
subscribedCount: number;
|
||||||
|
trackCount: number;
|
||||||
|
cloudTrackCount: number;
|
||||||
|
coverImgUrl: string;
|
||||||
|
coverImgId: number;
|
||||||
|
description: string;
|
||||||
|
tags: string[];
|
||||||
|
playCount: number;
|
||||||
|
trackUpdateTime: number;
|
||||||
|
specialType: number;
|
||||||
|
totalDuration: number;
|
||||||
|
creator: Creator;
|
||||||
|
tracks?: any;
|
||||||
|
subscribers: Subscriber[];
|
||||||
|
subscribed: boolean;
|
||||||
|
commentThreadId: string;
|
||||||
|
newImported: boolean;
|
||||||
|
adType: number;
|
||||||
|
highQuality: boolean;
|
||||||
|
privacy: number;
|
||||||
|
ordered: boolean;
|
||||||
|
anonimous: boolean;
|
||||||
|
coverStatus: number;
|
||||||
|
recommendInfo?: any;
|
||||||
|
shareCount: number;
|
||||||
|
coverImgId_str?: string;
|
||||||
|
commentCount: number;
|
||||||
|
copywriter: string;
|
||||||
|
tag: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Subscriber {
|
||||||
|
defaultAvatar: boolean;
|
||||||
|
province: number;
|
||||||
|
authStatus: number;
|
||||||
|
followed: boolean;
|
||||||
|
avatarUrl: string;
|
||||||
|
accountStatus: number;
|
||||||
|
gender: number;
|
||||||
|
city: number;
|
||||||
|
birthday: number;
|
||||||
|
userId: number;
|
||||||
|
userType: number;
|
||||||
|
nickname: string;
|
||||||
|
signature: string;
|
||||||
|
description: string;
|
||||||
|
detailDescription: string;
|
||||||
|
avatarImgId: number;
|
||||||
|
backgroundImgId: number;
|
||||||
|
backgroundUrl: string;
|
||||||
|
authority: number;
|
||||||
|
mutual: boolean;
|
||||||
|
expertTags?: any;
|
||||||
|
experts?: any;
|
||||||
|
djStatus: number;
|
||||||
|
vipType: number;
|
||||||
|
remarkName?: any;
|
||||||
|
authenticationTypes: number;
|
||||||
|
avatarDetail?: any;
|
||||||
|
avatarImgIdStr: string;
|
||||||
|
backgroundImgIdStr: string;
|
||||||
|
anchor: boolean;
|
||||||
|
avatarImgId_str?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Creator {
|
||||||
|
defaultAvatar: boolean;
|
||||||
|
province: number;
|
||||||
|
authStatus: number;
|
||||||
|
followed: boolean;
|
||||||
|
avatarUrl: string;
|
||||||
|
accountStatus: number;
|
||||||
|
gender: number;
|
||||||
|
city: number;
|
||||||
|
birthday: number;
|
||||||
|
userId: number;
|
||||||
|
userType: number;
|
||||||
|
nickname: string;
|
||||||
|
signature: string;
|
||||||
|
description: string;
|
||||||
|
detailDescription: string;
|
||||||
|
avatarImgId: number;
|
||||||
|
backgroundImgId: number;
|
||||||
|
backgroundUrl: string;
|
||||||
|
authority: number;
|
||||||
|
mutual: boolean;
|
||||||
|
expertTags?: string[];
|
||||||
|
experts?: Expert;
|
||||||
|
djStatus: number;
|
||||||
|
vipType: number;
|
||||||
|
remarkName?: any;
|
||||||
|
authenticationTypes: number;
|
||||||
|
avatarDetail?: AvatarDetail;
|
||||||
|
avatarImgIdStr: string;
|
||||||
|
backgroundImgIdStr: string;
|
||||||
|
anchor: boolean;
|
||||||
|
avatarImgId_str?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AvatarDetail {
|
||||||
|
userType: number;
|
||||||
|
identityLevel: number;
|
||||||
|
identityIconUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Expert {
|
||||||
|
"2": string;
|
||||||
|
"1"?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推荐歌单
|
||||||
|
export interface IRecommendList {
|
||||||
|
hasTaste: boolean;
|
||||||
|
code: number;
|
||||||
|
category: number;
|
||||||
|
result: IRecommendItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRecommendItem {
|
||||||
|
id: number;
|
||||||
|
type: number;
|
||||||
|
name: string;
|
||||||
|
copywriter: string;
|
||||||
|
picUrl: string;
|
||||||
|
canDislike: boolean;
|
||||||
|
trackNumberUpdateTime: number;
|
||||||
|
playCount: number;
|
||||||
|
trackCount: number;
|
||||||
|
highQuality: boolean;
|
||||||
|
alg: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
export interface IListDetail {
|
||||||
|
code: number;
|
||||||
|
relatedVideos?: any;
|
||||||
|
playlist: Playlist;
|
||||||
|
urls?: any;
|
||||||
|
privileges: Privilege[];
|
||||||
|
sharedPrivilege?: any;
|
||||||
|
resEntrance?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Privilege {
|
||||||
|
id: number;
|
||||||
|
fee: number;
|
||||||
|
payed: number;
|
||||||
|
realPayed: number;
|
||||||
|
st: number;
|
||||||
|
pl: number;
|
||||||
|
dl: number;
|
||||||
|
sp: number;
|
||||||
|
cp: number;
|
||||||
|
subp: number;
|
||||||
|
cs: boolean;
|
||||||
|
maxbr: number;
|
||||||
|
fl: number;
|
||||||
|
pc?: any;
|
||||||
|
toast: boolean;
|
||||||
|
flag: number;
|
||||||
|
paidBigBang: boolean;
|
||||||
|
preSell: boolean;
|
||||||
|
playMaxbr: number;
|
||||||
|
downloadMaxbr: number;
|
||||||
|
rscl?: any;
|
||||||
|
freeTrialPrivilege: FreeTrialPrivilege;
|
||||||
|
chargeInfoList: ChargeInfoList[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChargeInfoList {
|
||||||
|
rate: number;
|
||||||
|
chargeUrl?: any;
|
||||||
|
chargeMessage?: any;
|
||||||
|
chargeType: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FreeTrialPrivilege {
|
||||||
|
resConsumable: boolean;
|
||||||
|
userConsumable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Playlist {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
coverImgId: number;
|
||||||
|
coverImgUrl: string;
|
||||||
|
coverImgId_str: string;
|
||||||
|
adType: number;
|
||||||
|
userId: number;
|
||||||
|
createTime: number;
|
||||||
|
status: number;
|
||||||
|
opRecommend: boolean;
|
||||||
|
highQuality: boolean;
|
||||||
|
newImported: boolean;
|
||||||
|
updateTime: number;
|
||||||
|
trackCount: number;
|
||||||
|
specialType: number;
|
||||||
|
privacy: number;
|
||||||
|
trackUpdateTime: number;
|
||||||
|
commentThreadId: string;
|
||||||
|
playCount: number;
|
||||||
|
trackNumberUpdateTime: number;
|
||||||
|
subscribedCount: number;
|
||||||
|
cloudTrackCount: number;
|
||||||
|
ordered: boolean;
|
||||||
|
description: string;
|
||||||
|
tags: string[];
|
||||||
|
updateFrequency?: any;
|
||||||
|
backgroundCoverId: number;
|
||||||
|
backgroundCoverUrl?: any;
|
||||||
|
titleImage: number;
|
||||||
|
titleImageUrl?: any;
|
||||||
|
englishTitle?: any;
|
||||||
|
officialPlaylistType?: any;
|
||||||
|
subscribers: Subscriber[];
|
||||||
|
subscribed: boolean;
|
||||||
|
creator: Subscriber;
|
||||||
|
tracks: Track[];
|
||||||
|
videoIds?: any;
|
||||||
|
videos?: any;
|
||||||
|
trackIds: TrackId[];
|
||||||
|
shareCount: number;
|
||||||
|
commentCount: number;
|
||||||
|
remixVideo?: any;
|
||||||
|
sharedUsers?: any;
|
||||||
|
historySharedUsers?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TrackId {
|
||||||
|
id: number;
|
||||||
|
v: number;
|
||||||
|
t: number;
|
||||||
|
at: number;
|
||||||
|
alg?: any;
|
||||||
|
uid: number;
|
||||||
|
rcmdReason: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Track {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
pst: number;
|
||||||
|
t: number;
|
||||||
|
ar: Ar[];
|
||||||
|
alia: string[];
|
||||||
|
pop: number;
|
||||||
|
st: number;
|
||||||
|
rt?: string;
|
||||||
|
fee: number;
|
||||||
|
v: number;
|
||||||
|
crbt?: any;
|
||||||
|
cf: string;
|
||||||
|
al: Al;
|
||||||
|
dt: number;
|
||||||
|
h: H;
|
||||||
|
m: H;
|
||||||
|
l?: H;
|
||||||
|
a?: any;
|
||||||
|
cd: string;
|
||||||
|
no: number;
|
||||||
|
rtUrl?: any;
|
||||||
|
ftype: number;
|
||||||
|
rtUrls: any[];
|
||||||
|
djId: number;
|
||||||
|
copyright: number;
|
||||||
|
s_id: number;
|
||||||
|
mark: number;
|
||||||
|
originCoverType: number;
|
||||||
|
originSongSimpleData?: any;
|
||||||
|
single: number;
|
||||||
|
noCopyrightRcmd?: any;
|
||||||
|
mst: number;
|
||||||
|
cp: number;
|
||||||
|
mv: number;
|
||||||
|
rtype: number;
|
||||||
|
rurl?: any;
|
||||||
|
publishTime: number;
|
||||||
|
tns?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface H {
|
||||||
|
br: number;
|
||||||
|
fid: number;
|
||||||
|
size: number;
|
||||||
|
vd: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Al {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
picUrl: string;
|
||||||
|
tns: any[];
|
||||||
|
pic_str?: string;
|
||||||
|
pic: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Ar {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
tns: any[];
|
||||||
|
alias: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Subscriber {
|
||||||
|
defaultAvatar: boolean;
|
||||||
|
province: number;
|
||||||
|
authStatus: number;
|
||||||
|
followed: boolean;
|
||||||
|
avatarUrl: string;
|
||||||
|
accountStatus: number;
|
||||||
|
gender: number;
|
||||||
|
city: number;
|
||||||
|
birthday: number;
|
||||||
|
userId: number;
|
||||||
|
userType: number;
|
||||||
|
nickname: string;
|
||||||
|
signature: string;
|
||||||
|
description: string;
|
||||||
|
detailDescription: string;
|
||||||
|
avatarImgId: number;
|
||||||
|
backgroundImgId: number;
|
||||||
|
backgroundUrl: string;
|
||||||
|
authority: number;
|
||||||
|
mutual: boolean;
|
||||||
|
expertTags?: any;
|
||||||
|
experts?: any;
|
||||||
|
djStatus: number;
|
||||||
|
vipType: number;
|
||||||
|
remarkName?: any;
|
||||||
|
authenticationTypes: number;
|
||||||
|
avatarDetail?: any;
|
||||||
|
backgroundImgIdStr: string;
|
||||||
|
anchor: boolean;
|
||||||
|
avatarImgIdStr: string;
|
||||||
|
avatarImgId_str: string;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-page pb-20">
|
<div class="main-page">
|
||||||
<!-- 推荐歌手 -->
|
<!-- 推荐歌手 -->
|
||||||
<recommend-singer />
|
<recommend-singer />
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
@@ -19,9 +19,12 @@ import PlaylistType from "@/components/PlaylistType.vue";
|
|||||||
import RecommendSonglist from "@/components/RecommendSonglist.vue";
|
import RecommendSonglist from "@/components/RecommendSonglist.vue";
|
||||||
import RecommendAlbum from "@/components/RecommendAlbum.vue";
|
import RecommendAlbum from "@/components/RecommendAlbum.vue";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.main-page {
|
||||||
|
@apply mt-4 pb-32;
|
||||||
|
}
|
||||||
.main-content {
|
.main-content {
|
||||||
@apply mt-6 flex;
|
@apply mt-6 flex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { getRecommendList, getListDetail } from '@/api/list'
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import type { IRecommendList, IRecommendItem } from "@/type/list";
|
||||||
|
import type { IListDetail } from "@/type/listDetail";
|
||||||
|
import { setAnimationClass, setAnimationDelay } from "@/utils";
|
||||||
|
import SongItem from "@/components/common/SongItem.vue";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const recommendList = ref<IRecommendList>()
|
||||||
|
const showMusic = ref(false)
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await getRecommendList()
|
||||||
|
recommendList.value = data
|
||||||
|
})
|
||||||
|
|
||||||
|
const recommendItem = ref<IRecommendItem>()
|
||||||
|
const listDetail = ref<IListDetail>()
|
||||||
|
const selectRecommendItem = async (item: IRecommendItem) => {
|
||||||
|
const { data } = await getListDetail(item.id)
|
||||||
|
showMusic.value = true
|
||||||
|
recommendItem.value = item
|
||||||
|
listDetail.value = data
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
const closeMusic = () => {
|
||||||
|
showMusic.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const musicFullClass = computed(() => {
|
||||||
|
if (recommendItem.value) {
|
||||||
|
return setAnimationClass('animate__fadeInUp')
|
||||||
|
} else {
|
||||||
|
return setAnimationClass('animate__fadeOutDown')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// 格式化数字 千,万, 百万, 千万,亿
|
||||||
|
const formatNumber = (num: any) => {
|
||||||
|
num = num * 1
|
||||||
|
if (num < 10000) {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
if (num < 100000000) {
|
||||||
|
return (num / 10000).toFixed(1) + '万'
|
||||||
|
}
|
||||||
|
return (num / 100000000).toFixed(1) + '亿'
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDetail = computed(() => (detail: any) => {
|
||||||
|
let song = {
|
||||||
|
artists: detail.ar,
|
||||||
|
name: detail.al.name,
|
||||||
|
id: detail.al.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
detail.song = song
|
||||||
|
detail.picUrl = detail.al.picUrl
|
||||||
|
return detail
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="list-page">
|
||||||
|
<n-layout class="recommend" :native-scrollbar="false" @click="showMusic = false">
|
||||||
|
<div class="recommend-title" :class="setAnimationClass('animate__bounceInLeft')">歌单推荐</div>
|
||||||
|
<div class="recommend-list">
|
||||||
|
<div
|
||||||
|
class="recommend-item"
|
||||||
|
v-for="(item,index) in recommendList?.result"
|
||||||
|
:class="setAnimationClass('animate__bounceIn')"
|
||||||
|
:style="setAnimationDelay(index, 30)"
|
||||||
|
@click.stop="selectRecommendItem(item)"
|
||||||
|
>
|
||||||
|
<div class="recommend-item-img">
|
||||||
|
<img :src="item.picUrl + '?param=200y200'" alt />
|
||||||
|
<div class="top">
|
||||||
|
<div class="play-count">{{ formatNumber(item.playCount) }}</div>
|
||||||
|
<i class="iconfont icon-videofill"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="recommend-item-title">{{ item.name }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-layout>
|
||||||
|
|
||||||
|
<div class="music-page" v-show="showMusic" :class="musicFullClass">
|
||||||
|
<i class="iconfont icon-icon_error music-close" @click="closeMusic()"></i>
|
||||||
|
<div class="music-title">{{ recommendItem?.name }}</div>
|
||||||
|
<n-layout class="music-list" :native-scrollbar="false">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in listDetail?.playlist.tracks"
|
||||||
|
:key="item.id"
|
||||||
|
:class="setAnimationClass('animate__bounceInRight')"
|
||||||
|
:style="setAnimationDelay(index, 100)"
|
||||||
|
>
|
||||||
|
<SongItem :item="formatDetail(item)" />
|
||||||
|
</div>
|
||||||
|
</n-layout>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-page {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommend {
|
||||||
|
width: 100%;
|
||||||
|
height: 800px;
|
||||||
|
background-color: #000000;
|
||||||
|
&-title {
|
||||||
|
@apply text-lg font-bold text-white py-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-list {
|
||||||
|
@apply flex flex-wrap;
|
||||||
|
}
|
||||||
|
&-item {
|
||||||
|
width: 200px;
|
||||||
|
@apply mr-6 mb-4;
|
||||||
|
&-img {
|
||||||
|
@apply rounded-xl overflow-hidden relative;
|
||||||
|
&:hover img {
|
||||||
|
@apply hover:scale-110 transition-all duration-300 ease-in-out;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
.top {
|
||||||
|
@apply absolute w-full h-full top-0 left-0 flex justify-center items-center transition-all duration-300 ease-in-out cursor-pointer;
|
||||||
|
background-color: #00000088;
|
||||||
|
opacity: 0;
|
||||||
|
i {
|
||||||
|
font-size: 50px;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
@apply opacity-100;
|
||||||
|
}
|
||||||
|
&:hover i {
|
||||||
|
@apply transform scale-150 opacity-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-count {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-title {
|
||||||
|
@apply p-2 text-sm text-white truncate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.music {
|
||||||
|
&-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 734px;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #000000f0;
|
||||||
|
top: 100px;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 30px 30px 0 0;
|
||||||
|
animation-duration: 300ms;
|
||||||
|
}
|
||||||
|
&-title {
|
||||||
|
@apply text-lg font-bold text-white p-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-close {
|
||||||
|
@apply absolute top-4 right-4 cursor-pointer text-white text-3xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-list {
|
||||||
|
height: 594px;
|
||||||
|
background-color: #00000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user