Skip to content

Commit

Permalink
✨ feat: 支持批量删除歌曲
Browse files Browse the repository at this point in the history
  • Loading branch information
imsyy committed Sep 27, 2024
1 parent acf60b8 commit 0e0bde8
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 15 deletions.
11 changes: 9 additions & 2 deletions src/components/List/SongList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
:class="['song-item', { play: musicStore.playSong.id === item.id }]"
:style="{ margin }"
@dblclick.stop="playSong(item)"
@contextmenu="songListMenuRef?.openDropdown($event, data, item, index, type)"
@contextmenu="
songListMenuRef?.openDropdown($event, data, item, index, type, playListId)
"
>
<!-- 序号 -->
<div class="num" @dblclick.stop>
Expand Down Expand Up @@ -226,7 +228,7 @@
</template>
</DynamicScroller>
<!-- 右键菜单 -->
<SongListMenu ref="songListMenuRef" />
<SongListMenu ref="songListMenuRef" @removeSong="removeSong" />
<!-- 列表操作 -->
<Teleport to="body">
<Transition name="fade" mode="out-in">
Expand Down Expand Up @@ -300,6 +302,8 @@ const emit = defineEmits<{
reachBottom: [];
// 滚动
scroll: [e: Event];
// 删除歌曲
removeSong: [id: number[]];
}>();
// 右键菜单
Expand Down Expand Up @@ -432,6 +436,9 @@ const onScroll = (e: Event) => {
emit("scroll", e);
};
// 删除指定索引
const removeSong = (id: number[]) => emit("removeSong", id);
onDeactivated(() => {
floatToolShow.value = false;
});
Expand Down
30 changes: 22 additions & 8 deletions src/components/Menu/SongListMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<script setup lang="ts">
import type { SongType } from "@/types/main";
import { NAlert, type DropdownOption } from "naive-ui";
import { useStatusStore, useSettingStore, useLocalStore, useDataStore } from "@/stores";
import { useStatusStore, useLocalStore, useDataStore } from "@/stores";
import { renderIcon, copyData } from "@/utils/helper";
import { deleteCloudSong } from "@/api/cloud";
import {
Expand All @@ -28,13 +28,14 @@ import {
openSongInfoEditor,
} from "@/utils/modal";
import player from "@/utils/player";
import { deleteSongs } from "@/utils/auth";

const router = useRouter();
const emit = defineEmits<{ removeSong: [index: number[]] }>();

const router = useRouter();
const dataStore = useDataStore();
const localStore = useLocalStore();
const statusStore = useStatusStore();
const settingStore = useSettingStore();

// 右键菜单数据
const dropdownX = ref<number>(0);
Expand All @@ -49,19 +50,23 @@ const openDropdown = (
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 isSong = song.type === "song";
const isLocal = !!song?.path;
// 是否当前播放
const isCurrent = statusStore.playIndex === index;
console.log(isHasMv, isCloud, isLocal, data, index, settingStore.showTran);

// 是否为用户歌单
const isUserPlaylist = !!playListId && userPlaylistsData.some((pl) => pl.id === playListId);
// 生成菜单
nextTick().then(() => {
dropdownOptions.value = [
Expand Down Expand Up @@ -93,7 +98,7 @@ const openDropdown = (
{
key: "mv",
label: "观看 MV",
show: isSong && isHasMv,
show: type === "song" && isHasMv,
props: {
onClick: () => router.push({ name: "video", query: { id: song.mv, type: "mv" } }),
},
Expand Down Expand Up @@ -160,6 +165,15 @@ const openDropdown = (
key: "line-two",
type: "divider",
},
{
key: "delete",
label: "从歌单中删除",
show: isUserPlaylist && !isCloud,
props: {
onClick: () => deleteSongs(playListId!, [song.id], () => emit("removeSong", [song.id])),
},
icon: renderIcon("Delete"),
},
{
key: "cloud-delete",
label: "从云盘中删除",
Expand Down Expand Up @@ -207,7 +221,7 @@ const openDropdown = (
{
key: "download",
label: "下载歌曲",
show: !isLocal && isSong,
show: !isLocal && type === "song",
props: { onClick: () => openDownloadSong(song) },
icon: renderIcon("Download"),
},
Expand Down
21 changes: 21 additions & 0 deletions src/components/Modal/batchList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@
<n-flex class="batch-footer" justify="space-between" align="center">
<n-text :depth="3" class="count">已选择 {{ checkCount }} 首</n-text>
<n-flex class="menu">
<!-- 批量删除 -->
<n-button
v-if="playListId"
:disabled="!checkCount"
type="error"
strong
secondary
@click="
deleteSongs(
playListId,
checkSongData.map((item) => item.id),
)
"
>
<template #icon>
<SvgIcon name="Delete" />
</template>
删除选中的歌曲
</n-button>
<!-- 添加到歌单 -->
<n-button
:disabled="!checkCount"
Expand All @@ -33,6 +52,7 @@ import type { DataTableColumns, DataTableRowKey } from "naive-ui";
import type { SongType } from "@/types/main";
import { isArray, isObject } from "lodash-es";
import { openPlaylistAdd } from "@/utils/modal";
import { deleteSongs } from "@/utils/auth";
interface DataType {
key?: number;
Expand All @@ -47,6 +67,7 @@ interface DataType {
const props = defineProps<{
data: SongType[];
isLocal: boolean;
playListId?: number;
}>();
// 选中数据
Expand Down
33 changes: 33 additions & 0 deletions src/utils/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { debounce } from "lodash-es";
import { isBeforeSixAM } from "./time";
import { dailyRecommend } from "@/api/rec";
import { isElectron } from "./helper";
import { playlistTracks } from "@/api/playlist";

// 是否登录
export const isLogin = () => !!getCookie("MUSIC_U");
Expand Down Expand Up @@ -258,3 +259,35 @@ export const updateDailySongsData = async (refresh = false) => {
throw error;
}
};

/**
* 删除歌曲
* @param pid 歌单id
* @param ids 要删除的歌曲id
*/
export const deleteSongs = async (pid: number, ids: number[], callback?: () => void) => {
try {
window.$dialog.warning({
title: "删除歌曲",
content: ids?.length > 1 ? "确定删除这些选中的歌曲吗?" : "确定删除这个歌曲吗?",
positiveText: "删除",
negativeText: "取消",
onPositiveClick: async () => {
const result = await playlistTracks(pid, ids, "del");
if (result.status === 200) {
if (result.body?.code !== 200) {
window.$message.error(result.body?.message || "删除歌曲失败,请重试");
return;
}
callback && callback();
window.$message.success("删除成功");
} else {
window.$message.error(result?.message || "删除歌曲失败,请重试");
}
},
});
} catch (error) {
console.error("❌ Error deleting songs:", error);
throw error;
}
};
5 changes: 3 additions & 2 deletions src/utils/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ export const openPlaylistAdd = (data: SongType[], isLocal: boolean) => {
* 开启批量操作
* @param data 歌曲列表
* @param isLocal 是否为本地音乐
* @param playListId 歌单 id
*/
export const openBatchList = (data: SongType[], isLocal: boolean) => {
export const openBatchList = (data: SongType[], isLocal: boolean, playListId?: number) => {
window.$modal.create({
preset: "card",
transformOrigin: "center",
Expand All @@ -128,7 +129,7 @@ export const openBatchList = (data: SongType[], isLocal: boolean) => {
maxWidth: "70vw",
},
title: "批量操作",
content: () => h(batchList, { data, isLocal }),
content: () => h(batchList, { data, isLocal, playListId }),
});
};

Expand Down
9 changes: 8 additions & 1 deletion src/views/List/liked.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
:playListId="playlistId"
hidden-padding
@scroll="listScroll"
@removeSong="removeSong"
/>
<n-empty
v-else
Expand Down Expand Up @@ -237,7 +238,7 @@ const moreOptions = computed<DropdownOption[]>(() => [
label: "批量操作",
key: "batch",
props: {
onClick: () => openBatchList(playlistDataShow.value, false),
onClick: () => openBatchList(playlistDataShow.value, false, playlistId.value),
},
icon: renderIcon("Batch"),
},
Expand Down Expand Up @@ -389,6 +390,12 @@ const loadingMsgShow = (show: boolean = true) => {
}
};

// 删除指定索引歌曲
const removeSong = (ids: number[]) => {
if (!playlistData.value) return;
playlistData.value = playlistData.value.filter((song) => !ids.includes(song.id));
};

onActivated(() => {
if (!isActivated.value) {
isActivated.value = true;
Expand Down
16 changes: 14 additions & 2 deletions src/views/List/playlist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
:playListId="playlistId"
hidden-padding
@scroll="listScroll"
@removeSong="removeSong"
/>
<n-empty
v-else
Expand Down Expand Up @@ -216,8 +217,8 @@ import { isLogin, updateUserLikePlaylist } from "@/utils/auth";
import { debounce } from "lodash-es";
import { useDataStore, useStatusStore } from "@/stores";
import { openBatchList, openUpdatePlaylist } from "@/utils/modal";
import player from "@/utils/player";
import { formatTimestamp } from "@/utils/time";
import player from "@/utils/player";

const router = useRouter();
const dataStore = useDataStore();
Expand Down Expand Up @@ -291,7 +292,12 @@ const moreOptions = computed<DropdownOption[]>(() => [
label: "批量操作",
key: "batch",
props: {
onClick: () => openBatchList(playlistDataShow.value, false),
onClick: () =>
openBatchList(
playlistDataShow.value,
false,
isUserPlaylist.value ? playlistId.value : undefined,
),
},
icon: renderIcon("Batch"),
},
Expand Down Expand Up @@ -454,6 +460,12 @@ const toDeletePlaylist = async () => {
});
};

// 删除指定索引歌曲
const removeSong = (ids: number[]) => {
if (!playlistData.value) return;
playlistData.value = playlistData.value.filter((song) => !ids.includes(song.id));
};

// 编辑歌单
const updatePlaylist = () => {
if (!playlistDetailData.value || !playlistId.value) return;
Expand Down

0 comments on commit 0e0bde8

Please sign in to comment.