feat: support cloud disk

This commit is contained in:
qier222 2021-06-05 21:14:06 +08:00
parent 996904f056
commit 571d0d71f8
No known key found for this signature in database
GPG Key ID: 9C85007ED905F14D
10 changed files with 250 additions and 35 deletions

View File

@ -10,6 +10,7 @@ const table = {
'/album/detail/dynamic': require('../module/album_detail_dynamic'),
'/recommend/resource': require('../module/recommend_resource'),
'/playlist/subscribe': require('../module/playlist_subscribe'),
'/user/cloud/detail': require('../module/user_cloud_detail'),
'/playlist/catlist': require('../module/playlist_catlist'),
'/playlist/detail': require('../module/playlist_detail'),
'/login/cellphone': require('../module/login_cellphone'),
@ -17,6 +18,7 @@ const table = {
'/playlist/create': require('../module/playlist_create'),
'/playlist/tracks': require('../module/playlist_tracks'),
'/recommend/songs': require('../module/recommend_songs'),
'/user/cloud/del': require('../module/user_cloud_del'),
'/toplist/artist': require('../module/toplist_artist'),
'/artist/sublist': require('../module/artist_sublist'),
'/login/refresh': require('../module/login_refresh'),

View File

@ -95,6 +95,7 @@ export default {
this.$store.dispatch('fetchLikedAlbums');
this.$store.dispatch('fetchLikedArtists');
this.$store.dispatch('fetchLikedMVs');
this.$store.dispatch('fetchCloudDisk');
}
},
handleScroll() {

View File

@ -130,3 +130,69 @@ export function likedMVs() {
},
});
}
/**
* 上传歌曲到云盘需要登录
*/
export function uploadSong(file) {
let formData = new FormData();
formData.append('songFile', file);
return request({
url: '/cloud',
method: 'post',
params: {
timestamp: new Date().getTime(),
},
data: formData,
headers: {
'Content-Type': 'multipart/form-data',
},
});
}
/**
* 获取云盘歌曲需要登录
* 说明 : 登录后调用此接口 , 可获取云盘数据 , 获取的数据没有对应 url, 需要再调用一 /song/url 获取 url
* - limit : 返回数量 , 默认为 200
* - offset : 偏移数量用于分页 , :( 页数 -1)*200, 其中 200 limit 的值 , 默认为 0
* @param {Object} params
* @param {number} params.limit
* @param {number=} params.offset
*/
export function cloudDisk(params = {}) {
params.timestamp = new Date().getTime();
return request({
url: '/user/cloud',
method: 'get',
params,
});
}
/**
* 获取云盘歌曲详情需要登录
*/
export function cloudDiskTrackDetail(id) {
return request({
url: '/user/cloud/detail',
method: 'get',
params: {
timestamp: new Date().getTime(),
id,
},
});
}
/**
* 删除云盘歌曲需要登录
* @param {Array} id
*/
export function cloudDiskTrackDelete(id) {
return request({
url: '/user/cloud/del',
method: 'get',
params: {
timestamp: new Date().getTime(),
id,
},
});
}

View File

@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="arrow-up" class="svg-inline--fa fa-arrow-up fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M34.9 289.5l-22.2-22.2c-9.4-9.4-9.4-24.6 0-33.9L207 39c9.4-9.4 24.6-9.4 33.9 0l194.3 194.3c9.4 9.4 9.4 24.6 0 33.9L413 289.4c-9.5 9.5-25 9.3-34.3-.4L264 168.6V456c0 13.3-10.7 24-24 24h-32c-13.3 0-24-10.7-24-24V168.6L69.2 289.1c-9.3 9.8-24.8 10-34.3.4z"></path></svg>

After

Width:  |  Height:  |  Size: 487 B

View File

