Merge branch 'master' of github.com:qier222/YesPlayMusic

master
kunkka 4 years ago
commit 9409ae9c5c

@ -1,6 +1,6 @@
{ {
"name": "YesPlayMusic", "name": "YesPlayMusic",
"version": "0.1.0", "version": "0.2.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",

@ -37,3 +37,34 @@ export function newAlbums(params) {
params, params,
}); });
} }
/**
* 专辑动态信息
* 说明 : 调用此接口 , 传入专辑 id, 可获得专辑动态信息,如是否收藏,收藏数,评论数,分享数
* - id - 专辑id
* @param {number} id
*/
export function albumDynamicDetail(id) {
return request({
url: "/album/detail/dynamic",
method: "get",
params: { id, timestamp: new Date().getTime() },
});
}
/**
* 收藏/取消收藏专辑
* 说明 : 调用此接口,可收藏/取消收藏专辑
* - id - 返专辑 id
* - t - 1 为收藏,其他为取消收藏
* @param {Object} params
* @param {number} params.id
* @param {number} params.t
*/
export function likeAAlbum(params) {
return request({
url: "/album/sub",
method: "post",
params,
});
}

@ -12,6 +12,7 @@ export function getArtist(id) {
method: "get", method: "get",
params: { params: {
id, id,
timestamp: new Date().getTime(),
}, },
}).then((data) => { }).then((data) => {
data.hotSongs = mapTrackPlayableStatus(data.hotSongs); data.hotSongs = mapTrackPlayableStatus(data.hotSongs);
@ -71,3 +72,20 @@ export function artistMv(id) {
}, },
}); });
} }
/**
* 收藏歌手
* 说明 : 调用此接口 , 传入歌手 id, 可收藏歌手
* - id: 歌手 id
* - t: 操作,1 为收藏,其他为取消收藏
* @param {Object} params
* @param {number} params.id
* @param {number} params.t
*/
export function followAArtist(params) {
return request({
url: "/artist/sub",
method: "post",
params,
});
}

@ -36,7 +36,7 @@ export function userPlaylist(params) {
} }
/** /**
* 喜欢音乐列表 * 喜欢音乐列表需要登录
* 说明 : 调用此接口 , 传入用户 id, 可获取已喜欢音乐id列表(id数组) * 说明 : 调用此接口 , 传入用户 id, 可获取已喜欢音乐id列表(id数组)
* - uid: 用户 id * - uid: 用户 id
* @param {number} uid * @param {number} uid
@ -52,8 +52,13 @@ export function userLikedSongsIDs(uid) {
}); });
} }
/**
* 每日签到
* 说明 : 调用此接口可签到获取积分
* - type: 签到类型 , 默认 0, 其中 0 为安卓端签到 ,1 web/PC 签到
* @param {number} type
*/
export function dailySignin(type = 0) { export function dailySignin(type = 0) {
//可选参数 : type: 签到类型 , 默认 0, 其中 0 为安卓端签到 ,1 为 web/PC 签到
return request({ return request({
url: "/daily_signin", url: "/daily_signin",
method: "post", method: "post",
@ -62,3 +67,50 @@ export function dailySignin(type = 0) {
}, },
}); });
} }
/**
* 获取收藏的专辑需要登录
* 说明 : 调用此接口可获取到用户收藏的专辑
* - limit : 返回数量 , 默认为 30
* - offset : 偏移数量用于分页 , :( 页数 -1)*30, 其中 30 limit 的值 , 默认为 0
* @param {Object} params
* @param {number} params.limit
* @param {number=} params.offset
*/
export function likedAlbums() {
return request({
url: "/album/sublist",
method: "get",
params: {
timestamp: new Date().getTime(),
},
});
}
/**
* 获取收藏的歌手需要登录
* 说明 : 调用此接口可获取到用户收藏的歌手
*/
export function likedArtists() {
return request({
url: "/artist/sublist",
method: "get",
params: {
timestamp: new Date().getTime(),
},
});
}
/**
* 获取收藏的MV需要登录
* 说明 : 调用此接口可获取到用户收藏的MV
*/
export function likedMVs() {
return request({
url: "/mv/sublist",
method: "get",
params: {
timestamp: new Date().getTime(),
},
});
}

