mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 01:10:16 +08:00
feat(download): 支持重试下载功能并更新相关逻辑
This commit is contained in:
96
src/App.vue
96
src/App.vue
@@ -40,7 +40,7 @@ import DownloadQueue from './components/DownloadQueue.vue';
|
||||
import DownloadDetail from './components/DownloadDetail.vue';
|
||||
import { APM_STORE_ARCHITECTURE, APM_STORE_BASE_URL, currentApp } from './global/storeConfig';
|
||||
import { downloads } from './global/downloadStatus';
|
||||
import { handleInstall } from './js/processInstall';
|
||||
import { handleInstall, handleRetry } from './modeuls/processInstall';
|
||||
|
||||
const logger = pino();
|
||||
|
||||
@@ -249,56 +249,58 @@ const escapeHtml = (s) => {
|
||||
};
|
||||
|
||||
// 下载管理方法
|
||||
const simulateDownload = (download) => {
|
||||
// 模拟下载进度(实际应该调用真实的下载 API)
|
||||
const totalSize = Math.random() * 100 + 50; // MB
|
||||
download.totalSize = totalSize * 1024 * 1024;
|
||||
// 在这里保留这个方便以后参考
|
||||
// const simulateDownload = (download) => {
|
||||
// // 模拟下载进度(实际应该调用真实的下载 API)
|
||||
// const totalSize = Math.random() * 100 + 50; // MB
|
||||
// download.totalSize = totalSize * 1024 * 1024;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const downloadObj = downloads.value.find(d => d.id === download.id);
|
||||
if (!downloadObj || downloadObj.status !== 'downloading') {
|
||||
clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
// const interval = setInterval(() => {
|
||||
// const downloadObj = downloads.value.find(d => d.id === download.id);
|
||||
// if (!downloadObj || downloadObj.status !== 'downloading') {
|
||||
// clearInterval(interval);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 更新进度
|
||||
downloadObj.progress = Math.min(downloadObj.progress + Math.random() * 10, 100);
|
||||
downloadObj.downloadedSize = (downloadObj.progress / 100) * downloadObj.totalSize;
|
||||
downloadObj.speed = (Math.random() * 5 + 1) * 1024 * 1024; // 1-6 MB/s
|
||||
// // 更新进度
|
||||
// downloadObj.progress = Math.min(downloadObj.progress + Math.random() * 10, 100);
|
||||
// downloadObj.downloadedSize = (downloadObj.progress / 100) * downloadObj.totalSize;
|
||||
// downloadObj.speed = (Math.random() * 5 + 1) * 1024 * 1024; // 1-6 MB/s
|
||||
|
||||
const remainingBytes = downloadObj.totalSize - downloadObj.downloadedSize;
|
||||
downloadObj.timeRemaining = Math.ceil(remainingBytes / downloadObj.speed);
|
||||
// const remainingBytes = downloadObj.totalSize - downloadObj.downloadedSize;
|
||||
// downloadObj.timeRemaining = Math.ceil(remainingBytes / downloadObj.speed);
|
||||
|
||||
// 添加日志
|
||||
if (downloadObj.progress % 20 === 0 && downloadObj.progress > 0 && downloadObj.progress < 100) {
|
||||
downloadObj.logs.push({
|
||||
time: Date.now(),
|
||||
message: `下载进度: ${downloadObj.progress.toFixed(0)}%`
|
||||
});
|
||||
}
|
||||
// // 添加日志
|
||||
// if (downloadObj.progress % 20 === 0 && downloadObj.progress > 0 && downloadObj.progress < 100) {
|
||||
// downloadObj.logs.push({
|
||||
// time: Date.now(),
|
||||
// message: `下载进度: ${downloadObj.progress.toFixed(0)}%`
|
||||
// });
|
||||
// }
|
||||
|
||||
// 下载完成
|
||||
if (downloadObj.progress >= 100) {
|
||||
clearInterval(interval);
|
||||
downloadObj.status = 'installing';
|
||||
downloadObj.logs.push({
|
||||
time: Date.now(),
|
||||
message: '下载完成,开始安装...'
|
||||
});
|
||||
// // 下载完成
|
||||
// if (downloadObj.progress >= 100) {
|
||||
// clearInterval(interval);
|
||||
// downloadObj.status = 'installing';
|
||||
// downloadObj.logs.push({
|
||||
// time: Date.now(),
|
||||
// message: '下载完成,开始安装...'
|
||||
// });
|
||||
|
||||
// 模拟安装
|
||||
setTimeout(() => {
|
||||
downloadObj.status = 'completed';
|
||||
downloadObj.endTime = Date.now();
|
||||
downloadObj.logs.push({
|
||||
time: Date.now(),
|
||||
message: '安装完成!'
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
// // 模拟安装
|
||||
// setTimeout(() => {
|
||||
// downloadObj.status = 'completed';
|
||||
// downloadObj.endTime = Date.now();
|
||||
// downloadObj.logs.push({
|
||||
// time: Date.now(),
|
||||
// message: '安装完成!'
|
||||
// });
|
||||
// }, 2000);
|
||||
// }
|
||||
// }, 500);
|
||||
// };
|
||||
|
||||
// 目前 APM 商店不能暂停下载(因为 APM 本身不支持),但保留这些方法以备将来使用
|
||||
const pauseDownload = (id) => {
|
||||
const download = downloads.value.find(d => d.id === id);
|
||||
if (download && download.status === 'downloading') {
|
||||
@@ -310,6 +312,7 @@ const pauseDownload = (id) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 同理
|
||||
const resumeDownload = (id) => {
|
||||
const download = downloads.value.find(d => d.id === id);
|
||||
if (download && download.status === 'paused') {
|
||||
@@ -322,6 +325,7 @@ const resumeDownload = (id) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 同理
|
||||
const cancelDownload = (id) => {
|
||||
const index = downloads.value.findIndex(d => d.id === id);
|
||||
if (index !== -1) {
|
||||
@@ -342,14 +346,14 @@ const cancelDownload = (id) => {
|
||||
const retryDownload = (id) => {
|
||||
const download = downloads.value.find(d => d.id === id);
|
||||
if (download && download.status === 'failed') {
|
||||
download.status = 'downloading';
|
||||
download.status = 'queued';
|
||||
download.progress = 0;
|
||||
download.downloadedSize = 0;
|
||||
download.logs.push({
|
||||
time: Date.now(),
|
||||
message: '重新开始下载...'
|
||||
});
|
||||
simulateDownload(download);
|
||||
handleRetry(download);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="actions-section">
|
||||
<button
|
||||
<!-- <button
|
||||
v-if="download.status === 'downloading'"
|
||||
@click="pause"
|
||||
class="action-btn secondary"
|
||||
@@ -111,7 +111,7 @@
|
||||
>
|
||||
<i class="fas fa-play"></i>
|
||||
继续下载
|
||||
</button>
|
||||
</button> -->
|
||||
<button
|
||||
v-if="download.status === 'failed'"
|
||||
@click="retry"
|
||||
@@ -120,14 +120,14 @@
|
||||
<i class="fas fa-redo"></i>
|
||||
重试下载
|
||||
</button>
|
||||
<button
|
||||
<!-- <button
|
||||
v-if="download.status !== 'completed'"
|
||||
@click="cancel"
|
||||
class="action-btn danger"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
取消下载
|
||||
</button>
|
||||
</button> -->
|
||||
<button
|
||||
v-if="download.status === 'completed'"
|
||||
@click="openApp"
|
||||
@@ -168,15 +168,15 @@ const handleOverlayClick = () => {
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
emit('pause', props.download.id);
|
||||
// emit('pause', props.download.id);
|
||||
};
|
||||
|
||||
const resume = () => {
|
||||
emit('resume', props.download.id);
|
||||
// emit('resume', props.download.id);
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel', props.download.id);
|
||||
//emit('cancel', props.download.id);
|
||||
};
|
||||
|
||||
const retry = () => {
|
||||
@@ -523,9 +523,11 @@ const copyLogs = () => {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
/* .action-btn {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 12px 20px;
|
||||
@@ -539,9 +541,9 @@ const copyLogs = () => {
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
} */
|
||||
|
||||
.action-btn.primary {
|
||||
/* .action-btn.primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
@@ -554,15 +556,23 @@ const copyLogs = () => {
|
||||
.action-btn.secondary {
|
||||
background: var(--glass);
|
||||
color: var(--text);
|
||||
}
|
||||
} */
|
||||
|
||||
.action-btn.secondary:hover {
|
||||
background: var(--border);
|
||||
.action-btn.primary {
|
||||
background: rgba(255, 59, 48, 0.1);
|
||||
color: #FF3B30;
|
||||
min-width: 140px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-btn.danger {
|
||||
background: rgba(255, 59, 48, 0.1);
|
||||
color: #FF3B30;
|
||||
width: fill-available;
|
||||
min-width: 140px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-btn.danger:hover {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<div class="download-info">
|
||||
<div class="download-name">{{ download.name }}</div>
|
||||
<div class="download-status-text">
|
||||
<!-- downloading 这部分APM用不到,留给后续的Spark Store -->
|
||||
<span v-if="download.status === 'downloading'">
|
||||
下载中 {{ download.progress }}%
|
||||
</span>
|
||||
@@ -62,7 +63,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="download-actions">
|
||||
<button
|
||||
<!-- <button
|
||||
v-if="download.status === 'downloading'"
|
||||
@click.stop="pauseDownload(download.id)"
|
||||
class="action-icon"
|
||||
@@ -77,7 +78,7 @@
|
||||
title="继续"
|
||||
>
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
</button> -->
|
||||
<button
|
||||
v-if="download.status === 'failed'"
|
||||
@click.stop="retryDownload(download.id)"
|
||||
@@ -86,13 +87,13 @@
|
||||
>
|
||||
<i class="fas fa-redo"></i>
|
||||
</button>
|
||||
<button
|
||||
<!-- <button
|
||||
@click.stop="cancelDownload(download.id)"
|
||||
class="action-icon"
|
||||
title="取消"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,15 +134,15 @@ const toggleExpand = () => {
|
||||
};
|
||||
|
||||
const pauseDownload = (id) => {
|
||||
emit('pause', id);
|
||||
// emit('pause', id);
|
||||
};
|
||||
|
||||
const resumeDownload = (id) => {
|
||||
emit('resume', id);
|
||||
// emit('resume', id);
|
||||
};
|
||||
|
||||
const cancelDownload = (id) => {
|
||||
emit('cancel', id);
|
||||
// emit('cancel', id);
|
||||
};
|
||||
|
||||
const retryDownload = (id) => {
|
||||
|
||||
@@ -29,4 +29,5 @@ export interface DownloadItem {
|
||||
message: string; // 日志消息
|
||||
}>;
|
||||
source: string; // 例如 'APM Store'
|
||||
retry: boolean; // 当前是否为重试下载
|
||||
}
|
||||
@@ -31,23 +31,28 @@ export const handleInstall = () => {
|
||||
logs: [
|
||||
{ time: Date.now(), message: '开始下载...' }
|
||||
],
|
||||
source: 'APM Store'
|
||||
source: 'APM Store',
|
||||
retry: false
|
||||
};
|
||||
|
||||
downloads.value.push(download);
|
||||
|
||||
// 模拟下载进度(实际应该调用真实的下载 API)
|
||||
// simulateDownload(download);
|
||||
|
||||
// Send to main process to start download
|
||||
window.ipcRenderer.send('queue-install', download);
|
||||
window.ipcRenderer.send('queue-install', JSON.stringify(download));
|
||||
|
||||
const encodedPkg = encodeURIComponent(currentApp.value.Pkgname);
|
||||
// const encodedPkg = encodeURIComponent(currentApp.value.Pkgname);
|
||||
// openApmStoreUrl(`apmstore://install?pkg=${encodedPkg}`, {
|
||||
// fallbackText: `/usr/bin/apm-installer --install ${currentApp.value.Pkgname}`
|
||||
// });
|
||||
};
|
||||
|
||||
export const handleRetry = (download_: DownloadItem) => {
|
||||
if (!download_?.pkgname) return;
|
||||
download_.retry = true;
|
||||
// Send to main process to start download
|
||||
window.ipcRenderer.send('queue-install', JSON.stringify(download_));
|
||||
};
|
||||
|
||||
window.ipcRenderer.on('install-status', (_event, log: InstallLog) => {
|
||||
const downloadObj: any = downloads.value.find(d => d.id === log.id);
|
||||
downloadObj.status = log.message;
|
||||
Reference in New Issue
Block a user