Skip to content

Commit abbf423

Browse files
committed
✨ feat: 准备下载管理
1 parent 1a959c8 commit abbf423

4 files changed

Lines changed: 132 additions & 128 deletions

File tree

src/components/Layout/Menu.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,13 @@ const menuOptions = computed<MenuOption[] | MenuGroupOption[]>(() => {
132132
show: isLogin() === 1 && !settingStore.hideCloud,
133133
icon: renderIcon("Cloud"),
134134
},
135-
// {
136-
// key: "download",
137-
// link: "download",
138-
// label: "下载管理",
139-
// show: isElectron,
140-
// icon: renderIcon("Download"),
141-
// },
135+
{
136+
key: "download",
137+
link: "download",
138+
label: "下载管理",
139+
show: isElectron,
140+
icon: renderIcon("Download"),
141+
},
142142
{
143143
key: "local",
144144
link: "local",

src/components/Modal/BatchList.vue

Lines changed: 49 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -161,52 +161,6 @@
161161
</n-flex>
162162
</template>
163163
</n-modal>
164-
165-
<!-- 批量下载进度通知 -->
166-
<Teleport to="body">
167-
<Transition name="slide-fade">
168-
<div
169-
v-if="batchDownloadState.isDownloading"
170-
class="batch-download-notification"
171-
:class="{ 'is-hovered': isNotificationHovered }"
172-
@mouseenter="isNotificationHovered = true"
173-
@mouseleave="isNotificationHovered = false"
174-
>
175-
<n-card content-style="padding: 16px;" :bordered="false">
176-
<n-flex vertical :size="12">
177-
<n-flex justify="space-between" align="center">
178-
<n-text strong>批量下载中</n-text>
179-
<n-text depth="3" style="font-size: 12px">
180-
{{ batchDownloadState.processed }}/{{ batchDownloadState.total }}
181-
</n-text>
182-
</n-flex>
183-
184-
<n-text style="font-size: 13px" ellipsis>
185-
正在下载: {{ batchDownloadState.currentSong }}
186-
</n-text>
187-
188-
<n-progress
189-
type="line"
190-
:percentage="batchDownloadState.percent"
191-
:show-indicator="false"
192-
processing
193-
status="success"
194-
style="height: 6px"
195-
/>
196-
197-
<n-flex justify="space-between" style="font-size: 12px">
198-
<n-text depth="3">
199-
{{ batchDownloadState.transferred }} / {{ batchDownloadState.totalSize }}
200-
</n-text>
201-
<n-text depth="3">
202-
成功: {{ batchDownloadState.success }}
203-
</n-text>
204-
</n-flex>
205-
</n-flex>
206-
</n-card>
207-
</div>
208-
</Transition>
209-
</Teleport>
210164
</div>
211165
</template>
212166

@@ -226,18 +180,17 @@ import {
226180
NInputGroup,
227181
NButton,
228182
NAlert,
229-
NCard,
230-
NProgress,
231183
NText,
232184
NFlex,
233185
} from "naive-ui";
234-
import { useLocalStore, useSettingStore } from "@/stores";
186+
import { useLocalStore, useSettingStore, useDataStore } from "@/stores";
235187
import { isElectron } from "@/utils/env";
236188
import { songLevelData, getSongLevelsData } from "@/utils/meta";
237189
import { downloadSong } from "@/utils/download";
238190
239191
const localStore = useLocalStore();
240192
const settingStore = useSettingStore();
193+
const dataStore = useDataStore();
241194
242195
interface DataType {
243196
key?: number;
@@ -269,19 +222,6 @@ const showQualityModal = ref(false);
269222
const selectedQuality = ref<SongLevelType>("h");
270223
const downloadPath = ref<string>(settingStore.downloadPath);
271224
272-
// 批量下载状态
273-
const batchDownloadState = reactive({
274-
isDownloading: false,
275-
total: 0,
276-
processed: 0,
277-
success: 0,
278-
currentSong: "",
279-
percent: 0,
280-
transferred: "0MB",
281-
totalSize: "0MB",
282-
});
283-
const isNotificationHovered = ref(false);
284-
285225
// 音质选项
286226
const qualityOptions = computed(() => {
287227
// 批量下载时,默认显示所有常用音质选项
@@ -474,23 +414,26 @@ const executeBatchDownload = async (songs: SongType[]) => {
474414
if (!songs.length) return;
475415
476416
// 重置状态
477-
batchDownloadState.isDownloading = true;
478-
batchDownloadState.total = songs.length;
479-
batchDownloadState.processed = 0;
480-
batchDownloadState.success = 0;
481-
batchDownloadState.percent = 0;
482-
batchDownloadState.transferred = "0MB";
483-
batchDownloadState.totalSize = "0MB";
417+
dataStore.batchDownload.isDownloading = true;
418+
dataStore.batchDownload.total = songs.length;
419+
dataStore.batchDownload.processed = 0;
420+
dataStore.batchDownload.success = 0;
421+
dataStore.batchDownload.percent = 0;
422+
dataStore.batchDownload.transferred = "0MB";
423+
dataStore.batchDownload.totalSize = "0MB";
484424
485425
let failCount = 0;
486426
const failedSongs: SongType[] = [];
487427
488428
// 监听下载进度
489-
const onProgress = (_event: any, progress: { percent: number; transferredBytes: number; totalBytes: number }) => {
429+
const onProgress = (
430+
_event: any,
431+
progress: { percent: number; transferredBytes: number; totalBytes: number },
432+
) => {
490433
const { percent, transferredBytes, totalBytes } = progress;
491-
batchDownloadState.percent = Number((percent * 100).toFixed(0));
492-
batchDownloadState.transferred = (transferredBytes / 1024 / 1024).toFixed(2) + "MB";
493-
batchDownloadState.totalSize = (totalBytes / 1024 / 1024).toFixed(2) + "MB";
434+
dataStore.batchDownload.percent = Number((percent * 100).toFixed(0));
435+
dataStore.batchDownload.transferred = (transferredBytes / 1024 / 1024).toFixed(2) + "MB";
436+
dataStore.batchDownload.totalSize = (totalBytes / 1024 / 1024).toFixed(2) + "MB";
494437
};
495438
496439
if (isElectron) {
@@ -499,7 +442,7 @@ const executeBatchDownload = async (songs: SongType[]) => {
499442
500443
try {
501444
for (const song of songs) {
502-
batchDownloadState.currentSong = song.name;
445+
dataStore.batchDownload.currentSong = song.name;
503446
try {
504447
const result = await downloadSong({
505448
song,
@@ -509,7 +452,7 @@ const executeBatchDownload = async (songs: SongType[]) => {
509452
});
510453
511454
if (result.success) {
512-
batchDownloadState.success++;
455+
dataStore.batchDownload.success++;
513456
if (result.skipped) {
514457
window.$notification.create({
515458
title: "已跳过重复文件",
@@ -531,32 +474,50 @@ const executeBatchDownload = async (songs: SongType[]) => {
531474
failCount++;
532475
failedSongs.push(song);
533476
} finally {
534-
batchDownloadState.processed++;
477+
dataStore.batchDownload.processed++;
535478
// Reset progress for next song
536-
batchDownloadState.percent = 0;
537-
batchDownloadState.transferred = "0MB";
538-
batchDownloadState.totalSize = "0MB";
479+
dataStore.batchDownload.percent = 0;
480+
dataStore.batchDownload.transferred = "0MB";
481+
dataStore.batchDownload.totalSize = "0MB";
539482
}
540483
}
541484
542485
if (failCount > 0) {
543486
window.$dialog.warning({
544487
title: "下载完成,但有部分失败",
545-
content: () => h("div", [
546-
h("div", { style: "margin-bottom: 10px" }, `${batchDownloadState.success} 首下载成功,${failCount} 首下载失败。`),
547-
h("div", { style: "max-height: 200px; overflow-y: auto; background: rgba(0,0,0,0.05); padding: 8px; border-radius: 4px;" }, [
548-
h("div", { style: "font-weight: bold; margin-bottom: 4px" }, "失败列表:"),
549-
...failedSongs.map(s => h("div", { style: "font-size: 12px" }, `${s.name} - ${isArray(s.artists) ? s.artists[0]?.name : s.artists || '未知歌手'}`))
550-
])
551-
]),
488+
content: () =>
489+
h("div", [
490+
h(
491+
"div",
492+
{ style: "margin-bottom: 10px" },
493+
`${dataStore.batchDownload.success} 首下载成功,${failCount} 首下载失败。`,
494+
),
495+
h(
496+
"div",
497+
{
498+
style:
499+
"max-height: 200px; overflow-y: auto; background: rgba(0,0,0,0.05); padding: 8px; border-radius: 4px;",
500+
},
501+
[
502+
h("div", { style: "font-weight: bold; margin-bottom: 4px" }, "失败列表:"),
503+
...failedSongs.map((s) =>
504+
h(
505+
"div",
506+
{ style: "font-size: 12px" },
507+
`${s.name} - ${isArray(s.artists) ? s.artists[0]?.name : s.artists || "未知歌手"}`,
508+
),
509+
),
510+
],
511+
),
512+
]),
552513
positiveText: "重试失败歌曲",
553514
negativeText: "取消",
554515
onPositiveClick: () => {
555516
executeBatchDownload(failedSongs);
556517
},
557518
});
558519
} else {
559-
window.$message.success(`批量下载完成,共 ${batchDownloadState.success} 首`);
520+
window.$message.success(`批量下载完成,共 ${dataStore.batchDownload.success} 首`);
560521
}
561522
} catch (error) {
562523
console.error("Batch download error:", error);
@@ -565,7 +526,7 @@ const executeBatchDownload = async (songs: SongType[]) => {
565526
if (isElectron) {
566527
window.electron.ipcRenderer.removeListener("download-progress", onProgress);
567528
}
568-
batchDownloadState.isDownloading = false;
529+
dataStore.batchDownload.isDownloading = false;
569530
}
570531
};
571532
</script>
@@ -577,35 +538,4 @@ const executeBatchDownload = async (songs: SongType[]) => {
577538
.range-input {
578539
width: 100px;
579540
}
580-
581-
.batch-download-notification {
582-
position: fixed;
583-
bottom: 24px;
584-
right: 24px;
585-
z-index: 9999;
586-
width: 320px;
587-
transition: all 0.3s ease;
588-
589-
&.is-hovered {
590-
opacity: 0;
591-
transform: translateY(10px);
592-
pointer-events: none;
593-
}
594-
595-
:deep(.n-card) {
596-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
597-
border: 1px solid rgba(255, 255, 255, 0.1);
598-
}
599-
}
600-
601-
.slide-fade-enter-active,
602-
.slide-fade-leave-active {
603-
transition: all 0.3s ease;
604-
}
605-
606-
.slide-fade-enter-from,
607-
.slide-fade-leave-to {
608-
transform: translateY(20px);
609-
opacity: 0;
610-
}
611541
</style>

src/stores/data.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import type {
1010
import { playlistCatlist } from "@/api/playlist";
1111
import { cloneDeep, isEmpty } from "lodash-es";
1212
import { isLogin } from "@/utils/auth";
13-
import localforage from "localforage";
1413
import { formatCategoryList } from "@/utils/format";
14+
import localforage from "localforage";
1515

1616
interface ListState {
1717
playList: SongType[];
@@ -33,6 +33,25 @@ interface ListState {
3333
cats: CatType[];
3434
hqCats: CatType[];
3535
};
36+
/** 批量下载 */
37+
batchDownload: {
38+
/** 是否正在下载 */
39+
isDownloading: boolean;
40+
/** 下载总数 */
41+
total: number;
42+
/** 已处理数量 */
43+
processed: number;
44+
/** 成功数量 */
45+
success: number;
46+
/** 当前下载歌曲 */
47+
currentSong: string;
48+
/** 当前歌曲下载进度 */
49+
percent: number;
50+
/** 已传输大小 */
51+
transferred: string;
52+
/** 总大小 */
53+
totalSize: string;
54+
};
3655
}
3756

3857
type UserDataKeys = keyof ListState["userLikeData"];
@@ -100,6 +119,17 @@ export const useDataStore = defineStore("data", {
100119
cats: [],
101120
hqCats: [],
102121
},
122+
// 批量下载
123+
batchDownload: {
124+
isDownloading: false,
125+
total: 0,
126+
processed: 0,
127+
success: 0,
128+
currentSong: "",
129+
percent: 0,
130+
transferred: "0MB",
131+
totalSize: "0MB",
132+
},
103133
}),
104134
getters: {
105135
// 是否为喜欢歌曲

src/views/Download/downloading.vue

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,58 @@
11
<template>
22
<div class="download-downloading">
3-
<n-empty description="暂无正在下载的任务" class="empty" />
3+
<div v-if="dataStore.batchDownload.isDownloading" class="download-card">
4+
<n-card content-style="padding: 16px;" :bordered="false">
5+
<n-flex vertical :size="12">
6+
<n-flex justify="space-between" align="center">
7+
<n-text strong>批量下载中</n-text>
8+
<n-text depth="3" style="font-size: 12px">
9+
{{ dataStore.batchDownload.processed }}/{{ dataStore.batchDownload.total }}
10+
</n-text>
11+
</n-flex>
12+
<n-text style="font-size: 13px" ellipsis>
13+
正在下载: {{ dataStore.batchDownload.currentSong }}
14+
</n-text>
15+
<n-progress
16+
type="line"
17+
:percentage="dataStore.batchDownload.percent"
18+
:show-indicator="false"
19+
processing
20+
status="success"
21+
style="height: 6px"
22+
/>
23+
<n-flex justify="space-between" style="font-size: 12px">
24+
<n-text depth="3">
25+
{{ dataStore.batchDownload.transferred }} / {{ dataStore.batchDownload.totalSize }}
26+
</n-text>
27+
<n-text depth="3">
28+
成功: {{ dataStore.batchDownload.success }}
29+
</n-text>
30+
</n-flex>
31+
</n-flex>
32+
</n-card>
33+
</div>
34+
<n-empty v-else description="暂无正在下载的任务" class="empty" />
435
</div>
536
</template>
637

38+
<script setup lang="ts">
39+
import { useDataStore } from "@/stores";
40+
41+
const dataStore = useDataStore();
42+
</script>
43+
744
<style lang="scss" scoped>
845
.download-downloading {
946
height: 100%;
1047
display: flex;
1148
align-items: center;
1249
justify-content: center;
50+
.download-card {
51+
width: 400px;
52+
:deep(.n-card) {
53+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
54+
border: 1px solid rgba(255, 255, 255, 0.1);
55+
}
56+
}
1357
}
1458
</style>

0 commit comments

Comments
 (0)