mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-28 02:47:22 +08:00
✨ feat: 添加播放历史页面
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.0.1",
|
"version": "1.3.0",
|
||||||
"isProxy": false,
|
"isProxy": false,
|
||||||
"author": "alger"
|
"author": "alger"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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",
|
||||||
|
|||||||
@@ -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 } })
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user