From 4ca161395927ea0c0e45015dcd660738d0fc882f Mon Sep 17 00:00:00 2001 From: 779776787 <226029138+xMydev@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:38:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=8B=E8=BD=BD=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为下载过程增加实时的下载进度提示 --- assets/js/main.js | 284 ++++++++++++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 111 deletions(-) diff --git a/assets/js/main.js b/assets/js/main.js index eb4a1b2..c684c2a 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -182,6 +182,41 @@ document.addEventListener('DOMContentLoaded', () => { } } + /** + * @description 更新单个爬虫规则项的综合下载状态 + * @param {number} index - 爬虫规则的索引 + */ + function updateCombinedSiteStatus(index) { + const site = currentRulesData.sites[index]; + if (!site) return; + + const assetsToCheck = ['jar', 'ext', 'api']; + const statuses = []; + + assetsToCheck.forEach(key => { + const assetId = `site-${index}-${key}`; + if (downloadStatus.hasOwnProperty(assetId)) { + statuses.push(downloadStatus[assetId]); + } + }); + + if (statuses.length === 0) { + updateDownloadStatusUI(`site-item-${index}`, ''); // 如果没有可下载资源,则清除状态 + return; + } + + let combinedStatus = 'downloaded'; + if (statuses.some(s => s === 'failed')) { + combinedStatus = 'failed'; + } else if (statuses.some(s => s === 'downloading')) { + combinedStatus = 'downloading'; + } else if (statuses.some(s => s === 'pending')) { + combinedStatus = 'pending'; + } + + updateDownloadStatusUI(`site-item-${index}`, combinedStatus); + } + /** * @description 从URL加载并渲染规则 */ @@ -786,9 +821,9 @@ document.addEventListener('DOMContentLoaded', () => { /** - * @description 启动下载流程 + * @description 启动下载流程 (已优化下载状态提示) */ - async function startDownloadProcess(){ + async function startDownloadProcess() { const targetDir = document.getElementById('download-dir-input').value.trim(); const targetFilename = document.getElementById('download-filename-input').value.trim(); const mainJsonUrl = jsonUrlInput.value.trim(); @@ -797,120 +832,150 @@ document.addEventListener('DOMContentLoaded', () => { showToast('目录和文件名不能为空!', 'error'); return; } - - updateCurrentRulesDataFromForm(); // 保存前同步一次数据 - showToast('开始下载流程...', 'info'); - downloadModal.close(); - - document.getElementById('basic').classList.add('show-status'); - document.getElementById('sites').classList.add('show-status'); + + const fileNameElement = document.getElementById('file-name-display'); + const originalTitle = document.title; + const originalFileName = fileNameElement.textContent; + const downloadFailures = new Map(); + const assetsToDownload = []; + downloadStatus = {}; try { - const formData = new FormData(); - formData.append('action', 'save_config'); - formData.append('dir', targetDir); - formData.append('filename', targetFilename); - formData.append('content', JSON.stringify(currentRulesData, null, 2)); - const response = await fetch('index.php/Proxy/saveConfig', { method: 'POST', body: formData }); - if (!response.ok) throw new Error(`服务器返回错误: ${response.status}`); - const result = await response.json(); - if (!result.success) throw new Error(result.message); + showToast('开始下载流程...', 'info'); + downloadModal.close(); + + document.getElementById('basic').classList.add('show-status'); + document.getElementById('sites').classList.add('show-status'); + + updateCurrentRulesDataFromForm(); + let dataToSave = JSON.parse(JSON.stringify(currentRulesData)); + const baseUrl = getBaseUrl(mainJsonUrl); + + const discoverAndRegisterAsset = (originalPath, assetId, site = null) => { + if (!originalPath || typeof originalPath !== 'string') return; + if (site && assetId.endsWith('-ext') && (originalPath.startsWith('http://127.0.0.1') || originalPath.startsWith('http://localhost'))) return; + if (site && (site.type === 1 || site.type === 2)) return; + if (site && assetId.endsWith('-api') && site.type !== 3) return; + if (site && assetId.endsWith('-ext') && (site.api === 'csp_AppYs' || site.api === 'csp_AppYsV2')) return; + + const parsedPath = parseAssetPath(originalPath); + const isLocalRelative = parsedPath.startsWith('./'); + const isRemote = parsedPath.startsWith('http'); + + if (isLocalRelative || isRemote) { + const sourceUrl = isLocalRelative ? new URL(parsedPath, baseUrl).href : parsedPath; + const alreadyExists = assetsToDownload.some(task => task.sourceUrl === sourceUrl); + if (!alreadyExists) { + assetsToDownload.push({ + sourceUrl: sourceUrl, + originalPath: originalPath, + targetRelativePath: parsedPath, + id: assetId + }); + downloadStatus[assetId] = 'pending'; + } + } + }; + + discoverAndRegisterAsset(dataToSave.spider, 'spider'); + (dataToSave.sites || []).forEach((site, index) => { + discoverAndRegisterAsset(site.jar, `site-${index}-jar`, site); + discoverAndRegisterAsset(site.api, `site-${index}-api`, site); + discoverAndRegisterAsset(site.ext, `site-${index}-ext`, site); + }); + + renderAllTabs(currentRulesData); + const totalCount = assetsToDownload.length; + showToast(`共找到 ${totalCount} 个资源需要下载...`, 'info'); + + let downloadedCount = 0; + const updateStatusText = () => { + const statusText = `下载中 (已完成 ${downloadedCount} / 总计 ${totalCount})...`; + document.title = statusText; + fileNameElement.textContent = statusText; + }; + updateStatusText(); + + for (const task of assetsToDownload) { + downloadStatus[task.id] = 'downloading'; + updateDownloadStatusUI(task.id, 'downloading'); + if (task.id.startsWith('site-')) updateCombinedSiteStatus(parseInt(task.id.split('-')[1])); + + try { + const formData = new FormData(); + formData.append('action', 'download_asset'); + formData.append('source_url', task.sourceUrl); + formData.append('target_dir', targetDir); + formData.append('relative_path', task.targetRelativePath); + const response = await fetch('index.php/Proxy/downloadAsset', { method: 'POST', body: formData }); + if (!response.ok) throw new Error(`服务器返回错误: ${response.status}`); + const result = await response.json(); + if (!result.success) throw new Error(result.message); + downloadStatus[task.id] = 'downloaded'; + } catch (error) { + downloadStatus[task.id] = 'failed'; + downloadFailures.set(task.originalPath, error.message); + } + + downloadedCount++; + updateStatusText(); + + updateDownloadStatusUI(task.id, downloadStatus[task.id]); + if (task.id.startsWith('site-')) updateCombinedSiteStatus(parseInt(task.id.split('-')[1])); + } + + const remapPath = (originalPath) => { + if (downloadFailures.has(originalPath)) return originalPath; + for (const asset of assetsToDownload) { + if (asset.originalPath === originalPath) return asset.targetRelativePath; + } + return originalPath; + }; + + dataToSave.spider = remapPath(dataToSave.spider); + (dataToSave.sites || []).forEach(site => { + site.jar = remapPath(site.jar); + site.ext = remapPath(site.ext); + site.api = remapPath(site.api); + }); + + const finalContentToSave = JSON.stringify(dataToSave, null, 2); + const saveFormData = new FormData(); + saveFormData.append('action', 'save_config'); + saveFormData.append('dir', targetDir); + saveFormData.append('filename', targetFilename); + saveFormData.append('content', finalContentToSave); + const saveResponse = await fetch('index.php/Proxy/saveConfig', { method: 'POST', body: saveFormData }); + if (!saveResponse.ok) throw new Error(`服务器返回错误: ${saveResponse.status}`); + const saveResult = await saveResponse.json(); + if (!saveResult.success) throw new Error(saveResult.message); showToast('主配置文件保存成功!', 'success'); + + const failureCount = downloadFailures.size; + let reportMessage = `
总计任务: ${totalCount}
成功: ${totalCount - failureCount}
失败: ${failureCount}
失败列表 (已在配置中保留原始链接):