Skip to content

Commit 4fca789

Browse files
🐞 fix: 修复下载列表的排序、各项问题
1 parent 7f09f91 commit 4fca789

4 files changed

Lines changed: 168 additions & 102 deletions

File tree

electron/main/ipc/ipc-file.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,10 @@ const initFileIpc = (): void => {
379379
songData?: any;
380380
skipIfExist?: boolean;
381381
} = {
382-
fileName: "未知文件名",
383-
fileType: "mp3",
384-
path: app.getPath("downloads"),
385-
},
382+
fileName: "未知文件名",
383+
fileType: "mp3",
384+
path: app.getPath("downloads"),
385+
},
386386
): Promise<{ status: "success" | "skipped" | "error"; message?: string }> => {
387387
try {
388388
// 获取窗口

src/stores/data.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ interface ListState {
4040
song: SongType;
4141
/** 音质 */
4242
quality: SongLevelType;
43-
/** 状态:下载中 / 失败 */
44-
status: "downloading" | "failed";
43+
/** 状态:下载中 / 等待中 / 失败 */
44+
status: "downloading" | "waiting" | "failed";
4545
/** 下载进度 */
4646
progress: number;
4747
/** 已传输大小 */
@@ -350,14 +350,23 @@ export const useDataStore = defineStore("data", {
350350
this.downloadingSongs.push({
351351
song: cloneDeep(song),
352352
quality,
353-
status: "downloading",
353+
status: "waiting",
354354
progress: 0,
355355
transferred: "0MB",
356356
totalSize: "0MB",
357357
});
358358
// 保存到本地存储
359359
musicDB.setItem("downloadingSongs", cloneDeep(this.downloadingSongs));
360360
},
361+
// 更新下载状态
362+
updateDownloadStatus(songId: number, status: "downloading" | "waiting" | "failed") {
363+
const index = this.downloadingSongs.findIndex((item) => item.song.id === songId);
364+
if (index !== -1) {
365+
this.downloadingSongs[index].status = status;
366+
// 强制触发响应式更新 (Fix: 下一首歌曲状态更新UI不变化的问题)
367+
this.downloadingSongs = [...this.downloadingSongs];
368+
}
369+
},
361370
// 更新下载进度
362371
updateDownloadProgress(
363372
songId: number,
@@ -370,7 +379,7 @@ export const useDataStore = defineStore("data", {
370379
item.progress = progress;
371380
item.transferred = transferred;
372381
item.totalSize = totalSize;
373-
// 进度更新过于频繁,不再实时保存到本地存储,仅在添加/删除时保存
382+
// 进度更新过于频繁,不需要强制更新整个数组,以免影响性能
374383
}
375384
},
376385
// 移除正在下载的歌曲(下载失败时)
@@ -383,23 +392,25 @@ export const useDataStore = defineStore("data", {
383392
},
384393
// 标记下载失败(保留在列表中)
385394
markDownloadFailed(songId: number) {
386-
const item = this.downloadingSongs.find((item) => item.song.id === songId);
387-
if (item) {
388-
item.status = "failed";
389-
item.progress = 0;
390-
item.transferred = "0MB";
391-
item.totalSize = "0MB";
395+
const index = this.downloadingSongs.findIndex((item) => item.song.id === songId);
396+
if (index !== -1) {
397+
this.downloadingSongs[index].status = "failed";
398+
this.downloadingSongs[index].progress = 0;
399+
this.downloadingSongs[index].transferred = "0MB";
400+
this.downloadingSongs[index].totalSize = "0MB";
401+
this.downloadingSongs = [...this.downloadingSongs];
392402
musicDB.setItem("downloadingSongs", cloneDeep(this.downloadingSongs));
393403
}
394404
},
395405
// 重置下载任务状态(用于重试)
396406
resetDownloadingSong(songId: number) {
397-
const item = this.downloadingSongs.find((item) => item.song.id === songId);
398-
if (item) {
399-
item.status = "downloading";
400-
item.progress = 0;
401-
item.transferred = "0MB";
402-
item.totalSize = "0MB";
407+
const index = this.downloadingSongs.findIndex((item) => item.song.id === songId);
408+
if (index !== -1) {
409+
this.downloadingSongs[index].status = "waiting";
410+
this.downloadingSongs[index].progress = 0;
411+
this.downloadingSongs[index].transferred = "0MB";
412+
this.downloadingSongs[index].totalSize = "0MB";
413+
this.downloadingSongs = [...this.downloadingSongs];
403414
}
404415
},
405416
},

src/utils/downloadManager.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,17 @@ class DownloadManager {
118118
const total = (totalBytes / 1024 / 1024).toFixed(2) + "MB";
119119
dataStore.updateDownloadProgress(
120120
song.id,
121-
Number((percent * 100).toFixed(0)),
121+
Number((percent * 100).toFixed(1)),
122122
transferred,
123123
total,
124124
);
125125
};
126126
removeListener = window.electron.ipcRenderer.on("download-progress", progressHandler);
127127
}
128128

129+
// 更新状态为下载中
130+
dataStore.updateDownloadStatus(song.id, "downloading");
131+
129132
// 开始下载
130133
try {
131134
const result = await this.processDownload({

src/views/Download/downloading.vue

Lines changed: 133 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,103 +11,122 @@
1111
</div>
1212

1313
<!-- 列表内容 -->
14-
<n-virtual-list
15-
:item-size="90"
16-
:items="dataStore.downloadingSongs"
17-
class="virtual-list"
18-
item-resizable
19-
>
20-
<template #default="{ item, index }">
21-
<div :key="item.song.id" class="download-item">
22-
<!-- 序号 -->
23-
<div class="num">
24-
<n-text depth="3">{{ index + 1 }}</n-text>
25-
</div>
26-
<!-- 标题 (封面 + 信息) -->
27-
<div class="title">
28-
<s-image :src="item.song.coverSize?.s || item.song.cover" class="cover" />
29-
<div class="info">
30-
<div class="name">
31-
<n-text class="name-text" ellipsis>{{ item.song.name }}</n-text>
32-
</div>
33-
<div class="artists text-hidden">
34-
<n-text depth="3">
35-
{{
36-
Array.isArray(item.song.artists)
37-
? item.song.artists.map((a) => a.name).join(" / ")
38-
: item.song.artists
39-
}}
40-
</n-text>
41-
</div>
14+
<n-scrollbar class="virtual-list">
15+
<div
16+
v-for="(item, index) in sortedDownloadingSongs"
17+
:key="item.song.id"
18+
class="download-item"
19+
>
20+
<!-- 序号 -->
21+
<div class="num">
22+
<n-text depth="3">{{ index + 1 }}</n-text>
23+
</div>
24+
<!-- 标题 (封面 + 信息) -->
25+
<div class="title">
26+
<s-image :src="item.song.coverSize?.s || item.song.cover" class="cover" />
27+
<div class="info">
28+
<div class="name">
29+
<n-text class="name-text" ellipsis>{{ item.song.name }}</n-text>
30+
</div>
31+
<div class="artists text-hidden">
32+
<n-text depth="3">
33+
{{
34+
Array.isArray(item.song.artists)
35+
? item.song.artists.map((a) => a.name).join(" / ")
36+
: item.song.artists
37+
}}
38+
</n-text>
4239
</div>
4340
</div>
44-
<!-- 状态 -->
45-
<div class="status">
46-
<n-flex v-if="item.status === 'downloading'" vertical :size="6" style="width: 100%">
47-
<n-flex justify="space-between">
48-
<n-text depth="3" style="font-size: 12px">{{ item.progress }}%</n-text>
49-
<n-text depth="3" style="font-size: 12px">
50-
{{ item.transferred }} / {{ item.totalSize }}
51-
</n-text>
52-
</n-flex>
53-
<n-progress
54-
type="line"
55-
:percentage="item.progress"
56-
:show-indicator="false"
57-
processing
58-
status="success"
59-
style="height: 4px"
60-
/>
41+
</div>
42+
<!-- 状态 -->
43+
<div class="status">
44+
<n-flex v-if="item.status === 'downloading'" vertical :size="6" style="width: 100%">
45+
<n-flex justify="space-between">
46+
<n-text depth="3" style="font-size: 12px">{{ item.progress }}%</n-text>
47+
<n-text depth="3" style="font-size: 12px">
48+
{{ item.transferred }} / {{ item.totalSize }}
49+
</n-text>
6150
</n-flex>
62-
<n-flex v-else vertical :size="6" style="width: 100%">
63-
<n-text type="error" style="font-size: 12px">下载失败</n-text>
64-
<n-progress
65-
type="line"
66-
:percentage="0"
67-
:show-indicator="false"
68-
status="error"
69-
style="height: 4px"
51+
<div class="custom-progress">
52+
<div class="bar" :style="{ width: item.progress + '%' }" />
53+
<div
54+
class="light"
55+
v-if="item.status === 'downloading'"
56+
:style="{ left: item.progress + '%' }"
7057
/>
71-
</n-flex>
72-
</div>
73-
<!-- 操作 -->
74-
<div class="actions">
75-
<n-button
76-
type="primary"
77-
secondary
78-
strong
79-
style="margin-right: 12px"
80-
@click="DownloadManager.retryDownload(item.song.id)"
81-
>
82-
<template #icon>
83-
<SvgIcon name="Refresh" />
84-
</template>
85-
</n-button>
86-
<n-button
87-
type="error"
88-
secondary
89-
strong
90-
@click="DownloadManager.removeDownload(item.song.id)"
91-
>
92-
<template #icon>
93-
<SvgIcon name="Close" />
94-
</template>
95-
</n-button>
96-
</div>
58+
</div>
59+
</n-flex>
60+
<n-flex v-else-if="item.status === 'waiting'" vertical :size="6" style="width: 100%">
61+
<n-text depth="3" style="font-size: 12px">等待下载...</n-text>
62+
<n-progress
63+
type="line"
64+
:percentage="0"
65+
:show-indicator="false"
66+
style="height: 4px"
67+
/>
68+
</n-flex>
69+
<n-flex v-else vertical :size="6" style="width: 100%">
70+
<n-text type="error" style="font-size: 12px">下载失败</n-text>
71+
<n-progress
72+
type="line"
73+
:percentage="0"
74+
:show-indicator="false"
75+
status="error"
76+
style="height: 4px"
77+
/>
78+
</n-flex>
79+
</div>
80+
<!-- 操作 -->
81+
<div class="actions">
82+
<n-button
83+
type="primary"
84+
secondary
85+
strong
86+
style="margin-right: 12px"
87+
@click="DownloadManager.retryDownload(item.song.id)"
88+
>
89+
<template #icon>
90+
<SvgIcon name="Refresh" />
91+
</template>
92+
</n-button>
93+
<n-button
94+
type="error"
95+
secondary
96+
strong
97+
@click="DownloadManager.removeDownload(item.song.id)"
98+
>
99+
<template #icon>
100+
<SvgIcon name="Close" />
101+
</template>
102+
</n-button>
97103
</div>
98-
</template>
99-
</n-virtual-list>
104+
</div>
105+
</n-scrollbar>
100106
</div>
101107
<n-empty v-else description="暂无正在下载的任务" class="empty" />
102108
</Transition>
103109
</div>
104110
</template>
105111

106112
<script setup lang="ts">
113+
import { computed } from "vue";
107114
import { useDataStore } from "@/stores";
108115
import DownloadManager from "@/utils/downloadManager";
109116
110117
const dataStore = useDataStore();
118+
119+
const sortedDownloadingSongs = computed(() => {
120+
return [...dataStore.downloadingSongs].sort((a, b) => {
121+
// 优先级: 下载中 (1) > 等待中 (2) > 失败 (3)
122+
const getPriority = (status: string) => {
123+
if (status === "downloading") return 1;
124+
if (status === "waiting") return 2;
125+
return 3;
126+
};
127+
return getPriority(a.status) - getPriority(b.status);
128+
});
129+
});
111130
</script>
112131

113132
<style lang="scss" scoped>
@@ -237,6 +256,39 @@ const dataStore = useDataStore();
237256
min-width: 120px;
238257
}
239258
}
259+
.custom-progress {
260+
position: relative;
261+
width: 100%;
262+
height: 6px;
263+
margin-top: 4px;
264+
background-color: var(--surface-variant-hex);
265+
border-radius: 3px;
266+
overflow: hidden;
267+
268+
.bar {
269+
height: 100%;
270+
border-radius: 3px;
271+
background: rgb(var(--primary));
272+
background: linear-gradient(
273+
90deg,
274+
rgba(var(--primary), 0.7) 0%,
275+
rgba(var(--primary), 1) 100%
276+
);
277+
transition: width 0.3s ease-out;
278+
}
279+
280+
.light {
281+
position: absolute;
282+
top: 0;
283+
bottom: 0;
284+
width: 15px;
285+
transform: skewX(-20deg) translateX(-50%);
286+
background: rgba(255, 255, 255, 0.4);
287+
filter: blur(2px);
288+
transition: left 0.3s ease-out;
289+
pointer-events: none;
290+
}
291+
}
240292
}
241293
}
242294

0 commit comments

Comments
 (0)