mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 19:37:35 +08:00
333 lines
9.8 KiB
Vue
333 lines
9.8 KiB
Vue
<!-- 歌曲列表 - 右键菜单 -->
|
|
<template>
|
|
<n-dropdown
|
|
:x="dropdownX"
|
|
:y="dropdownY"
|
|
:show="dropdownShow"
|
|
:options="dropdownOptions"
|
|
class="song-list-menu"
|
|
placement="bottom-start"
|
|
trigger="manual"
|
|
size="large"
|
|
@select="dropdownShow = false"
|
|
@clickoutside="dropdownShow = false"
|
|
>
|
|
</n-dropdown>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { SongType } from "@/types/main";
|
|
import { NAlert, type DropdownOption } from "naive-ui";
|
|
import { useStatusStore, useLocalStore, useDataStore } from "@/stores";
|
|
import { renderIcon, copyData } from "@/utils/helper";
|
|
import { deleteCloudSong, importCloudSong } from "@/api/cloud";
|
|
import {
|
|
openCloudMatch,
|
|
openDownloadSong,
|
|
openPlaylistAdd,
|
|
openSongInfoEditor,
|
|
} from "@/utils/modal";
|
|
import { deleteSongs, isLogin } from "@/utils/auth";
|
|
import { songUrl } from "@/api/song";
|
|
import player from "@/utils/player";
|
|
|
|
const emit = defineEmits<{ removeSong: [index: number[]] }>();
|
|
|
|
const router = useRouter();
|
|
const dataStore = useDataStore();
|
|
const localStore = useLocalStore();
|
|
const statusStore = useStatusStore();
|
|
|
|
// 右键菜单数据
|
|
const dropdownX = ref<number>(0);
|
|
const dropdownY = ref<number>(0);
|
|
const dropdownShow = ref<boolean>(false);
|
|
const dropdownOptions = ref<DropdownOption[]>([]);
|
|
|
|
// 开启右键菜单
|
|
const openDropdown = (
|
|
e: MouseEvent,
|
|
data: SongType[],
|
|
song: SongType,
|
|
index: number,
|
|
type: "song" | "radio",
|
|
playListId?: number,
|
|
) => {
|
|
try {
|
|
e.preventDefault();
|
|
dropdownShow.value = false;
|
|
// 用户歌单
|
|
const userPlaylistsData = dataStore.userLikeData.playlists?.filter(
|
|
(pl) => pl.userId === dataStore.userData.userId,
|
|
);
|
|
// 当前状态
|
|
const isHasMv = !!song?.mv && song.mv !== 0;
|
|
const isCloud = router.currentRoute.value.name === "cloud";
|
|
const isLocal = !!song?.path;
|
|
const isLoginNormal = isLogin() === 1;
|
|
// 是否当前播放
|
|
const isCurrent = statusStore.playIndex === index;
|
|
// 是否为用户歌单
|
|
const isUserPlaylist = !!playListId && userPlaylistsData.some((pl) => pl.id === playListId);
|
|
// 生成菜单
|
|
nextTick().then(() => {
|
|
dropdownOptions.value = [
|
|
{
|
|
key: "play",
|
|
label: "立即播放",
|
|
props: {
|
|
onClick: () => player.addNextSong(song, true),
|
|
},
|
|
icon: renderIcon("Play", { size: 18 }),
|
|
},
|
|
{
|
|
key: "play-next",
|
|
label: "下一首播放",
|
|
show: !isCurrent && !statusStore.personalFmMode,
|
|
props: {
|
|
onClick: () => player.addNextSong(song, false),
|
|
},
|
|
icon: renderIcon("PlayNext", { size: 18 }),
|
|
},
|
|
{
|
|
key: "playlist-add",
|
|
label: "添加到歌单",
|
|
props: {
|
|
onClick: () => openPlaylistAdd([song], isLocal),
|
|
},
|
|
icon: renderIcon("AddList", { size: 18 }),
|
|
},
|
|
{
|
|
key: "mv",
|
|
label: "观看 MV",
|
|
show: type === "song" && isHasMv,
|
|
props: {
|
|
onClick: () => router.push({ name: "video", query: { id: song.mv, type: "mv" } }),
|
|
},
|
|
icon: renderIcon("Video", { size: 18 }),
|
|
},
|
|
{
|
|
key: "line-1",
|
|
type: "divider",
|
|
},
|
|
{
|
|
key: "more",
|
|
label: "更多操作",
|
|
icon: renderIcon("Menu", { size: 18 }),
|
|
children: [
|
|
{
|
|
key: "code-name",
|
|
label: `复制${type === "song" ? "歌曲" : "节目"}名称`,
|
|
props: {
|
|
onClick: () => copyData(song.name),
|
|
},
|
|
icon: renderIcon("Copy", { size: 18 }),
|
|
},
|
|
{
|
|
key: "code-id",
|
|
label: `复制${type === "song" ? "歌曲" : "节目"} ID`,
|
|
show: !isLocal,
|
|
props: {
|
|
onClick: () => copyData(song.id),
|
|
},
|
|
icon: renderIcon("Copy", { size: 18 }),
|
|
},
|
|
{
|
|
key: "share",
|
|
label: `分享${type === "song" ? "歌曲" : "节目"}链接`,
|
|
show: !isLocal,
|
|
props: {
|
|
onClick: () =>
|
|
copyData(
|
|
`https://music.163.com/#/${type}?id=${song.id}`,
|
|
"已复制分享链接到剪切板",
|
|
),
|
|
},
|
|
icon: renderIcon("Share", { size: 18 }),
|
|
},
|
|
{
|
|
key: "line-2",
|
|
type: "divider",
|
|
show: isLocal,
|
|
},
|
|
{
|
|
key: "meta-edit",
|
|
label: "音乐标签编辑",
|
|
show: isLocal,
|
|
props: {
|
|
onClick: () => {
|
|
if (song.path) openSongInfoEditor(song);
|
|
},
|
|
},
|
|
icon: renderIcon("EditNote", { size: 20 }),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: "line-two",
|
|
type: "divider",
|
|
},
|
|
{
|
|
key: "cloud-import",
|
|
label: "导入至云盘",
|
|
show: !isCloud && isLoginNormal && type === "song" && !isLocal,
|
|
props: {
|
|
onClick: () => importSongToCloud(song),
|
|
},
|
|
icon: renderIcon("Cloud"),
|
|
},
|
|
{
|
|
key: "delete",
|
|
label: "从歌单中删除",
|
|
show: isUserPlaylist && isLoginNormal && !isCloud,
|
|
props: {
|
|
onClick: () => deleteSongs(playListId!, [song.id], () => emit("removeSong", [song.id])),
|
|
},
|
|
icon: renderIcon("Delete"),
|
|
},
|
|
{
|
|
key: "cloud-delete",
|
|
label: "从云盘中删除",
|
|
show: isCloud,
|
|
props: {
|
|
onClick: () => deleteCloudSongData(song, index),
|
|
},
|
|
icon: renderIcon("Delete"),
|
|
},
|
|
{
|
|
key: "delete",
|
|
label: "从本地磁盘中删除",
|
|
show: isLocal && !isCurrent,
|
|
props: {
|
|
onClick: () => deleteLocalSong(song, data, index),
|
|
},
|
|
icon: renderIcon("Delete"),
|
|
},
|
|
{
|
|
key: "open-folder",
|
|
label: "打开歌曲所在目录",
|
|
show: isLocal,
|
|
props: {
|
|
onClick: () => window.electron.ipcRenderer.send("open-folder", song.path),
|
|
},
|
|
icon: renderIcon("SnippetFolder"),
|
|
},
|
|
{
|
|
key: "cloud-match",
|
|
label: "云盘歌曲纠正",
|
|
show: isCloud,
|
|
props: {
|
|
onClick: () => openCloudMatch(song?.id, index),
|
|
},
|
|
icon: renderIcon("AutoFix"),
|
|
},
|
|
{
|
|
key: "search",
|
|
label: "同名搜索",
|
|
props: {
|
|
onClick: () => router.push({ name: "search", query: { keyword: song.name } }),
|
|
},
|
|
icon: renderIcon("Search"),
|
|
},
|
|
{
|
|
key: "download",
|
|
label: "下载歌曲",
|
|
show: !isLocal && type === "song",
|
|
props: { onClick: () => openDownloadSong(song) },
|
|
icon: renderIcon("Download"),
|
|
},
|
|
];
|
|
// 显示菜单
|
|
dropdownX.value = e.clientX;
|
|
dropdownY.value = e.clientY;
|
|
dropdownShow.value = true;
|
|
});
|
|
} catch (error) {
|
|
console.error("右键菜单出现异常:", error);
|
|
window.$message.error("右键菜单出现异常");
|
|
}
|
|
};
|
|
|
|
// 删除歌曲
|
|
const deleteLocalSong = (song: SongType, data: SongType[], index: number) => {
|
|
if (!song.path) return;
|
|
window.$dialog.warning({
|
|
title: "确认删除",
|
|
content: () =>
|
|
h("div", { style: { marginTop: "20px" } }, [
|
|
h(NAlert, { showIcon: false }, { default: () => song.path }),
|
|
h("div", { style: { marginTop: "20px" } }, [
|
|
`确认从本地磁盘中删除 `,
|
|
h("strong", null, song.name),
|
|
`?该操作无法撤销!`,
|
|
]),
|
|
]),
|
|
positiveText: "删除",
|
|
negativeText: "取消",
|
|
onPositiveClick: async () => {
|
|
const result = await window.electron.ipcRenderer.invoke("delete-file", song.path);
|
|
if (result) {
|
|
data.splice(index, 1);
|
|
localStore.deleteLocalSong(index);
|
|
player.removeSongIndex(index);
|
|
window.$message.success(`${song.name} 删除成功`);
|
|
} else {
|
|
window.$message.error(`${song.name} 删除失败,请重试`);
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
// 删除云盘歌曲
|
|
const deleteCloudSongData = (song: SongType, index: number) => {
|
|
window.$dialog.warning({
|
|
title: "确认删除",
|
|
content: `确认从云盘中删除 ${song.name}?该操作无法撤销!`,
|
|
positiveText: "删除",
|
|
negativeText: "取消",
|
|
onPositiveClick: async () => {
|
|
const result = await deleteCloudSong(song.id);
|
|
if (result.code == 200) {
|
|
dataStore.cloudPlayList.splice(index, 1);
|
|
dataStore.setCloudPlayList(dataStore.cloudPlayList);
|
|
if (statusStore.playIndex === index) {
|
|
player.nextOrPrev("next");
|
|
}
|
|
window.$message.success("删除成功");
|
|
} else {
|
|
window.$message.error("删除失败,请重试");
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
// 导入至云盘
|
|
const importSongToCloud = async (song: SongType) => {
|
|
if (!song?.id) return;
|
|
// 获取歌曲下载信息
|
|
const songData = await songUrl(song.id);
|
|
const songDetail = songData?.data?.[0];
|
|
// 开始尝试导入
|
|
const { id, type, size, br, md5 } = songDetail;
|
|
const result = await importCloudSong(song?.name, type, size, Math.floor(br / 1000), md5, id);
|
|
if (result.code === 200) {
|
|
const failed = result?.data?.failed?.[0];
|
|
if (failed?.code !== -200) {
|
|
window.$message.success("导入成功");
|
|
} else {
|
|
window.$message.error(failed?.msg || "导入失败,请重试");
|
|
}
|
|
} else {
|
|
window.$message.error("导入失败,请重试");
|
|
}
|
|
};
|
|
|
|
defineExpose({ openDropdown });
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.delete-mata {
|
|
display: flex;
|
|
}
|
|
</style>
|