@ -240,6 +240,8 @@ export default {
this.$router.push({ path: '/library/liked-songs' });
} else if (this.player.playlistSource.type === 'url') {
this.$router.push({ path: this.player.playlistSource.id });
} else if (this.player.playlistSource.type === 'cloudDisk') {
this.$router.push({ path: '/library' });
} else {
this.$router.push({
path:

View File

@ -1,14 +1,14 @@
<template>
<div class="track-list">
<ContextMenu ref="menu">
<div class="item-info">
<img :src="rightClickedTrack.al.picUrl | resizeImage(224)" />
<div v-show="type !== 'cloudDisk'" class="item-info">
<img :src="rightClickedTrackComputed.al.picUrl | resizeImage(224)" />
<div class="info">
<div class="title">{{ rightClickedTrack.name }}</div>
<div class="subtitle">{{ rightClickedTrack.ar[0].name }}</div>
<div class="title">{{ rightClickedTrackComputed.name }}</div>
<div class="subtitle">{{ rightClickedTrackComputed.ar[0].name }}</div>
</div>
</div>
<hr />
<hr v-show="type !== 'cloudDisk'" />
<div class="item" @click="play">{{ $t('contextMenu.play') }}</div>
<div class="item" @click="addToQueue">{{
$t('contextMenu.addToQueue')
@ -19,11 +19,19 @@
@click="removeTrackFromQueue"
>从队列删除</div
>
<hr />
<div v-show="!isRightClickedTrackLiked" class="item" @click="like">
<hr v-show="type !== 'cloudDisk'" />
<div
v-show="!isRightClickedTrackLiked && type !== 'cloudDisk'"
class="item"
@click="like"
>
{{ $t('contextMenu.saveToMyLikedSongs') }}
</div>
<div v-show="isRightClickedTrackLiked" class="item" @click="like">
<div
v-show="isRightClickedTrackLiked && type !== 'cloudDisk'"
class="item"
@click="like"
>
{{ $t('contextMenu.removeFromMyLikedSongs') }}
</div>
<div
@ -32,17 +40,27 @@
@click="removeTrackFromPlaylist"
>从歌单中删除</div
>
<div class="item" @click="addTrackToPlaylist">{{
$t('contextMenu.addToPlaylist')
}}</div>
<div
v-show="type !== 'cloudDisk'"
class="item"
@click="addTrackToPlaylist"
>{{ $t('contextMenu.addToPlaylist') }}</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="track"
:track-prop="track"
:highlight-playing-track="highlightPlayingTrack"
@dblclick.native="playThisList(track.id)"
@dblclick.native="playThisList(track.id || track.songId)"
@click.right.native="openMenu($event, track, index)"
/>
</div>
@ -52,6 +70,7 @@
<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';
@ -65,9 +84,20 @@ export default {
ContextMenu,
},
props: {
tracks: Array,
type: String,
id: Number,
tracks: {
type: Array,
default: () => {
return [];
},
},
type: {
type: String,
default: 'tracklist',
}, // tracklist | album | playlist | cloudDisk
id: {
type: Number,
default: 0,
},
dbclickTrackFunc: {
type: String,
default: 'default',
@ -88,6 +118,7 @@ export default {
return [
// 'removeTrackFromPlaylist'
// 'removeTrackFromQueue'
// 'removeTrackFromCloudDisk'
];
},
},
@ -121,6 +152,16 @@ export default {
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') {
@ -158,11 +199,14 @@ export default {
} else if (this.dbclickTrackFunc === 'playPlaylistByID') {
this.player.playPlaylistByID(this.id, trackID);
} else if (this.dbclickTrackFunc === 'playAList') {
let trackIDs = this.tracks.map(t => t.id);
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) {
@ -226,6 +270,23 @@ export default {
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>

View File

@ -13,7 +13,7 @@
:class="{ hover: focus }"
@click="goToAlbum"
/>
<div v-if="isAlbum" class="no">
<div v-if="showOrderNumber" class="no">
<button v-show="focus && track.playable && !isPlaying" @click="playTrack">
<svg-icon
icon-class="play"
@ -37,7 +37,7 @@
<span v-if="isAlbum" class="featured">
<ArtistsInLine
:artists="track.ar"
:exclude="this.$parent.albumObject.artist.name"
:exclude="$parent.albumObject.artist.name"
prefix="-"
/></span>
<span v-if="isAlbum && track.mark === 1318912" class="explicit-symbol"
@ -58,11 +58,13 @@
</div>
<div></div>
</div>
<div v-if="!isTracklist && !isAlbum" class="album">
<div v-if="showAlbumName" class="album">
<router-link :to="`/album/${album.id}`">{{ album.name }}</router-link>
<div></div>
</div>
<div v-if="!isTracklist" class="actions">
<div v-if="showLikeButton" class="actions">
<button @click="likeThisSong">
<svg-icon
icon-class="heart"
@ -73,7 +75,7 @@
<svg-icon v-show="isLiked" icon-class="heart-solid"></svg-icon>
</button>
</div>
<div v-if="!isTracklist" class="time">
<div v-if="showTrackTime" class="time">
{{ track.dt | formatTime }}
</div>
</div>
@ -87,18 +89,26 @@ import { mapState } from 'vuex';
export default {
name: 'TrackListItem',
components: { ArtistsInLine, ExplicitSymbol },
props: {
track: Object,
trackProp: Object,
highlightPlayingTrack: {
type: Boolean,
default: true,
},
},
data() {
return { hover: false, trackStyle: {} };
},
computed: {
...mapState(['settings']),
track() {
return this.type === 'cloudDisk'
? this.trackProp.simpleSong
: this.trackProp;
},
imgUrl() {
let image =
this.track?.al?.picUrl ??
@ -112,7 +122,7 @@ export default {
return [];
},
album() {
return this.track.album || this.track.al;
return this.track.album || this.track.al || this.track?.simpleSong?.al;
},
translate() {
let t;
@ -134,17 +144,14 @@ export default {
this.track.alia?.length > 0
);
},
isTracklist() {
return this.type === 'tracklist';
},
isPlaylist() {
return this.type === 'playlist';
},
isLiked() {
return this.$parent.liked.songs.includes(this.track.id);
return this.$parent.liked.songs.includes(this.track?.id);
},
isPlaying() {
return this.$store.state.player.currentTrack.id === this.track.id;
return this.$store.state.player.currentTrack.id === this.track?.id;
},
trackClass() {
let trackClass = [this.type];
@ -169,7 +176,20 @@ export default {
? !this.$store.state.settings.enableUnblockNeteaseMusic
: true;
},
showLikeButton() {
return this.type !== 'tracklist' && this.type !== 'cloudDisk';
},
showOrderNumber() {
return this.type === 'album';
},
showAlbumName() {
return this.type !== 'album' && this.type !== 'tracklist';
},
showTrackTime() {
return this.type !== 'tracklist';
},
},
methods: {
goToAlbum() {
this.$router.push({ path: '/album/' + this.track.al.id });

View File

@ -9,6 +9,7 @@ import {
likedAlbums,
likedArtists,
likedMVs,
cloudDisk,
} from '@/api/user';
export default {
@ -147,4 +148,16 @@ export default {
}
});
},
fetchCloudDisk: ({ commit }) => {
if (!isAccountLoggedIn()) return;
return cloudDisk().then(result => {
console.log(result);
if (result.data) {
commit('updateLikedXXX', {
name: 'cloudDisk',
data: result.data,
});
}
});
},
};

View File

@ -19,6 +19,7 @@ export default {
albums: [],
artists: [],
mvs: [],
cloudDisk: [],
},
contextMenu: {
clickObjectID: 0,

View File

@ -80,14 +80,26 @@
>
{{ $t('library.mvs') }}
</div>
<div
class="tab"
:class="{ active: currentTab === 'cloudDisk' }"
@click="updateCurrentTab('cloudDisk')"
>
云盘
</div>
</div>
<button
v-show="currentTab === 'playlists'"
class="add-playlist"
icon="plus"
class="tab-button"
@click="openAddPlaylistModal"
><svg-icon icon-class="plus" />{{ $t('library.newPlayList') }}</button
>
><svg-icon icon-class="plus" />{{ $t('library.newPlayList') }}
</button>
<button
v-show="currentTab === 'cloudDisk'"
class="tab-button"
@click="selectUploadFiles"
><svg-icon icon-class="arrow-up-alt" /> 上传歌曲
</button>
</div>
<div v-show="currentTab === 'playlists'">
@ -121,8 +133,26 @@
<div v-show="currentTab === 'mvs'">
<MvRow :mvs="liked.mvs" />
</div>
<div v-show="currentTab === 'cloudDisk'">
<TrackList
:id="-8"
:tracks="liked.cloudDisk"
:column-number="3"
type="cloudDisk"
dbclick-track-func="playCloudDisk"
:extra-context-menu-item="['removeTrackFromCloudDisk']"
/>
</div>
</div>
<input
ref="cloudDiskUploadInput"
type="file"
style="display: none"
@change="uploadSongToCloudDisk"
/>
<ContextMenu>
<div class="item" @click="changePlaylistFilter('all')">{{
$t('contextMenu.allPlaylists')
@ -140,9 +170,10 @@
<script>
import { mapActions, mapMutations, mapState } from 'vuex';
import { getLyric } from '@/api/track';
import { randomNum, dailyTask } from '@/utils/common';
import { isAccountLoggedIn } from '@/utils/auth';
import { uploadSong } from '@/api/user';
import { getLyric } from '@/api/track';
import NProgress from 'nprogress';
import locale from '@/locale';
@ -227,6 +258,7 @@ export default {
this.$store.dispatch('fetchLikedAlbums');
this.$store.dispatch('fetchLikedArtists');
this.$store.dispatch('fetchLikedMVs');
this.$store.dispatch('fetchCloudDisk');
},
playLikedSongs() {
this.$store.state.player.playPlaylistByID(
@ -272,6 +304,22 @@ export default {
this.updateData({ key: 'libraryPlaylistFilter', value: type });
window.scrollTo({ top: 375, behavior: 'smooth' });
},
selectUploadFiles() {
this.$refs.cloudDiskUploadInput.click();
},
uploadSongToCloudDisk(e) {
const files = e.target.files;
uploadSong(files[0]).then(result => {
if (result.code === 200) {
let newCloudDisk = this.liked.cloudDisk;
newCloudDisk.unshift(result.privateCloud);
this.$store.commit('updateLikedXXX', {
name: 'cloudDisk',
data: newCloudDisk,
});
}
});
},
},
};
</script>
@ -427,7 +475,7 @@ h1 {
}
}
button.add-playlist {
button.tab-button {
color: var(--color-text);
border-radius: 8px;
padding: 0 14px;