class DownloadManager { constructor() { this.port = window.location.port || '55830'; this.baseUrl = `http://127.0.0.1:${this.port}/api`; this.currentDownload = null; this.init(); } init() { document.getElementById('port').textContent = this.port; // 绑定事件 document.getElementById('reloadBtn').addEventListener('click', () => this.reloadFolders()); document.getElementById('cleanupBtn').addEventListener('click', () => this.cleanupJson()); document.getElementById('closeModalBtn').addEventListener('click', () => this.hideModal()); document.querySelector('.close-btn').addEventListener('click', () => this.hideModal()); // 点击模态框外部关闭 document.getElementById('downloadModal').addEventListener('click', (e) => { if (e.target === document.getElementById('downloadModal')) { this.hideModal(); } }); // 初始加载 this.reloadFolders(); } showStatus(message, type = 'info') { const statusEl = document.getElementById('statusMessage'); statusEl.textContent = message; statusEl.style.borderLeftColor = type === 'error' ? '#ef4444' : type === 'warning' ? '#f59e0b' : '#10b981'; } showModal(title) { document.getElementById('modalTitle').textContent = title; document.getElementById('downloadModal').style.display = 'flex'; this.resetModal(); } hideModal() { document.getElementById('downloadModal').style.display = 'none'; if (this.currentDownload && this.currentDownload.abort) { this.currentDownload.abort(); } } resetModal() { document.getElementById('progressBar').style.width = '0%'; document.getElementById('progressText').textContent = '0%'; document.getElementById('totalFiles').textContent = '0'; document.getElementById('downloadedFiles').textContent = '0'; document.getElementById('pendingFiles').textContent = '0'; document.getElementById('downloadLog').innerHTML = ''; } updateProgress(progress, stats) { document.getElementById('progressBar').style.width = `${progress}%`; document.getElementById('progressText').textContent = `${Math.round(progress)}%`; if (stats) { document.getElementById('totalFiles').textContent = stats.total || 0; document.getElementById('downloadedFiles').textContent = stats.downloaded || 0; document.getElementById('pendingFiles').textContent = stats.pending || 0; } } addLogEntry(message, type = 'info') { const logEl = document.getElementById('downloadLog'); const entry = document.createElement('div'); entry.className = `log-entry ${type}`; entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; logEl.appendChild(entry); logEl.scrollTop = logEl.scrollHeight; } async reloadFolders() { try { this.showStatus('正在加载文件夹...', 'info'); document.getElementById('reloadBtn').disabled = true; const response = await fetch(`${this.baseUrl}/reload_folders`); const data = await response.json(); if (data.success) { this.displayFolders(data.folders); this.showStatus(`已加载 ${data.folders.length} 个未完成的任务`, 'success'); } else { throw new Error(data.message || '加载失败'); } } catch (error) { console.error('加载失败:', error); this.showStatus(`加载失败: ${error.message}`, 'error'); this.displayFolders([]); } finally { document.getElementById('reloadBtn').disabled = false; } } displayFolders(folders) { const foldersList = document.getElementById('foldersList'); const noFolders = document.getElementById('noFolders'); // 过滤掉进度为100%的文件夹 const incompleteFolders = folders.filter(folder => folder.progress < 100); if (incompleteFolders.length === 0) { foldersList.innerHTML = ''; noFolders.style.display = 'block'; return; } noFolders.style.display = 'none'; foldersList.innerHTML = incompleteFolders.map(folder => `
${this.escapeHtml(folder.title)}
总文件: ${folder.total} 已下载: ${folder.downloaded} 缺失: ${folder.total - folder.downloaded}
${folder.progress.toFixed(1)}%
`).join(''); } async downloadFolder(folderPath, title) { try { this.showModal(`正在下载: ${title}`); this.addLogEntry('开始下载缺失文件...', 'info'); console.log('发送请求:', { folder: folderPath, title: title }); const response = await fetch(`${this.baseUrl}/download_missing`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ folder: folderPath, title: title }) }); // 检查响应状态 if (!response.ok) { const errorText = await response.text(); console.error('HTTP错误:', response.status, errorText); throw new Error(`HTTP ${response.status}: ${errorText}`); } const data = await response.json(); console.log('收到响应:', data); if (data.success) { // 更新进度 const totalSaved = (data.saved || 0) + (data.skipped || 0); const total = data.total || 1; const progress = totalSaved / total * 100; this.updateProgress(progress, { total: total, downloaded: totalSaved, pending: data.failed || 0 }); // 添加日志 if (data.details) { data.details.forEach(detail => { if (detail.status === 'success') { this.addLogEntry(`成功下载: ${detail.key}`, 'success'); } else if (detail.status === 'failed') { this.addLogEntry(`下载失败 ${detail.key}: ${detail.message}`, 'error'); } else if (detail.status === 'skipped') { this.addLogEntry(`跳过: ${detail.key} (${detail.message})`, 'info'); } }); } this.addLogEntry(`下载完成: ${data.message}`, 'success'); // 刷新文件夹列表 setTimeout(() => this.reloadFolders(), 1000); } else { this.addLogEntry(`下载失败: ${data.message}`, 'error'); } } catch (error) { console.error('下载失败:', error); this.addLogEntry(`下载失败: ${error.message}`, 'error'); } } async cleanupJson() { if (!confirm('确定要删除所有JSON文件吗?此操作不可恢复。')) { return; } try { this.showStatus('正在清理JSON文件...', 'info'); document.getElementById('cleanupBtn').disabled = true; const response = await fetch(`${this.baseUrl}/cleanup_json`, { method: 'POST' }); const data = await response.json(); if (data.success) { this.showStatus(data.message, 'success'); // 刷新文件夹列表 setTimeout(() => this.reloadFolders(), 500); } else { throw new Error(data.message || '清理失败'); } } catch (error) { console.error('清理失败:', error); this.showStatus(`清理失败: ${error.message}`, 'error'); } finally { document.getElementById('cleanupBtn').disabled = false; } } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } escapeAttr(text) { return this.escapeHtml(text).replace(/"/g, '"'); } } // 初始化应用 const downloadManager = new DownloadManager();