feat: 添加播放历史页面

This commit is contained in:
alger
2023-12-29 16:04:44 +08:00
parent 8da7fdabe5
commit 492164d008
10 changed files with 317 additions and 207 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"version": "0.0.1", "version": "1.3.0",
"isProxy": false, "isProxy": false,
"author": "alger" "author": "alger"
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "alger-music", "name": "alger-music",
"version": "1.2.0", "version": "1.3.0",
"description": "这是一个用于音乐播放的应用程序。", "description": "这是一个用于音乐播放的应用程序。",
"author": "Alger <algerkc@qq.com>", "author": "Alger <algerkc@qq.com>",
"main": "app.js", "main": "app.js",
+5
View File
@@ -7,6 +7,11 @@ export const getMusicUrl = (id: number) => {
return request.get<IPlayMusicUrl>("/song/url", { params: { id: id } }) return request.get<IPlayMusicUrl>("/song/url", { params: { id: id } })
} }
// 获取歌曲详情
export const getMusicDetail = (ids: Array<number>) => {
return request.get("/song/detail", { params: { ids: ids.join(",")}})
}
// 根据音乐Id获取音乐歌词 // 根据音乐Id获取音乐歌词
export const getMusicLrc = (id: number) => { export const getMusicLrc = (id: number) => {
return request.get<ILyric>("/lyric", { params: { id: id } }) return request.get<ILyric>("/lyric", { params: { id: id } })
+4
View File
@@ -45,6 +45,7 @@
import { useStore } from 'vuex' import { useStore } from 'vuex'
import type { SongResult } from '@/type/music' import type { SongResult } from '@/type/music'
import { getImgUrl } from '@/utils' import { getImgUrl } from '@/utils'
import { useMusicHistory } from '@/hooks/MusicHistoryHook'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
item: SongResult item: SongResult
@@ -66,12 +67,15 @@ const isPlaying = computed(() => {
const emits = defineEmits(['play']) const emits = defineEmits(['play'])
const musicHistory = useMusicHistory()
// 播放音乐 设置音乐详情 打开音乐底栏 // 播放音乐 设置音乐详情 打开音乐底栏
const playMusicEvent = (item: any) => { const playMusicEvent = (item: any) => {
store.commit('setPlay', item) store.commit('setPlay', item)
store.commit('setIsPlay', true) store.commit('setIsPlay', true)
store.state.playListIndex = 0 store.state.playListIndex = 0
emits('play', item) emits('play', item)
musicHistory.addMusic(item)
} }
</script> </script>
+39
View File
@@ -0,0 +1,39 @@
// musicHistoryHooks
import { RemovableRef, useLocalStorage } from '@vueuse/core'
import type { SongResult } from '@/type/music'
export const useMusicHistory = () => {
const musicHistory = useLocalStorage<SongResult[]>('musicHistory', [])
const addMusic = (music: SongResult) => {
const index = musicHistory.value.findIndex((item) => item.id === music.id)
if (index !== -1) {
musicHistory.value[index].count =
(musicHistory.value[index].count || 0) + 1
musicHistory.value.unshift(musicHistory.value.splice(index, 1)[0])
} else {
musicHistory.value.unshift({ ...music, count: 1 })
}
}
const delMusic = (music: any) => {
const index = musicHistory.value.findIndex((item) => item.id === music.id)
if (index !== -1) {
musicHistory.value.splice(index, 1)
}
}
const musicList = ref(musicHistory.value)
watch(
() => musicHistory.value,
() => {
musicList.value = musicHistory.value
}
)
return {
musicHistory,
musicList,
addMusic,
delMusic,
}
}
+10
View File
@@ -39,6 +39,16 @@ const layoutRouter = [
}, },
component: () => import('@/views/mv/index.vue'), component: () => import('@/views/mv/index.vue'),
}, },
{
path: '/history',
name: 'history',
mate: {
title: '历史',
keepAlive: true,
icon: 'icon-a-TicketStar',
},
component: () => import('@/views/history/index.vue'),
},
{ {
path: '/user', path: '/user',
name: 'user', name: 'user',
+197 -196
View File
@@ -1,196 +1,197 @@
export interface IRecommendMusic { export interface IRecommendMusic {
code: number; code: number;
category: number; category: number;
result: SongResult[]; result: SongResult[];
} }
export interface SongResult { export interface SongResult {
id: number; id: number
type: number; type: number
name: string; name: string
copywriter?: any; copywriter?: any
picUrl: string; picUrl: string
canDislike: boolean; canDislike: boolean
trackNumberUpdateTime?: any; trackNumberUpdateTime?: any
song: Song; song: Song
alg: string; alg: string
} count?: number
}
interface Song {
name: string; interface Song {
id: number; name: string;
position: number; id: number;
alias: string[]; position: number;
status: number; alias: string[];
fee: number; status: number;
copyrightId: number; fee: number;
disc: string; copyrightId: number;
no: number; disc: string;
artists: Artist[]; no: number;
album: Album; artists: Artist[];
starred: boolean; album: Album;
popularity: number; starred: boolean;
score: number; popularity: number;
starredNum: number; score: number;
duration: number; starredNum: number;
playedNum: number; duration: number;
dayPlays: number; playedNum: number;
hearTime: number; dayPlays: number;
ringtone: string; hearTime: number;
crbt?: any; ringtone: string;
audition?: any; crbt?: any;
copyFrom: string; audition?: any;
commentThreadId: string; copyFrom: string;
rtUrl?: any; commentThreadId: string;
ftype: number; rtUrl?: any;
rtUrls: any[]; ftype: number;
copyright: number; rtUrls: any[];
transName?: any; copyright: number;
sign?: any; transName?: any;
mark: number; sign?: any;
originCoverType: number; mark: number;
originSongSimpleData?: any; originCoverType: number;
single: number; originSongSimpleData?: any;
noCopyrightRcmd?: any; single: number;
rtype: number; noCopyrightRcmd?: any;
rurl?: any; rtype: number;
mvid: number; rurl?: any;
bMusic: BMusic; mvid: number;
mp3Url?: any; bMusic: BMusic;
hMusic: BMusic; mp3Url?: any;
mMusic: BMusic; hMusic: BMusic;
lMusic: BMusic; mMusic: BMusic;
exclusive: boolean; lMusic: BMusic;
privilege: Privilege; exclusive: boolean;
} privilege: Privilege;
}
interface Privilege {
id: number; interface Privilege {
fee: number; id: number;
payed: number; fee: number;
st: number; payed: number;
pl: number; st: number;
dl: number; pl: number;
sp: number; dl: number;
cp: number; sp: number;
subp: number; cp: number;
cs: boolean; subp: number;
maxbr: number; cs: boolean;
fl: number; maxbr: number;
toast: boolean; fl: number;
flag: number; toast: boolean;
preSell: boolean; flag: number;
playMaxbr: number; preSell: boolean;
downloadMaxbr: number; playMaxbr: number;
rscl?: any; downloadMaxbr: number;
freeTrialPrivilege: FreeTrialPrivilege; rscl?: any;
chargeInfoList: ChargeInfoList[]; freeTrialPrivilege: FreeTrialPrivilege;
} chargeInfoList: ChargeInfoList[];
}
interface ChargeInfoList {
rate: number; interface ChargeInfoList {
chargeUrl?: any; rate: number;
chargeMessage?: any; chargeUrl?: any;
chargeType: number; chargeMessage?: any;
} chargeType: number;
}
interface FreeTrialPrivilege {
resConsumable: boolean; interface FreeTrialPrivilege {
userConsumable: boolean; resConsumable: boolean;
} userConsumable: boolean;
}
interface BMusic {
name?: any; interface BMusic {
id: number; name?: any;
size: number; id: number;
extension: string; size: number;
sr: number; extension: string;
dfsId: number; sr: number;
bitrate: number; dfsId: number;
playTime: number; bitrate: number;
volumeDelta: number; playTime: number;
} volumeDelta: number;
}
interface Album {
name: string; interface Album {
id: number; name: string;
type: string; id: number;
size: number; type: string;
picId: number; size: number;
blurPicUrl: string; picId: number;
companyId: number; blurPicUrl: string;
pic: number; companyId: number;
picUrl: string; pic: number;
publishTime: number; picUrl: string;
description: string; publishTime: number;
tags: string; description: string;
company: string; tags: string;
briefDesc: string; company: string;
artist: Artist; briefDesc: string;
songs: any[]; artist: Artist;
alias: string[]; songs: any[];
status: number; alias: string[];
copyrightId: number; status: number;
commentThreadId: string; copyrightId: number;
artists: Artist[]; commentThreadId: string;
subType: string; artists: Artist[];
transName?: any; subType: string;
onSale: boolean; transName?: any;
mark: number; onSale: boolean;
picId_str: string; mark: number;
} picId_str: string;
}
interface Artist {
name: string; interface Artist {
id: number; name: string;
picId: number; id: number;
img1v1Id: number; picId: number;
briefDesc: string; img1v1Id: number;
picUrl: string; briefDesc: string;
img1v1Url: string; picUrl: string;
albumSize: number; img1v1Url: string;
alias: any[]; albumSize: number;
trans: string; alias: any[];
musicSize: number; trans: string;
topicPerson: number; musicSize: number;
} topicPerson: number;
}
export interface IPlayMusicUrl {
data: Datum[]; export interface IPlayMusicUrl {
code: number; data: Datum[];
} code: number;
}
interface Datum {
id: number; interface Datum {
url: string; id: number;
br: number; url: string;
size: number; br: number;
md5: string; size: number;
code: number; md5: string;
expi: number; code: number;
type: string; expi: number;
gain: number; type: string;
fee: number; gain: number;
uf?: any; fee: number;
payed: number; uf?: any;
flag: number; payed: number;
canExtend: boolean; flag: number;
freeTrialInfo?: any; canExtend: boolean;
level: string; freeTrialInfo?: any;
encodeType: string; level: string;
freeTrialPrivilege: FreeTrialPrivilege; encodeType: string;
freeTimeTrialPrivilege: FreeTimeTrialPrivilege; freeTrialPrivilege: FreeTrialPrivilege;
urlSource: number; freeTimeTrialPrivilege: FreeTimeTrialPrivilege;
} urlSource: number;
}
interface FreeTimeTrialPrivilege {
resConsumable: boolean; interface FreeTimeTrialPrivilege {
userConsumable: boolean; resConsumable: boolean;
type: number; userConsumable: boolean;
remainTime: number; type: number;
} remainTime: number;
}
interface FreeTrialPrivilege {
resConsumable: boolean; interface FreeTrialPrivilege {
userConsumable: boolean; resConsumable: boolean;
} userConsumable: boolean;
}
+45
View File
@@ -0,0 +1,45 @@
<template>
<div class="history-page">
<div class="title">播放历史</div>
<n-scrollbar :size="100">
<div class="history-list-content" :class="setAnimationClass('animate__bounceInLeft')">
<div class="history-item" v-for="(item, index) in musicList" :key="item.id"
:class="setAnimationClass('animate__bounceIn')" :style="setAnimationDelay(index, 30)">
<song-item class="history-item-content" :item="item" />
<div class="history-item-delete">
<i class="iconfont icon-close" @click="delMusic(item)"></i>
</div>
</div>
</div>
</n-scrollbar>
</div>
</template>
<script setup lang="ts">
import { useMusicHistory } from '@/hooks/MusicHistoryHook'
import { setAnimationClass, setAnimationDelay } from "@/utils";
const {delMusic, musicList} = useMusicHistory();
</script>
<style scoped lang="scss">
.history-page{
@apply h-full w-full pt-2;
.title{
@apply text-xl font-bold;
}
.history-list-content{
@apply px-4 mt-2;
.history-item{
@apply flex items-center justify-between;
&-content{
@apply flex-1;
}
&-delete{
@apply cursor-pointer rounded-full border-2 border-gray-400 w-8 h-8 flex justify-center items-center;
}
}
}
}
</style>
+1 -1
View File
@@ -97,7 +97,7 @@ watch(
<style lang="scss" scoped> <style lang="scss" scoped>
.list-page { .list-page {
@apply relative h-full w-full pt-4; @apply relative h-full w-full pt-2;
} }
.recommend { .recommend {
+14 -8
View File
@@ -72,19 +72,25 @@ const close = () => {
<style scoped lang="scss"> <style scoped lang="scss">
.mv-list { .mv-list {
@apply relative h-full w-full pt-4; @apply relative h-full w-full pt-2;
&-title {
@apply text-xl font-bold;
}
&-content { &-content {
@apply grid gap-6 pb-28 pr-3; @apply grid gap-6 pb-28 pr-3 mt-2;
grid-template-columns: repeat(auto-fill, minmax(13%, 1fr)); grid-template-columns: repeat(auto-fill, minmax(14%, 1fr));
} }
.mv-item { .mv-item {
@apply p-2 rounded-lg;
background-color: #454545;
&-img { &-img {
@apply rounded-xl overflow-hidden relative; @apply rounded-lg overflow-hidden relative;
&:hover img { &:hover img {
@apply hover:scale-110 transition-all duration-300 ease-in-out; @apply hover:scale-110 transition-all duration-300 ease-in-out object-top;
} }
&-img { &-img {
@@ -93,11 +99,11 @@ const close = () => {
.top { .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; @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; background-color: #0000009b;
opacity: 0; opacity: 0;
i { i {
font-size: 50px; font-size: 40px;
transition: all 0.5s ease-in-out; transition: all 0.5s ease-in-out;
opacity: 0; opacity: 0;
} }
@@ -107,7 +113,7 @@ const close = () => {
} }
&:hover i { &:hover i {
@apply transform scale-150 opacity-100; @apply transform scale-150 opacity-80;
} }
.play-count { .play-count {