@ -49,10 +49,13 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
button { button {
height: 40px;
min-width: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 18px; font-size: 18px;
line-height: 18px;
font-weight: 600; font-weight: 600;
background-color: var(--color-primary-bg); background-color: var(--color-primary-bg);
color: var(--color-primary); color: var(--color-primary);

@ -76,8 +76,9 @@ export default {
created() { created() {
this.shadowStyle = { this.shadowStyle = {
height: `${this.size}px`, height: `${this.size}px`,
width: `${this.size}px`, width: `${~~(this.size * 0.96)}px`,
top: `${this.shadowMargin}px`, top: `${this.shadowMargin}px`,
right: `${~~(this.size * 0.02)}px`,
borderRadius: `${this.radius}px`, borderRadius: `${this.radius}px`,
}; };
this.playButtonStyle = { this.playButtonStyle = {
@ -165,6 +166,13 @@ export default {
height: 208px; height: 208px;
transform: scale(0.98); transform: scale(0.98);
} }
[data-theme="dark"] {
.shadow {
filter: blur(16px) brightness(68%);
transform: scale(0.96);
}
}
.play-button { .play-button {
opacity: 0; opacity: 0;
display: flex; display: flex;

@ -95,8 +95,12 @@ export default {
if (this.subText === "creator") return "by " + item.creator.nickname; if (this.subText === "creator") return "by " + item.creator.nickname;
if (this.subText === "releaseYear") if (this.subText === "releaseYear")
return new Date(item.publishTime).getFullYear(); return new Date(item.publishTime).getFullYear();
if (this.subText === "artist") if (this.subText === "artist") {
if (item.artist !== undefined)
return `<a href="/#/artist/${item.artist.id}">${item.artist.name}</a>`; return `<a href="/#/artist/${item.artist.id}">${item.artist.name}</a>`;
if (item.artists !== undefined)
return `<a href="/#/artist/${item.artists[0].id}">${item.artists[0].name}</a>`;
}
if (this.subText === "albumType+releaseYear") { if (this.subText === "albumType+releaseYear") {
let albumType = item.type; let albumType = item.type;
if (item.type === "EP/Single") { if (item.type === "EP/Single") {

@ -1,34 +1,27 @@
<template> <template>
<div class="mv-row"> <div class="mv-row">
<div class="mv" v-for="mv in mvs" :key="mv.id"> <div class="mv" v-for="mv in mvs" :key="getID(mv)">
<div class="cover-container"> <div class="cover-container">
<img <img
class="cover" class="cover"
:src="getUrl(mv)" :src="getUrl(mv)"
@mouseover="hoverVideoID = mv.id" @mouseover="hoverVideoID = getID(mv)"
@mouseleave="hoverVideoID = 0" @mouseleave="hoverVideoID = 0"
@click="goToMv(mv.id)" @click="goToMv(getID(mv))"
/> />
<transition name="fade"> <transition name="fade">
<img <img
class="shadow" class="shadow"
v-show="hoverVideoID === mv.id" v-show="hoverVideoID === getID(mv)"
:src="getUrl(mv)" :src="getUrl(mv)"
/> />
</transition> </transition>
</div> </div>
<div class="info"> <div class="info">
<div class="title"> <div class="title">
<router-link :to="'/mv/' + mv.id">{{ mv.name }}</router-link> <router-link :to="'/mv/' + getID(mv)">{{ getTitle(mv) }}</router-link>
</div>
<div class="artist">
<router-link
v-if="subtitle === 'artist'"
:to="'/artist/' + mv.artistId"
>{{ mv.artistName }}</router-link
>
<span v-if="subtitle === 'publishTime'">{{ mv.publishTime }}</span>
</div> </div>
<div class="artist" v-html="getSubtitle(mv)"></div>
</div> </div>
</div> </div>
</div> </div>
@ -62,6 +55,30 @@ export default {
if (mv.imgurl16v9 !== undefined) return mv.imgurl16v9; if (mv.imgurl16v9 !== undefined) return mv.imgurl16v9;
if (mv.coverUrl !== undefined) return mv.coverUrl; if (mv.coverUrl !== undefined) return mv.coverUrl;
}, },
getID(mv) {
if (mv.id !== undefined) return mv.id;
if (mv.vid !== undefined) return mv.vid;
},
getTitle(mv) {
if (mv.name !== undefined) return mv.name;
if (mv.title !== undefined) return mv.title;
},
getSubtitle(mv) {
if (this.subtitle === "artist") {
let artistName = "null";
let artistID = 0;
if (mv.artistName !== undefined) {
artistName = mv.artistName;
artistID = mv.artistId;
} else if (mv.creator !== undefined) {
artistName = mv.creator[0].userName;
artistID = mv.creator[0].userId;
}
return `<a href="/artist/${artistID}">${artistName}</a>`;
} else if (this.subtitle === "publishTime") {
return mv.publishTime;
}
},
}, },
}; };
</script> </script>

@ -137,8 +137,7 @@ nav {
left: 12px; left: 12px;
} }
&:hover { &:hover {
background: var(--color-primary-bg); background: var(--color-secondary-bg);
color: var(--color-primary);
} }
&:active { &:active {
transform: scale(0.92); transform: scale(0.92);

@ -146,13 +146,13 @@ export default {
this.progress = ~~this.howler.seek(); this.progress = ~~this.howler.seek();
}, 1000); }, 1000);
if (isAccountLoggedIn()) { if (isAccountLoggedIn()) {
userLikedSongsIDs(this.settings.user.userId).then((data) => { userLikedSongsIDs(this.data.user.userId).then((data) => {
this.updateLikedSongs(data.ids); this.updateLikedSongs(data.ids);
}); });
} }
}, },
computed: { computed: {
...mapState(["player", "howler", "settings", "liked", "accountLogin"]), ...mapState(["player", "howler", "settings", "liked", "data"]),
currentTrack() { currentTrack() {
return this.player.currentTrack; return this.player.currentTrack;
}, },
@ -175,6 +175,9 @@ export default {
let max = ~~(this.currentTrack.dt / 1000); let max = ~~(this.currentTrack.dt / 1000);
return max > 1 ? max - 1 : max; return max > 1 ? max - 1 : max;
}, },
accountLogin() {
return isAccountLoggedIn();
},
}, },
methods: { methods: {
...mapMutations([ ...mapMutations([
@ -266,7 +269,7 @@ export default {
}); });
}, },
goToList() { goToList() {
if (this.player.listInfo.id === this.settings.user.likedSongPlaylistID) if (this.player.listInfo.id === this.data.likedSongPlaylistID)
this.$router.push({ path: "/library/liked-songs" }); this.$router.push({ path: "/library/liked-songs" });
else else
this.$router.push({ this.$router.push({

@ -3,10 +3,18 @@
<ContextMenu ref="menu"> <ContextMenu ref="menu">
<div class="item" @click="play">Play</div> <div class="item" @click="play">Play</div>
<div class="item" @click="playNext">Play Next</div> <div class="item" @click="playNext">Play Next</div>
<div class="item" @click="like" v-show="!isRightClickedTrackLiked"> <div
class="item"
@click="like"
v-show="!isRightClickedTrackLiked && accountLogin"
>
Save to my Liked Songs Save to my Liked Songs
</div> </div>
<div class="item" @click="like" v-show="isRightClickedTrackLiked"> <div
class="item"
@click="like"
v-show="isRightClickedTrackLiked && accountLogin"
>
Remove from my Liked Songs Remove from my Liked Songs
</div> </div>
</ContextMenu> </ContextMenu>
@ -29,6 +37,7 @@ import {
playAList, playAList,
appendTrackToPlayerList, appendTrackToPlayerList,
} from "@/utils/play"; } from "@/utils/play";
import { isAccountLoggedIn } from "@/utils/auth";
import TrackListItem from "@/components/TrackListItem.vue"; import TrackListItem from "@/components/TrackListItem.vue";
import ContextMenu from "@/components/ContextMenu.vue"; import ContextMenu from "@/components/ContextMenu.vue";
@ -67,6 +76,9 @@ export default {
isRightClickedTrackLiked() { isRightClickedTrackLiked() {
return this.liked.songs.includes(this.rightClickedTrack?.id); return this.liked.songs.includes(this.rightClickedTrack?.id);
}, },
accountLogin() {
return isAccountLoggedIn();
},
}, },
methods: { methods: {
...mapMutations(["updateLikedSongs"]), ...mapMutations(["updateLikedSongs"]),

@ -78,7 +78,7 @@
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { isAccountLoggedIn } from "@/utils/auth";
import ArtistsInLine from "@/components/ArtistsInLine.vue"; import ArtistsInLine from "@/components/ArtistsInLine.vue";
import ExplicitSymbol from "@/components/ExplicitSymbol.vue"; import ExplicitSymbol from "@/components/ExplicitSymbol.vue";
@ -93,7 +93,6 @@ export default {
return { focus: false, trackStyle: {} }; return { focus: false, trackStyle: {} };
}, },
computed: { computed: {
...mapState(["accountLogin"]),
imgUrl() { imgUrl() {
if (this.track.al !== undefined) return this.track.al.picUrl; if (this.track.al !== undefined) return this.track.al.picUrl;
if (this.track.album !== undefined) return this.track.album.picUrl; if (this.track.album !== undefined) return this.track.album.picUrl;
@ -128,6 +127,9 @@ export default {
if (this.isPlaying) trackClass.push("playing"); if (this.isPlaying) trackClass.push("playing");
return trackClass; return trackClass;
}, },
accountLogin() {
return isAccountLoggedIn();
},
}, },
methods: { methods: {
goToAlbum() { goToAlbum() {

@ -118,7 +118,7 @@ const router = new VueRouter({
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 需要登录的逻辑 // 需要登录的逻辑
if (to.meta.requireLogin) { if (to.meta.requireLogin) {
if (store.state.settings.user.nickname === undefined) { if (store.state.data.user.nickname === undefined) {
next({ path: "/login" }); next({ path: "/login" });
} }
if (isLooseLoggedIn()) { if (isLooseLoggedIn()) {

@ -3,22 +3,28 @@ import Vuex from "vuex";
import state from "./state"; import state from "./state";
import mutations from "./mutations"; import mutations from "./mutations";
import actions from "./actions"; import actions from "./actions";
import initState from "./initState"; import initLocalStorage from "./initLocalStorage";
import { Howl, Howler } from "howler"; import { Howl, Howler } from "howler";
import { changeAppearance } from "@/utils/common"; import { changeAppearance } from "@/utils/common";
import updateApp from "@/utils/updateApp";
import pack from "../../package.json";
if (localStorage.getItem("appVersion") === null) { if (localStorage.getItem("appVersion") === null) {
localStorage.setItem("player", JSON.stringify(initState.player)); localStorage.setItem("player", JSON.stringify(initLocalStorage.player));
localStorage.setItem("settings", JSON.stringify(initState.settings)); localStorage.setItem("settings", JSON.stringify(initLocalStorage.settings));
localStorage.setItem("appVersion", "0.1"); localStorage.setItem("data", JSON.stringify(initLocalStorage.data));
localStorage.setItem("appVersion", pack.version);
window.location.reload(); window.location.reload();
} }
updateApp();
const saveToLocalStorage = (store) => { const saveToLocalStorage = (store) => {
store.subscribe((mutation, state) => { store.subscribe((mutation, state) => {
// console.log(mutation); // console.log(mutation);
localStorage.setItem("player", JSON.stringify(state.player)); localStorage.setItem("player", JSON.stringify(state.player));
localStorage.setItem("settings", JSON.stringify(state.settings)); localStorage.setItem("settings", JSON.stringify(state.settings));
localStorage.setItem("data", JSON.stringify(state.data));
}); });
}; };

@ -1,14 +1,4 @@
const initState = { export default {
howler: null,
accountLogin: false,
usernameLogin: false,
liked: {
songs: [],
},
contextMenu: {
clickObjectID: 0,
showMenu: false,
},
player: { player: {
enable: false, enable: false,
show: true, show: true,
@ -83,16 +73,16 @@ const initState = {
enable: true, enable: true,
}, },
], ],
user: {
id: 0,
},
lang: null, lang: null,
appearance: "auto", appearance: "auto",
musicQuality: 320000, musicQuality: 320000,
showGithubIcon: true, showGithubIcon: true,
showPlaylistsByAppleMusic: true, showPlaylistsByAppleMusic: true,
},
data: {
user: {},
likedSongPlaylistID: 0,
lastRefreshCookieDate: 0, lastRefreshCookieDate: 0,
loginMode: null,
}, },
}; };
export default initState;

@ -81,22 +81,6 @@ export default {
return track; return track;
}); });
}, },
updateAccountLogin(state, status) {
state.accountLogin = status;
},
updateUsernameLogin(state, status) {
state.usernameLogin = status;
},
updateLogout() {
this.commit("updateAccountLogin", false);
this.commit("updateUsernameLogin", false);
},
updateUser(state, user) {
state.settings.user = user;
},
updateUserInfo(state, { key, value }) {
state.settings.user[key] = value;
},
updateLikedSongs(state, trackIDs) { updateLikedSongs(state, trackIDs) {
state.liked.songs = trackIDs; state.liked.songs = trackIDs;
}, },
@ -119,4 +103,7 @@ export default {
updateSettings(state, { key, value }) { updateSettings(state, { key, value }) {
state.settings[key] = value; state.settings[key] = value;
}, },
updateData(state, { key, value }) {
state.data[key] = value;
},
}; };

@ -1,7 +1,5 @@
export default { export default {
howler: null, howler: null,
accountLogin: false,
usernameLogin: false,
liked: { liked: {
songs: [], songs: [],
}, },
@ -11,4 +9,5 @@ export default {
}, },
player: JSON.parse(localStorage.getItem("player")), player: JSON.parse(localStorage.getItem("player")),
settings: JSON.parse(localStorage.getItem("settings")), settings: JSON.parse(localStorage.getItem("settings")),
data: JSON.parse(localStorage.getItem("data")),
}; };

@ -4,14 +4,12 @@ import store from "@/store";
export function doLogout() { export function doLogout() {
logout(); logout();
// 移除前端本地用来认证登录的字段
Cookies.remove("loginMode");
// 网易云的接口会自动移除该 cookies // 网易云的接口会自动移除该 cookies
// Cookies.remove("MUSIC_U"); // Cookies.remove("MUSIC_U");
// 更新状态仓库中的用户信息 // 更新状态仓库中的用户信息
store.commit("updateUser", { id: 0 }); store.commit("updateData", { key: "user", value: {} });
// 更新状态仓库中的登录状态 // 更新状态仓库中的登录状态
store.commit("updateLogout"); store.commit("updateData", { key: "loginMode", value: null });
} }
// MUSIC_U 只有在账户登录的情况下才有 // MUSIC_U 只有在账户登录的情况下才有
@ -23,16 +21,13 @@ export function isLoggedIn() {
export function isAccountLoggedIn() { export function isAccountLoggedIn() {
return ( return (
Cookies.get("MUSIC_U") !== undefined && Cookies.get("MUSIC_U") !== undefined &&
Cookies.get("loginMode") === "account" store.state.data.loginMode === "account"
); );
} }
// 用户名搜索(用户数据为只读) // 用户名搜索(用户数据为只读)
export function isUsernameLoggedIn() { export function isUsernameLoggedIn() {
return ( return store.state.data.loginMode === "username";
Cookies.get("MUSIC_U") === undefined &&
Cookies.get("loginMode") === "username"
);
} }
// 账户登录或者用户名搜索都判断为登录,宽松检查 // 账户登录或者用户名搜索都判断为登录,宽松检查

@ -10,7 +10,7 @@ export function isTrackPlayable(track) {
reason: "", reason: "",
}; };
if (track.fee === 1 || track.privilege?.fee === 1) { if (track.fee === 1 || track.privilege?.fee === 1) {
if (isAccountLoggedIn() && store.state.settings.user.vipType === 11) { if (isAccountLoggedIn() && store.state.data.user.vipType === 11) {
result.playable = true; result.playable = true;
} else { } else {
result.playable = false; result.playable = false;
@ -80,13 +80,13 @@ export function updateHttps(url) {
} }
export function dailyTask() { export function dailyTask() {
let lastDate = store.state.settings.lastRefreshCookieDate; let lastDate = store.state.data.lastRefreshCookieDate;
if ( if (
isAccountLoggedIn() && isAccountLoggedIn() &&
(lastDate === undefined || lastDate !== dayjs().date()) (lastDate === undefined || lastDate !== dayjs().date())
) { ) {
console.log("execute dailyTask"); console.log("execute dailyTask");
store.commit("updateSettings", { store.commit("updateData", {
key: "lastRefreshCookieDate", key: "lastRefreshCookieDate",
value: dayjs().date(), value: dayjs().date(),
}); });

@ -0,0 +1,28 @@
import pack from "../../package.json";
function updateTo_0_2_0() {
// 0.1 to 0.2.0
// 移动 settings 内的 user 数据到 data
let settings = JSON.parse(localStorage.getItem("settings"));
let data = {
likedSongPlaylistID: settings.user.likedSongPlaylistID,
lastRefreshCookieDate: settings.lastRefreshCookieDate,
};
delete settings.user.likedSongPlaylistID;
delete settings.lastRefreshCookieDate;
data.user = settings.user;
delete settings.user;
localStorage.setItem("settings", JSON.stringify(settings));
localStorage.setItem("data", JSON.stringify(data));
localStorage.setItem("appVersion", "0.2.0");
window.location.reload();
}
export default function () {
while (localStorage.getItem("appVersion") !== pack.version) {
let currentVersion = localStorage.getItem("appVersion");
if (currentVersion === "0.1") {
updateTo_0_2_0();
}
}
}

@ -41,6 +41,15 @@
> >
{{ $t("play") }} {{ $t("play") }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone
v-if="accountLogin"
shape="round"
:iconClass="dynamicDetail.isSub ? 'heart-solid' : 'heart'"
:iconButton="true"
:horizontalPadding="0"
@click.native="likeAlbum"
>
</ButtonTwoTone>
</div> </div>
</div> </div>
</div> </div>
@ -93,8 +102,9 @@ import { mapMutations, mapActions, mapState } from "vuex";
import { getArtistAlbum } from "@/api/artist"; import { getArtistAlbum } from "@/api/artist";
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from "@/api/track";
import { playAlbumByID } from "@/utils/play"; import { playAlbumByID } from "@/utils/play";
import { getAlbum } from "@/api/album"; import { getAlbum, albumDynamicDetail, likeAAlbum } from "@/api/album";
import NProgress from "nprogress"; import NProgress from "nprogress";
import { isAccountLoggedIn } from "@/utils/auth";
import ExplicitSymbol from "@/components/ExplicitSymbol.vue"; import ExplicitSymbol from "@/components/ExplicitSymbol.vue";
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from "@/components/ButtonTwoTone.vue";
@ -124,18 +134,22 @@ export default {
showFullDescription: false, showFullDescription: false,
show: false, show: false,
moreAlbums: [], moreAlbums: [],
dynamicDetail: {},
}; };
}, },
created() { created() {
this.loadData(this.$route.params.id); this.loadData(this.$route.params.id);
}, },
computed: { computed: {
...mapState(["player"]), ...mapState(["player", "data"]),
albumTime() { albumTime() {
let time = 0; let time = 0;
this.tracks.map((t) => (time = time + t.dt)); this.tracks.map((t) => (time = time + t.dt));
return time; return time;
}, },
accountLogin() {
return isAccountLoggedIn();
},
filteredMoreAlbums() { filteredMoreAlbums() {
let moreAlbums = this.moreAlbums.filter((a) => a.id !== this.album.id); let moreAlbums = this.moreAlbums.filter((a) => a.id !== this.album.id);
let realAlbums = moreAlbums.filter((a) => a.type === "专辑"); let realAlbums = moreAlbums.filter((a) => a.type === "专辑");
@ -159,6 +173,15 @@ export default {
} }
playAlbumByID(id, trackID); playAlbumByID(id, trackID);
}, },
likeAlbum() {
likeAAlbum({
id: this.album.id,
t: this.dynamicDetail.isSub ? 0 : 1,
}).then((data) => {
if (data.code === 200)
this.dynamicDetail.isSub = !this.dynamicDetail.isSub;
});
},
loadData(id) { loadData(id) {
getAlbum(id).then((data) => { getAlbum(id).then((data) => {
this.album = data.album; this.album = data.album;
@ -179,6 +202,9 @@ export default {
} }
); );
}); });
albumDynamicDetail(id).then((data) => {
this.dynamicDetail = data;
});
}, },
}, },
beforeRouteUpdate(to, from, next) { beforeRouteUpdate(to, from, next) {
@ -235,6 +261,13 @@ export default {
opacity: 0.88; opacity: 0.88;
} }
} }
.buttons {
margin-top: 32px;
display: flex;
button {
margin-right: 16px;
}
}
} }
} }

@ -16,6 +16,10 @@
<ButtonTwoTone @click.native="playPopularSongs()" :iconClass="`play`"> <ButtonTwoTone @click.native="playPopularSongs()" :iconClass="`play`">
{{ $t("play") }} {{ $t("play") }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone @click.native="followArtist" color="grey">
<span v-if="artist.followed">Following</span>
<span v-else>Follow</span>
</ButtonTwoTone>
</div> </div>
</div> </div>
</div> </div>
@ -94,7 +98,12 @@
<script> <script>
import { mapMutations, mapActions, mapState } from "vuex"; import { mapMutations, mapActions, mapState } from "vuex";
import { getArtist, getArtistAlbum, artistMv } from "@/api/artist"; import {
getArtist,
getArtistAlbum,
artistMv,
followAArtist,
} from "@/api/artist";
import { playAList } from "@/utils/play"; import { playAList } from "@/utils/play";
import NProgress from "nprogress"; import NProgress from "nprogress";
@ -168,6 +177,14 @@ export default {
let trackIDs = this.popularTracks.map((t) => t.id); let trackIDs = this.popularTracks.map((t) => t.id);
playAList(trackIDs, this.artist.id, "artist", trackID); playAList(trackIDs, this.artist.id, "artist", trackID);
}, },
followArtist() {
followAArtist({
id: this.artist.id,
t: this.artist.followed ? 0 : 1,
}).then((data) => {
if (data.code === 200) this.artist.followed = !this.artist.followed;
});
},
}, },
created() { created() {
this.loadData(this.$route.params.id); this.loadData(this.$route.params.id);

@ -40,9 +40,40 @@
</div> </div>
</div> </div>
<div class="playlists" v-if="playlists.length > 1"> <div class="section-two" id="liked">
<div class="title">{{ $t("playlist.playlist") }}</div> <div class="tabs">
<div> <div
class="tab"
:class="{ active: currentTab === 'playlists' }"
@click="updateCurrentTab('playlists')"
>
Playlists
</div>
<div
class="tab"
:class="{ active: currentTab === 'albums' }"
@click="updateCurrentTab('albums')"
>
Albums
</div>
<div
class="tab"
:class="{ active: currentTab === 'artists' }"
@click="updateCurrentTab('artists')"
>
Artists
</div>
<div
class="tab"
:class="{ active: currentTab === 'mvs' }"
@click="updateCurrentTab('mvs')"
>
MVs
</div>
</div>
<div v-show="currentTab === 'playlists'">
<div v-if="playlists.length > 1">
<CoverRow <CoverRow
:items="playlists.slice(1)" :items="playlists.slice(1)"
type="playlist" type="playlist"
@ -51,13 +82,37 @@
/> />
</div> </div>
</div> </div>
<div v-show="currentTab === 'albums'">
<CoverRow
:items="albums"
type="album"
subText="artist"
:showPlayButton="true"
/>
</div>
<div v-show="currentTab === 'artists'">
<CoverRow :items="artists" type="artist" :showPlayButton="true" />
</div>
<div v-show="currentTab === 'mvs'">
<MvRow :mvs="mvs" />
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getTrackDetail, getLyric } from "@/api/track"; import { getTrackDetail, getLyric } from "@/api/track";
import { userDetail, userPlaylist } from "@/api/user"; import {
userDetail,
userPlaylist,
likedAlbums,
likedArtists,
likedMVs,
} from "@/api/user";
import { randomNum, dailyTask } from "@/utils/common"; import { randomNum, dailyTask } from "@/utils/common";
import { getPlaylistDetail } from "@/api/playlist"; import { getPlaylistDetail } from "@/api/playlist";
import { playPlaylistByID } from "@/utils/play"; import { playPlaylistByID } from "@/utils/play";
@ -65,11 +120,12 @@ import NProgress from "nprogress";
import TrackList from "@/components/TrackList.vue"; import TrackList from "@/components/TrackList.vue";
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from "@/components/CoverRow.vue";
import MvRow from "@/components/MvRow.vue";
import SvgIcon from "@/components/SvgIcon.vue"; import SvgIcon from "@/components/SvgIcon.vue";
export default { export default {
name: "Library", name: "Library",
components: { SvgIcon, CoverRow, TrackList }, components: { SvgIcon, CoverRow, TrackList, MvRow },
data() { data() {
return { return {
show: false, show: false,
@ -88,11 +144,15 @@ export default {
likedSongs: [], likedSongs: [],
likedSongIDs: [], likedSongIDs: [],
lyric: undefined, lyric: undefined,
currentTab: "playlists",
albums: [],
artists: [],
mvs: [],
}; };
}, },
created() { created() {
NProgress.start(); NProgress.start();
userDetail(this.settings.user.userId).then((data) => { userDetail(this.data.user.userId).then((data) => {
this.user = data; this.user = data;
}); });
}, },
@ -101,7 +161,7 @@ export default {
dailyTask(); dailyTask();
}, },
computed: { computed: {
...mapState(["settings"]), ...mapState(["data"]),
likedSongsInState() { likedSongsInState() {
return this.$store.state.liked.songs; return this.$store.state.liked.songs;
}, },
@ -129,18 +189,38 @@ export default {
playLikedSongs() { playLikedSongs() {
playPlaylistByID(this.playlists[0].id, "first", true); playPlaylistByID(this.playlists[0].id, "first", true);
}, },
updateCurrentTab(tab) {
this.currentTab = tab;
document
.getElementById("liked")
.scrollIntoView({ block: "start", behavior: "smooth" });
if (tab === "albums") {
if (this.albums.length === 0) this.loadLikedAlbums();
} else if (tab === "artists") {
if (this.artists.length === 0) this.loadLikedArtists();
} else if (tab === "mvs") {
if (this.mvs.length === 0) this.loadLikedMVs();
}
},
goToLikedSongsList() { goToLikedSongsList() {
this.$router.push({ path: "/library/liked-songs" }); this.$router.push({ path: "/library/liked-songs" });
}, },
loadData() { loadData() {
if (this.hasMorePlaylists) { if (this.hasMorePlaylists && this.currentTab === "playlists") {
this.getUserPlaylists(); this.getUserPlaylists();
} }
if (this.currentTab === "albums") {
this.loadLikedAlbums();
} else if (this.currentTab === "artists") {
this.loadLikedArtists();
} else if (this.currentTab === "mvs") {
this.loadLikedMVs();
}
this.getLikedSongs(); this.getLikedSongs();
}, },
getUserPlaylists(replace = false) { getUserPlaylists(replace = false) {
userPlaylist({ userPlaylist({
uid: this.settings.user.userId, uid: this.data.user.userId,
offset: this.playlists.length === 0 ? 0 : this.playlists.length - 1, offset: this.playlists.length === 0 ? 0 : this.playlists.length - 1,
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
}).then((data) => { }).then((data) => {
@ -153,8 +233,7 @@ export default {
}); });
}, },
getLikedSongs(getLyric = true) { getLikedSongs(getLyric = true) {
getPlaylistDetail(this.settings.user.likedSongPlaylistID, true).then( getPlaylistDetail(this.data.likedSongPlaylistID, true).then((data) => {
(data) => {
this.likedSongsPlaylist = data.playlist; this.likedSongsPlaylist = data.playlist;
let TrackIDs = data.playlist.trackIds.slice(0, 20).map((t) => t.id); let TrackIDs = data.playlist.trackIds.slice(0, 20).map((t) => t.id);
this.likedSongIDs = TrackIDs; this.likedSongIDs = TrackIDs;
@ -164,8 +243,7 @@ export default {
this.show = true; this.show = true;
}); });
if (getLyric) this.getRandomLyric(); if (getLyric) this.getRandomLyric();
} });
);
}, },
getRandomLyric() { getRandomLyric() {
getLyric( getLyric(
@ -174,6 +252,21 @@ export default {
if (data.lrc !== undefined) this.lyric = data.lrc.lyric; if (data.lrc !== undefined) this.lyric = data.lrc.lyric;
}); });
}, },
loadLikedAlbums() {
likedAlbums().then((data) => {
this.albums = data.data;
});
},
loadLikedArtists() {
likedArtists().then((data) => {
this.artists = data.data;
});
},
loadLikedMVs() {
likedMVs().then((data) => {
this.mvs = data.data;
});
},
}, },
watch: { watch: {
likedSongsInState() { likedSongsInState() {
@ -280,14 +373,37 @@ h1 {
} }
} }
.playlists { .section-two {
// margin-top: 42px;
// padding-top: 14px;
// border-top: 1px solid rgba(128, 128, 128, 0.18);
margin-top: 54px; margin-top: 54px;
.title { min-height: calc(100vh - 182px);
}
.tabs {
display: flex;
flex-wrap: wrap;
font-size: 18px;
color: var(--color-text); color: var(--color-text);
opacity: 0.88; margin-bottom: 6px;
margin-bottom: 8px; .tab {
font-size: 24px;
font-weight: 600; font-weight: 600;
padding: 8px 14px;
margin: 10px 14px 6px 0;
border-radius: 8px;
cursor: pointer;
user-select: none;
transition: 0.2s;
opacity: 0.68;
&:hover {
opacity: 0.88;
background-color: var(--color-secondary-bg);
}
}
.tab.active {
opacity: 0.88;
background-color: var(--color-secondary-bg);
} }
} }
</style> </style>

@ -89,7 +89,6 @@ import { loginWithPhone, loginWithEmail } from "@/api/auth";
import md5 from "crypto-js/md5"; import md5 from "crypto-js/md5";
import { mapMutations } from "vuex"; import { mapMutations } from "vuex";
import { userPlaylist } from "@/api/user"; import { userPlaylist } from "@/api/user";
import Cookies from "js-cookie";
export default { export default {
name: "Login", name: "Login",
@ -112,15 +111,14 @@ export default {
NProgress.done(); NProgress.done();
}, },
methods: { methods: {
...mapMutations(["updateUser", "updateUserInfo", "updateAccountLogin"]), ...mapMutations(["updateData"]),
afterLogin() { afterLogin() {
this.updateAccountLogin(true); this.updateData({ key: "loginMode", value: "account" });
Cookies.set("loginMode", "account", { expires: 3650 });
userPlaylist({ userPlaylist({
uid: this.$store.state.settings.user.userId, uid: this.$store.state.data.user.userId,
limit: 1, limit: 1,
}).then((data) => { }).then((data) => {
this.updateUserInfo({ this.updateData({
key: "likedSongPlaylistID", key: "likedSongPlaylistID",
value: data.playlist[0].id, value: data.playlist[0].id,
}); });
@ -163,7 +161,7 @@ export default {
}) })
.then((data) => { .then((data) => {
if (data.code !== 502) { if (data.code !== 502) {
this.updateUser(data.profile); this.updateData({ key: "user", value: data.profile });
this.afterLogin(); this.afterLogin();
} }
}) })
@ -180,7 +178,7 @@ export default {
}) })
.then((data) => { .then((data) => {
if (data.code !== 502) { if (data.code !== 502) {
this.updateUser(data.profile); this.updateData({ key: "user", value: data.profile });
this.afterLogin(); this.afterLogin();
} }
}) })

@ -52,7 +52,6 @@
import { mapMutations } from "vuex"; import { mapMutations } from "vuex";
import NProgress from "nprogress"; import NProgress from "nprogress";
import { search } from "@/api/others"; import { search } from "@/api/others";
import Cookies from "js-cookie";
import { userPlaylist } from "@/api/user"; import { userPlaylist } from "@/api/user";
import { throttle } from "@/utils/common"; import { throttle } from "@/utils/common";
@ -74,7 +73,7 @@ export default {
NProgress.done(); NProgress.done();
}, },
methods: { methods: {
...mapMutations(["updateUser", "updateUserInfo", "updateUsernameLogin"]), ...mapMutations(["updateData"]),
search() { search() {
if (!this.keyword) return; if (!this.keyword) return;
search({ keywords: this.keyword, limit: 9, type: 1002 }).then((data) => { search({ keywords: this.keyword, limit: 9, type: 1002 }).then((data) => {
@ -83,14 +82,13 @@ export default {
}); });
}, },
confirm() { confirm() {
this.updateUser(this.activeUser); this.updateData({ key: "user", value: this.activeUser });
this.updateUsernameLogin(true); this.updateData({ key: "loginMode", value: "username" });
Cookies.set("loginMode", "username", { expires: 3650 });
userPlaylist({ userPlaylist({
uid: this.activeUser.userId, uid: this.activeUser.userId,
limit: 1, limit: 1,
}).then((data) => { }).then((data) => {
this.updateUserInfo({ this.updateData({
key: "likedSongPlaylistID", key: "likedSongPlaylistID",
value: data.playlist[0].id, value: data.playlist[0].id,
}); });

@ -45,9 +45,7 @@
{{ $t("play") }} {{ $t("play") }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
v-if=" v-if="accountLogin && playlist.creator.userId !== data.user.userId"
accountLogin && playlist.creator.userId !== settings.user.userId
"
shape="round" shape="round"
:iconClass="playlist.subscribed ? 'heart-solid' : 'heart'" :iconClass="playlist.subscribed ? 'heart-solid' : 'heart'"
:iconButton="true" :iconButton="true"
@ -61,8 +59,8 @@
<div class="user-info" v-else> <div class="user-info" v-else>
<h1> <h1>
<img class="avatar" :src="settings.user.avatarUrl | resizeImage" />{{ <img class="avatar" :src="data.user.avatarUrl | resizeImage" />{{
settings.user.nickname data.user.nickname
}}{{ $t("library.sLikedSongs") }} }}{{ $t("library.sLikedSongs") }}
</h1> </h1>
</div> </div>
@ -92,6 +90,7 @@ import NProgress from "nprogress";
import { getPlaylistDetail, subscribePlaylist } from "@/api/playlist"; import { getPlaylistDetail, subscribePlaylist } from "@/api/playlist";
import { playAList } from "@/utils/play"; import { playAList } from "@/utils/play";
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from "@/api/track";
import { isAccountLoggedIn } from "@/utils/auth";
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from "@/components/ButtonTwoTone.vue";
import TrackList from "@/components/TrackList.vue"; import TrackList from "@/components/TrackList.vue";
@ -122,7 +121,7 @@ export default {
}, },
created() { created() {
if (this.$route.name === "likedSongs") { if (this.$route.name === "likedSongs") {
this.loadData(this.settings.user.likedSongPlaylistID); this.loadData(this.data.user.likedSongPlaylistID);
} else { } else {
this.loadData(this.$route.params.id); this.loadData(this.$route.params.id);
} }
@ -131,10 +130,13 @@ export default {
window.removeEventListener("scroll", this.handleScroll, true); window.removeEventListener("scroll", this.handleScroll, true);
}, },
computed: { computed: {
...mapState(["player", "settings", "accountLogin"]), ...mapState(["player", "data"]),
isLikeSongsPage() { isLikeSongsPage() {
return this.$route.name === "likedSongs"; return this.$route.name === "likedSongs";
}, },
accountLogin() {
return isAccountLoggedIn();
},
}, },
methods: { methods: {
...mapMutations(["appendTrackToPlayerList"]), ...mapMutations(["appendTrackToPlayerList"]),

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save