mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
feat: 新增收藏/取消收藏专辑 & 歌手页优化
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "splayer",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"private": true,
|
||||
"author": "imsyy",
|
||||
"home": "https://imsyy.top",
|
||||
|
||||
@@ -48,3 +48,20 @@ export const getToplist = (detail = true) => {
|
||||
url: `/toplist${detail ? "/detail" : null}`,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏专辑
|
||||
* @param {number} t - 操作类型,1为收藏,2为取消收藏
|
||||
* @param {number} id - 专辑id
|
||||
*/
|
||||
export const likeAlbum = (t, id) => {
|
||||
return axios({
|
||||
method: "GET",
|
||||
url: "/album/sub",
|
||||
params: {
|
||||
t,
|
||||
id,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -49,6 +49,23 @@ export const getUserPlaylist = (uid, limit = 30, offset = 0) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户的专辑列表
|
||||
* @param {number} [limit=30] - 返回数量,默认30
|
||||
* @param {number} [offset=0] - 偏移数量,默认0
|
||||
*/
|
||||
export const getUserAlbum = (limit = 30, offset = 0) => {
|
||||
return axios({
|
||||
method: "GET",
|
||||
url: "/album/sublist",
|
||||
params: {
|
||||
limit,
|
||||
offset,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌手列表
|
||||
*/
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
<script setup>
|
||||
import { PlayOne, Headset } from "@icon-park/vue-next";
|
||||
import { delPlayList, likePlaylist } from "@/api/playlist";
|
||||
import { likeAlbum } from "@/api/album";
|
||||
import { musicStore, userStore } from "@/store";
|
||||
import { useRouter } from "vue-router";
|
||||
import AllArtists from "./AllArtists.vue";
|
||||
@@ -163,31 +164,51 @@ const openRightMenu = (e, data) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "like",
|
||||
key: "likePlaylist",
|
||||
label: isLikeOrDislike(data.id) ? "收藏歌单" : "取消收藏歌单",
|
||||
show:
|
||||
user.userLogin &&
|
||||
user.getUserPlayLists.has &&
|
||||
props.listType === "playList" &&
|
||||
router.currentRoute.value.name != "playlists"
|
||||
? true
|
||||
: false,
|
||||
props: {
|
||||
onClick: () => {
|
||||
toLikePlaylist(data.id);
|
||||
toChangeLike(data.id);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "likeAlbum",
|
||||
label: isLikeOrDislike(data.id) ? "收藏专辑" : "取消收藏专辑",
|
||||
show:
|
||||
user.userLogin && user.getUserAlbum.has && props.listType === "album"
|
||||
? true
|
||||
: false,
|
||||
props: {
|
||||
onClick: () => {
|
||||
toChangeLike(data.id);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "copy",
|
||||
label: "复制歌单链接",
|
||||
label: `复制${props.listType === "playList" ? "歌单" : "专辑"}链接`,
|
||||
props: {
|
||||
onClick: () => {
|
||||
if (navigator.clipboard) {
|
||||
try {
|
||||
navigator.clipboard.writeText(
|
||||
`https://music.163.com/#/playlist?id=${data.id}`
|
||||
`https://music.163.com/#/${
|
||||
props.listType === "playList" ? "playlist" : "album"
|
||||
}?id=${data.id}`
|
||||
);
|
||||
$message.success(
|
||||
`${
|
||||
props.listType === "playList" ? "歌单" : "专辑"
|
||||
}链接复制成功`
|
||||
);
|
||||
$message.success("歌单链接复制成功");
|
||||
} catch (err) {
|
||||
$message.error("复制失败:", err);
|
||||
}
|
||||
@@ -211,7 +232,7 @@ const onClickoutside = () => {
|
||||
|
||||
// 链接跳转
|
||||
const toLink = (id) => {
|
||||
if (props.listType == "playList" || props.listType == "topList") {
|
||||
if (props.listType === "playList" || props.listType === "topList") {
|
||||
router.push({
|
||||
path: "/playlist",
|
||||
query: {
|
||||
@@ -219,7 +240,7 @@ const toLink = (id) => {
|
||||
page: 1,
|
||||
},
|
||||
});
|
||||
} else if (props.listType == "album") {
|
||||
} else if (props.listType === "album") {
|
||||
router.push({
|
||||
path: "/album",
|
||||
query: {
|
||||
@@ -250,36 +271,54 @@ const toDelPlayList = (data) => {
|
||||
|
||||
// 判断收藏还是取消
|
||||
const isLikeOrDislike = (id) => {
|
||||
if (user.getUserPlayLists.like[0]) {
|
||||
const index = user.getUserPlayLists.like.findIndex(
|
||||
(item) => item.id === id
|
||||
);
|
||||
if (index !== -1) {
|
||||
return false;
|
||||
const listType = props.listType;
|
||||
const playlists = user.getUserPlayLists.like;
|
||||
const albums = user.getUserAlbum.list;
|
||||
if (listType === "playList" && playlists.length) {
|
||||
return !playlists.some((item) => item.id === id);
|
||||
}
|
||||
if (listType === "album" && albums.length) {
|
||||
return !albums.some((item) => item.id === id);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 收藏/取消收藏
|
||||
const toChangeLike = async (id) => {
|
||||
const listType = props.listType;
|
||||
const type = isLikeOrDislike(id) ? 1 : 2;
|
||||
const likeFn = listType === "playList" ? likePlaylist : likeAlbum;
|
||||
const likeMsg = listType === "playList" ? "歌单" : "专辑";
|
||||
try {
|
||||
const res = await likeFn(type, id);
|
||||
if (res.code === 200) {
|
||||
$message.success(`${likeMsg}${type == 1 ? "收藏成功" : "取消收藏成功"}`);
|
||||
listType === "playList" ? user.setUserPlayLists() : user.setUserAlbum();
|
||||
} else {
|
||||
$message.error(`${likeMsg}${type == 1 ? "收藏失败" : "取消收藏失败"}`);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
} catch (err) {
|
||||
$message.error(`${likeMsg}${type == 1 ? "收藏失败" : "取消收藏失败"}`);
|
||||
console.error(
|
||||
`${likeMsg}${type == 1 ? "收藏失败:" : "取消收藏失败:"}` + err
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 收藏/取消收藏歌单
|
||||
const toLikePlaylist = (id) => {
|
||||
const type = isLikeOrDislike(id) ? 1 : 2;
|
||||
likePlaylist(type, id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$message.success(`歌单${type == 1 ? "收藏成功" : "取消收藏成功"}`);
|
||||
user.setUserPlayLists();
|
||||
} else {
|
||||
$message.error(`歌单${type == 1 ? "收藏失败" : "取消收藏失败"}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.name === "playlists" && !music.catList.sub)
|
||||
if (router.currentRoute.value.name === "playlists" && !music.catList.sub) {
|
||||
music.setCatList();
|
||||
if (user.userLogin && !user.getUserPlayLists.has) user.setUserPlayLists();
|
||||
}
|
||||
if (
|
||||
user.userLogin &&
|
||||
!user.getUserPlayLists.has &&
|
||||
props.listType === "playList"
|
||||
) {
|
||||
user.setUserPlayLists();
|
||||
}
|
||||
if (user.userLogin && !user.getUserAlbum.has && props.listType === "album") {
|
||||
user.setUserAlbum();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getUserSubcount,
|
||||
getUserPlaylist,
|
||||
getUserArtistlist,
|
||||
getUserAlbum,
|
||||
} from "@/api/user";
|
||||
import { formatNumber } from "@/utils/timeTools.js";
|
||||
|
||||
@@ -25,6 +26,11 @@ const useUserDataStore = defineStore("userData", {
|
||||
own: [], // 创建歌单
|
||||
like: [], // 收藏歌单
|
||||
},
|
||||
// 用户专辑
|
||||
userAlbum: {
|
||||
has: false,
|
||||
list: [],
|
||||
},
|
||||
// 用户收藏歌手
|
||||
userArtistLists: {
|
||||
has: false,
|
||||
@@ -53,6 +59,10 @@ const useUserDataStore = defineStore("userData", {
|
||||
getUserArtistlists(state) {
|
||||
return state.userArtistLists;
|
||||
},
|
||||
// 获取用户收藏专辑
|
||||
getUserAlbum(state) {
|
||||
return state.userAlbum;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 更改 cookie
|
||||
@@ -143,26 +153,57 @@ const useUserDataStore = defineStore("userData", {
|
||||
}
|
||||
},
|
||||
// 更改用户收藏歌手
|
||||
setUserArtistLists() {
|
||||
setUserArtistLists(callback) {
|
||||
if (this.userLogin) {
|
||||
getUserArtistlist().then((res) => {
|
||||
if (res.data) {
|
||||
this.userArtistLists = {
|
||||
list: [],
|
||||
};
|
||||
this.userArtistLists.has = true;
|
||||
res.data.forEach((v) => {
|
||||
this.userArtistLists.list.push({
|
||||
id: v.id,
|
||||
name: v.name,
|
||||
cover: v.img1v1Url,
|
||||
size: v.musicSize,
|
||||
getUserArtistlist()
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
this.userArtistLists = {
|
||||
list: [],
|
||||
};
|
||||
this.userArtistLists.has = true;
|
||||
res.data.forEach((v) => {
|
||||
this.userArtistLists.list.push({
|
||||
id: v.id,
|
||||
name: v.name,
|
||||
cover: v.img1v1Url,
|
||||
size: v.musicSize,
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$message.error("用户收藏歌手为空");
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
$message.error("用户收藏歌手为空");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("用户收藏歌手获取失败:" + err);
|
||||
$message.error("用户收藏歌手获取失败,请刷新后重试");
|
||||
});
|
||||
} else {
|
||||
$message.error("请登录账号后使用");
|
||||
}
|
||||
},
|
||||
// 更改用户收藏专辑
|
||||
async setUserAlbum() {
|
||||
if (this.userLogin) {
|
||||
try {
|
||||
let offset = 0;
|
||||
let totalCount = null;
|
||||
this.userAlbum.list = [];
|
||||
while (totalCount === null || offset < totalCount) {
|
||||
const { count, data } = await getUserAlbum(30, offset);
|
||||
console.log(count, data);
|
||||
this.userAlbum.list = this.userAlbum.list.concat(data);
|
||||
totalCount = count;
|
||||
offset += 30;
|
||||
}
|
||||
});
|
||||
this.userAlbum.has = true;
|
||||
} catch (err) {
|
||||
console.error("用户收藏专辑获取失败:" + err);
|
||||
$message.error("用户收藏专辑获取失败,请刷新后重试");
|
||||
}
|
||||
} else {
|
||||
$message.error("请登录账号后使用");
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="title" v-else-if="!albumId">
|
||||
<span class="key">未提供所需数据</span>
|
||||
<span class="key">参数不完整</span>
|
||||
<br />
|
||||
<n-button strong secondary @click="router.go(-1)" style="margin-top: 20px">
|
||||
返回上一级
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="artist">
|
||||
<div class="artistData" v-if="artistData">
|
||||
<div class="artistData" v-if="artistId && artistData">
|
||||
<div class="cover">
|
||||
<n-avatar
|
||||
round
|
||||
@@ -31,6 +31,30 @@
|
||||
<n-text class="desc text-hidden" @click="artistDescShow = true">
|
||||
{{ artistData.desc }}
|
||||
</n-text>
|
||||
<n-space class="button">
|
||||
<!-- <n-button type="primary" strong secondary>
|
||||
<template #icon>
|
||||
<n-icon :component="PlayArrowRound" />
|
||||
</template>
|
||||
播放热门歌曲
|
||||
</n-button> -->
|
||||
<n-button
|
||||
:type="artistLikeBtn ? 'primary' : 'default'"
|
||||
strong
|
||||
secondary
|
||||
@click="toLikeArtist(artistData)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon
|
||||
:component="
|
||||
artistLikeBtn ? PersonAddAlt1Round : PersonRemoveAlt1Round
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
{{ artistLikeBtn ? "收藏歌手" : "取消收藏歌手" }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<!-- 歌手介绍 -->
|
||||
<n-modal
|
||||
class="s-modal"
|
||||
v-model:show="artistDescShow"
|
||||
@@ -44,12 +68,24 @@
|
||||
</n-modal>
|
||||
</div>
|
||||
</div>
|
||||
<div class="error" v-else-if="!artistId">
|
||||
<n-text>参数不完整</n-text>
|
||||
<br />
|
||||
<n-button
|
||||
strong
|
||||
secondary
|
||||
@click="router.go(-1)"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
返回上一级
|
||||
</n-button>
|
||||
</div>
|
||||
<n-tabs
|
||||
class="main-tab"
|
||||
type="segment"
|
||||
@update:value="tabChange"
|
||||
v-model:value="tabValue"
|
||||
v-if="artistId"
|
||||
v-if="artistData"
|
||||
>
|
||||
<n-tab name="songs"> 热门单曲 </n-tab>
|
||||
<n-tab name="albums"> 专辑 </n-tab>
|
||||
@@ -72,14 +108,24 @@
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { getArtistDetail } from "@/api/artist";
|
||||
import { MusicNoteFilled, AlbumFilled, VideocamRound } from "@vicons/material";
|
||||
import { userStore } from "@/store";
|
||||
import { getArtistDetail, likeArtist } from "@/api/artist";
|
||||
import {
|
||||
MusicNoteFilled,
|
||||
AlbumFilled,
|
||||
VideocamRound,
|
||||
PersonAddAlt1Round,
|
||||
PersonRemoveAlt1Round,
|
||||
} from "@vicons/material";
|
||||
|
||||
const router = useRouter();
|
||||
const user = userStore();
|
||||
|
||||
// 歌手数据
|
||||
const artistId = ref(router.currentRoute.value.query.id);
|
||||
const artistData = ref(null);
|
||||
const artistDescShow = ref(false);
|
||||
const artistLikeBtn = ref(false);
|
||||
|
||||
// Tab 默认选中
|
||||
const tabValue = ref(router.currentRoute.value.path.split("/")[2]);
|
||||
@@ -87,20 +133,29 @@ const tabValue = ref(router.currentRoute.value.path.split("/")[2]);
|
||||
// 获取歌手数据
|
||||
const getArtistDetailData = (id) => {
|
||||
if (id) {
|
||||
getArtistDetail(id).then((res) => {
|
||||
console.log(res);
|
||||
artistData.value = {
|
||||
name: res.data.artist.name,
|
||||
occupation: res.data.identify ? res.data.identify.imageDesc : null,
|
||||
cover: res.data.artist.cover,
|
||||
desc: res.data.artist.briefDesc,
|
||||
albumSize: res.data.artist.albumSize,
|
||||
musicSize: res.data.artist.musicSize,
|
||||
mvSize: res.data.artist.mvSize,
|
||||
};
|
||||
});
|
||||
getArtistDetail(id)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
artistData.value = {
|
||||
id: res.data.artist.id,
|
||||
name: res.data.artist.name,
|
||||
occupation: res.data.identify ? res.data.identify.imageDesc : null,
|
||||
cover: res.data.artist.cover,
|
||||
desc: res.data.artist.briefDesc,
|
||||
albumSize: res.data.artist.albumSize,
|
||||
musicSize: res.data.artist.musicSize,
|
||||
mvSize: res.data.artist.mvSize,
|
||||
};
|
||||
// 请求后回顶
|
||||
if ($mainContent) $mainContent.scrollIntoView({ behavior: "smooth" });
|
||||
})
|
||||
.catch((err) => {
|
||||
router.go(-1);
|
||||
console.error("歌手信息获取失败:" + err);
|
||||
$message.error("歌手信息获取失败");
|
||||
});
|
||||
} else {
|
||||
$message.error("请提供歌手id");
|
||||
$message.error("参数不完整");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -116,8 +171,47 @@ const tabChange = (value) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 判断收藏还是取消
|
||||
const isLikeOrDislike = (id) => {
|
||||
if (user.getUserArtistlists.list[0]) {
|
||||
const index = user.getUserArtistlists.list.findIndex(
|
||||
(item) => item.id === Number(id)
|
||||
);
|
||||
if (index !== -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 收藏/取消收藏歌手
|
||||
const toLikeArtist = (data) => {
|
||||
const type = isLikeOrDislike(data.id) ? 1 : 2;
|
||||
likeArtist(type, data.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$message.success(
|
||||
`${data.name}${type == 1 ? "收藏成功" : "取消收藏成功"}`
|
||||
);
|
||||
user.setUserArtistLists(() => {
|
||||
artistLikeBtn.value = isLikeOrDislike(artistId.value);
|
||||
});
|
||||
} else {
|
||||
$message.error(`${data.name}${type == 1 ? "收藏失败" : "取消收藏失败"}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getArtistDetailData(artistId.value);
|
||||
artistLikeBtn.value = isLikeOrDislike(artistId.value);
|
||||
if (user.userLogin && !user.getUserArtistlists.has) {
|
||||
user.setUserArtistLists(() => {
|
||||
console.log("执行回调", artistId.value, isLikeOrDislike(artistId.value));
|
||||
artistLikeBtn.value = isLikeOrDislike(artistId.value);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 监听路由参数变化
|
||||
@@ -126,6 +220,7 @@ watch(
|
||||
(val) => {
|
||||
artistId.value = val.query.id;
|
||||
tabValue.value = val.path.split("/")[2];
|
||||
artistLikeBtn.value = isLikeOrDislike(artistId.value);
|
||||
if (val.path.split("/")[1] == "artist") {
|
||||
getArtistDetailData(artistId.value);
|
||||
}
|
||||
@@ -136,6 +231,15 @@ watch(
|
||||
<style lang="scss" scoped>
|
||||
.artist {
|
||||
margin-top: 30px;
|
||||
.error {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 20px;
|
||||
.n-text {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.artistData {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -167,6 +271,8 @@ watch(
|
||||
}
|
||||
.cover {
|
||||
margin-right: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.n-avatar {
|
||||
height: 240px;
|
||||
width: 240px;
|
||||
@@ -179,7 +285,7 @@ watch(
|
||||
.name {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 4px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.occupation {
|
||||
@@ -209,13 +315,16 @@ watch(
|
||||
}
|
||||
.desc {
|
||||
margin-top: 12px;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-line-clamp: 2;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.button {
|
||||
margin-top: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="title" v-else-if="!playListId">
|
||||
<span class="key">未提供所需数据</span>
|
||||
<span class="key">参数不完整</span>
|
||||
<br />
|
||||
<n-button strong secondary @click="router.go(-1)" style="margin-top: 20px">
|
||||
返回上一级
|
||||
|
||||
Reference in New Issue
Block a user