YesPlayMusic/src/components/TrackList.vue

306 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="track-list">
<ContextMenu ref="menu">
<div v-show="type !== 'cloudDisk'" class="item-info">
<img
:src="rightClickedTrackComputed.al.picUrl | resizeImage(224)"
loading="lazy"
/>
<div class="info">
<div class="title">{{ rightClickedTrackComputed.name }}</div>
<div class="subtitle">{{ rightClickedTrackComputed.ar[0].name }}</div>
</div>
</div>
<hr v-show="type !== 'cloudDisk'" />
<div class="item" @click="play">{{ $t('contextMenu.play') }}</div>
<div class="item" @click="addToQueue">{{
$t('contextMenu.addToQueue')
}}</div>
<div
v-if="extraContextMenuItem.includes('removeTrackFromQueue')"
class="item"
@click="removeTrackFromQueue"
>从队列删除</div
>
<hr v-show="type !== 'cloudDisk'" />
<div
v-show="!isRightClickedTrackLiked && type !== 'cloudDisk'"
class="item"
@click="like"
>
{{ $t('contextMenu.saveToMyLikedSongs') }}
</div>
<div
v-show="isRightClickedTrackLiked && type !== 'cloudDisk'"
class="item"
@click="like"
>
{{ $t('contextMenu.removeFromMyLikedSongs') }}
</div>
<div
v-if="extraContextMenuItem.includes('removeTrackFromPlaylist')"
class="item"
@click="removeTrackFromPlaylist"
>从歌单中删除</div
>
<div
v-show="type !== 'cloudDisk'"
class="item"
@click="addTrackToPlaylist"
>{{ $t('contextMenu.addToPlaylist') }}</div
>
<div v-show="type !== 'cloudDisk'" class="item" @click="copyLink">{{
$t('contextMenu.copyUrl')
}}</div>
<div
v-if="extraContextMenuItem.includes('removeTrackFromCloudDisk')"
class="item"
@click="removeTrackFromCloudDisk"
>从云盘中删除</div
>
</ContextMenu>
<div :style="listStyles">
<TrackListItem
v-for="(track, index) in tracks"
:key="itemKey === 'id' ? track.id : `${track.id}${index}`"
:track-prop="track"
:highlight-playing-track="highlightPlayingTrack"
@dblclick.native="playThisList(track.id || track.songId)"
@click.right.native="openMenu($event, track, index)"
/>
</div>
</div>
</template>
<script>
import { mapActions, mapMutations, mapState } from 'vuex';
import { addOrRemoveTrackFromPlaylist } from '@/api/playlist';
import { cloudDiskTrackDelete } from '@/api/user';
import { isAccountLoggedIn } from '@/utils/auth';
import TrackListItem from '@/components/TrackListItem.vue';
import ContextMenu from '@/components/ContextMenu.vue';
import locale from '@/locale';
export default {
name: 'TrackList',
components: {
TrackListItem,
ContextMenu,
},
props: {
tracks: {
type: Array,
default: () => {
return [];
},
},
type: {
type: String,
default: 'tracklist',
}, // tracklist | album | playlist | cloudDisk
id: {
type: Number,
default: 0,
},
dbclickTrackFunc: {
type: String,
default: 'default',
},
albumObject: {
type: Object,
default: () => {
return {
artist: {
name: '',
},
};
},
},
extraContextMenuItem: {
type: Array,
default: () => {
return [
// 'removeTrackFromPlaylist'
// 'removeTrackFromQueue'
// 'removeTrackFromCloudDisk'
];
},
},
columnNumber: {
type: Number,
default: 4,
},
highlightPlayingTrack: {
type: Boolean,
default: true,
},
itemKey: {
type: String,
default: 'id',
},
},
data() {
return {
rightClickedTrack: {
id: 0,
name: '',
ar: [{ name: '' }],
al: { picUrl: '' },
},
rightClickedTrackIndex: -1,
listStyles: {},
};
},
computed: {
...mapState(['liked', 'player']),
isRightClickedTrackLiked() {
return this.liked.songs.includes(this.rightClickedTrack?.id);
},
rightClickedTrackComputed() {
return this.type === 'cloudDisk'
? {
id: 0,
name: '',
ar: [{ name: '' }],
al: { picUrl: '' },
}
: this.rightClickedTrack;
},
},
created() {
if (this.type === 'tracklist') {
this.listStyles = {
display: 'grid',
gap: '4px',
gridTemplateColumns: `repeat(${this.columnNumber}, 1fr)`,
};
}
},
methods: {
...mapMutations(['updateModal']),
...mapActions(['nextTrack', 'showToast', 'likeATrack']),
openMenu(e, track, index = -1) {
this.rightClickedTrack = track;
this.rightClickedTrackIndex = index;
this.$refs.menu.openMenu(e);
},
closeMenu() {
this.rightClickedTrack = {
id: 0,
name: '',
ar: [{ name: '' }],
al: { picUrl: '' },
};
this.rightClickedTrackIndex = -1;
},
playThisList(trackID) {
if (this.dbclickTrackFunc === 'default') {
this.playThisListDefault(trackID);
} else if (this.dbclickTrackFunc === 'none') {
// do nothing
} else if (this.dbclickTrackFunc === 'playTrackOnListByID') {
this.player.playTrackOnListByID(trackID);
} else if (this.dbclickTrackFunc === 'playPlaylistByID') {
this.player.playPlaylistByID(this.id, trackID);
} else if (this.dbclickTrackFunc === 'playAList') {
let trackIDs = this.tracks.map(t => t.id || t.songId);
this.player.replacePlaylist(trackIDs, this.id, 'artist', trackID);
} else if (this.dbclickTrackFunc === 'dailyTracks') {
let trackIDs = this.tracks.map(t => t.id);
this.player.replacePlaylist(trackIDs, '/daily/songs', 'url', trackID);
} else if (this.dbclickTrackFunc === 'playCloudDisk') {
let trackIDs = this.tracks.map(t => t.id || t.songId);
this.player.replacePlaylist(trackIDs, this.id, 'cloudDisk', trackID);
}
},
playThisListDefault(trackID) {
if (this.type === 'playlist') {
this.player.playPlaylistByID(this.id, trackID);
} else if (this.type === 'album') {
this.player.playAlbumByID(this.id, trackID);
} else if (this.type === 'tracklist') {
let trackIDs = this.tracks.map(t => t.id);
this.player.replacePlaylist(trackIDs, this.id, 'artist', trackID);
}
},
play() {
this.player.addTrackToPlayNext(this.rightClickedTrack.id, true);
},
addToQueue() {
this.player.addTrackToPlayNext(this.rightClickedTrack.id);
},
like() {
this.likeATrack(this.rightClickedTrack.id);
},
addTrackToPlaylist() {
if (!isAccountLoggedIn()) {
this.showToast(locale.t('toast.needToLogin'));
return;
}
this.updateModal({
modalName: 'addTrackToPlaylistModal',
key: 'show',
value: true,
});
this.updateModal({
modalName: 'addTrackToPlaylistModal',
key: 'selectedTrackID',
value: this.rightClickedTrack.id,
});
},
removeTrackFromPlaylist() {
if (!isAccountLoggedIn()) {
this.showToast(locale.t('toast.needToLogin'));
return;
}
if (confirm(`确定要从歌单删除 ${this.rightClickedTrack.name}`)) {
let trackID = this.rightClickedTrack.id;
addOrRemoveTrackFromPlaylist({
op: 'del',
pid: this.id,
tracks: trackID,
}).then(data => {
this.showToast(
data.body.code === 200
? locale.t('toast.removedFromPlaylist')
: data.body.message
);
this.$parent.removeTrack(trackID);
});
}
},
copyLink() {
navigator.clipboard.writeText(
`https://music.163.com/song?id=${this.rightClickedTrack.id}`
);
this.showToast(locale.t('toast.copied'));
},
removeTrackFromQueue() {
this.$store.state.player.removeTrackFromQueue(
this.rightClickedTrackIndex
);
},
removeTrackFromCloudDisk() {
if (confirm(`确定要从云盘删除 ${this.rightClickedTrack.songName}`)) {
let trackID = this.rightClickedTrack.songId;
cloudDiskTrackDelete(trackID).then(data => {
this.showToast(
data.code === 200 ? '已将此歌曲从云盘删除' : data.message
);
let newCloudDisk = this.liked.cloudDisk.filter(
t => t.songId !== trackID
);
this.$store.commit('updateLikedXXX', {
name: 'cloudDisk',
data: newCloudDisk,
});
});
}
},
},
};
</script>
<style lang="scss" scoped></style>