let historyStartDate = ''; let historyEndDate = ''; let historyStatus = ''; let historyConfigName = ''; let historyPageSize = 10; // 每页显示条数变化 function onHistoryPageSizeChange() { historyPageSize = parseInt(document.getElementById('history-page-size').value) || 10; loadHistoryWithDate(1); } // 状态筛选变化 function onHistoryStatusChange() { historyStatus = document.getElementById('history-status-filter').value; loadHistoryWithDate(1); } // 统一的批量下载:勾选时下载选中记录,未勾选时按筛选条件下载全部匹配记录 async function batchDownloadHistory() { // 同步当前筛选条件 const configNameInput = document.getElementById('history-config-name'); if (configNameInput) { historyConfigName = configNameInput.value.trim(); } historyStatus = document.getElementById('history-status-filter').value; historyStartDate = document.getElementById('history-start-date').value; historyEndDate = document.getElementById('history-end-date').value; const checkboxes = document.querySelectorAll('.history-checkbox:checked'); const hasSelection = checkboxes.length > 0; if (hasSelection) { // 模式1:下载勾选的记录 const ids = Array.from(checkboxes).map(cb => parseInt(cb.value)); const failedCount = Array.from(checkboxes).filter(cb => cb.dataset.status !== '1').length; const successCount = checkboxes.length - failedCount; if (successCount === 0) { showToast('选中的记录都是生成失败的,无法下载'); return; } if (failedCount > 0) { if (!confirm(`选中的 ${checkboxes.length} 条记录中有 ${failedCount} 条是生成失败的,无法下载。\n是否打包下载成功的 ${successCount} 条?`)) { return; } } else { if (!confirm(`确定要下载选中的 ${successCount} 条报表吗?`)) { return; } } try { const response = await fetch('/api/download/batch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: ids }) }); await handleDownloadResponse(response); } catch (error) { showToast('下载失败: ' + error.message); } } else { // 模式2:按筛选条件下载全部匹配的成功记录 const filterDescParts = []; if (historyConfigName) filterDescParts.push(`配置名称含"${historyConfigName}"`); if (historyStartDate) filterDescParts.push(`开始日期 ${historyStartDate}`); if (historyEndDate) filterDescParts.push(`结束日期 ${historyEndDate}`); const filterDesc = filterDescParts.length > 0 ? filterDescParts.join(',') : '全部记录'; // 查询匹配数量 let matchCount = 0; let dateRange = ''; try { const countResponse = await fetch('/api/download/batch-filter-count', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ config_name: historyConfigName, start_date: historyStartDate, end_date: historyEndDate }) }); const countResult = await countResponse.json(); if (countResult.success) { matchCount = countResult.data.total || 0; if (countResult.data.min_date && countResult.data.max_date) { dateRange = `\n报表日期范围: ${countResult.data.min_date} 至 ${countResult.data.max_date}`; } } } catch (e) { console.error('查询导出数量失败:', e); } if (matchCount === 0) { showToast(`没有符合条件的成功记录可导出。\n筛选条件: ${filterDesc}`); return; } if (!confirm(`未选中任何记录,将按当前筛选条件导出全部匹配的成功记录。\n\n筛选条件: ${filterDesc}\n匹配记录数: ${matchCount} 条${dateRange}\n\n注意: 仅导出生成成功的报表。\n是否继续?`)) { return; } try { const response = await fetch('/api/download/batch-filter', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ config_name: historyConfigName, start_date: historyStartDate, end_date: historyEndDate, status: historyStatus }) }); await handleDownloadResponse(response); } catch (error) { showToast('下载失败: ' + error.message); } } } // 处理下载响应(两种模式共用) async function handleDownloadResponse(response) { if (!response.ok) { try { const result = await response.json(); showToast('下载失败: ' + (result.message || '未知错误')); } catch (e) { showToast('下载失败: 服务器返回错误'); } return; } const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const timestamp = new Date().toISOString().slice(0, 19).replace(/[-T:]/g, ''); a.download = `批量下载_${timestamp}.zip`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); // 显示统计信息(如果有) const totalMatched = response.headers.get('X-Total-Matched'); const filesZipped = response.headers.get('X-Files-Zipped'); const filesMissing = response.headers.get('X-Files-Missing'); const skippedFailed = response.headers.get('X-Skipped-Failed'); if (totalMatched && filesZipped) { const missing = filesMissing ? parseInt(filesMissing) : 0; const skipped = skippedFailed ? parseInt(skippedFailed) : 0; let msg = `下载完成!\n`; msg += `匹配记录: ${totalMatched} 条\n`; msg += `成功打包: ${filesZipped} 个文件`; if (missing > 0) { msg += `\n缺失文件: ${filesMissing} 个(历史文件可能已被清理)`; } if (skipped > 0) { msg += `\n跳过失败记录: ${skipped} 个`; } if (missing > 0 || skipped > 0) { showToast(msg); } } } // 按日期查询 function queryHistoryByDate() { const startDate = document.getElementById('history-start-date').value; const endDate = document.getElementById('history-end-date').value; const configNameInput = document.getElementById('history-config-name'); historyStartDate = startDate; historyEndDate = endDate; historyConfigName = configNameInput ? configNameInput.value.trim() : ''; loadHistoryWithDate(1); } // 清除筛选 function clearHistoryFilter() { document.getElementById('history-status-filter').value = ''; document.getElementById('history-start-date').value = ''; document.getElementById('history-end-date').value = ''; const configNameInput = document.getElementById('history-config-name'); if (configNameInput) configNameInput.value = ''; historyStatus = ''; historyStartDate = ''; historyEndDate = ''; historyConfigName = ''; loadHistory(1); } // 带日期参数加载历史记录 // 渲染历史记录列表 function renderHistory(list) { const tbody = document.getElementById('history-list'); if (!list || list.length === 0) { tbody.innerHTML = '暂无历史记录'; return; } // 解析日期字符串(支持多种格式) const parseDate = (dateStr) => { if (!dateStr) return null; // 尝试匹配 "2026-07-12" 格式 let match = dateStr.match(/(\d{4})-(\d{1,2})-(\d{1,2})/); if (match) { return { year: parseInt(match[1]), month: parseInt(match[2]), day: parseInt(match[3]) }; } // 尝试匹配 "Sun, 12 Jul 2026 00:00:00 GMT" 格式 match = dateStr.match(/(\d{1,2})\s+(\w+)\s+(\d{4})/); if (match) { const months = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12 }; return { year: parseInt(match[3]), month: months[match[2]] || 1, day: parseInt(match[1]) }; } return null; }; // 解析时间字符串(支持多种格式) const parseTime = (timeStr) => { if (!timeStr) return null; // 尝试匹配 "2026-07-12 08:00:00" 格式 let match = timeStr.match(/(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})/); if (match) { return { year: match[1], month: match[2].padStart(2, '0'), day: match[3].padStart(2, '0'), hour: match[4].padStart(2, '0'), minute: match[5].padStart(2, '0'), second: match[6].padStart(2, '0') }; } // 尝试匹配 "Sun, 12 Jul 2026 08:00:00 GMT" 格式 match = timeStr.match(/(\d{1,2})\s+(\w+)\s+(\d{4})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})/); if (match) { const months = { 'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12' }; return { year: match[3], month: months[match[2]] || '01', day: match[1].padStart(2, '0'), hour: match[4].padStart(2, '0'), minute: match[5].padStart(2, '0'), second: match[6].padStart(2, '0') }; } return null; }; tbody.innerHTML = list.map(item => { const statusText = item.status === 1 ? '成功' : '失败'; const statusClass = item.status === 1 ? 'status-success' : 'status-failed'; // 格式化报表日期为中文 let reportDate = '-'; const dateObj = parseDate(item.report_date); if (dateObj) { reportDate = `${dateObj.year}年${dateObj.month}月${dateObj.day}日`; } else if (item.report_date) { reportDate = item.report_date; } // 格式化时间范围 let timeRange = '-'; const startObj = parseTime(item.start_time); const endObj = parseTime(item.end_time); if (startObj && endObj) { const formatTimeObj = (obj) => `${obj.year}-${obj.month}-${obj.day} ${obj.hour}:${obj.minute}:${obj.second}`; timeRange = `${formatTimeObj(startObj)} 至 ${formatTimeObj(endObj)}`; } else if (item.start_time && item.end_time) { timeRange = `${item.start_time} 至 ${item.end_time}`; } const configName = item.config_name || '-'; const splitValue = item.split_name || '-'; const totalOrders = item.total_orders || 0; const totalDegree = item.total_degree ? parseFloat(item.total_degree).toFixed(3) : '0.000'; return ` ${reportDate} ${timeRange} ${configName} ${splitValue} ${totalOrders} ${totalDegree} ${statusText} ${item.status === 1 && item.file_path ? `下载` : '-'} `; }).join(''); } // 渲染分页控件 function renderPagination(page, total, pageSize) { const container = document.getElementById('history-pagination'); if (!container) return; const totalPages = Math.ceil(total / pageSize); if (totalPages <= 1) { container.innerHTML = ''; return; } let html = ''; container.innerHTML = html; } // 加载历史记录(无日期筛选) async function loadHistory(page = 1) { loadHistoryWithDate(page); } async function loadHistoryWithDate(page) { try { // 同步配置名称搜索框的值(翻页时保留筛选条件) const configNameInput = document.getElementById('history-config-name'); if (configNameInput) { historyConfigName = configNameInput.value.trim(); } let url = `/api/report/history?page=${page}&page_size=${historyPageSize}`; if (historyStatus) url += `&status=${historyStatus}`; if (historyStartDate) url += `&start_date=${historyStartDate}`; if (historyEndDate) url += `&end_date=${historyEndDate}`; if (historyConfigName) url += `&config_name=${encodeURIComponent(historyConfigName)}`; const response = await fetch(url); const result = await response.json(); if (result.success) { renderHistory(result.data.list); renderPagination(result.data.page, result.data.total, result.data.page_size); } else { showToast('加载失败: ' + (result.error || result.message || '未知错误')); } } catch (error) { showToast('加载失败: ' + error.message); } } // 全选/取消全选历史记录 function toggleSelectAllHistory() { const selectAllCheckbox = document.getElementById('select-all-history'); const checkboxes = document.querySelectorAll('#history-list input[type="checkbox"]'); checkboxes.forEach(cb => { cb.checked = selectAllCheckbox.checked; }); } // 删除单条历史记录 async function deleteHistory(id) { if (!confirm('确定要删除这条历史记录吗?对应的报表文件也会被删除。')) { return; } try { const response = await fetch('/api/report/history/batch-delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: [id] }) }); const result = await response.json(); if (result.success) { showToast('删除成功'); loadHistoryWithDate(); } else { showToast('删除失败: ' + result.message); } } catch (error) { showToast('删除失败: ' + error.message); } } // 批量删除历史记录 async function batchDeleteHistory() { const checkboxes = document.querySelectorAll('.history-checkbox:checked'); if (checkboxes.length === 0) { showToast('请至少选择一条记录'); return; } if (!confirm(`确定要删除选中的 ${checkboxes.length} 条历史记录吗?对应的报表文件也会被删除。`)) { return; } const ids = Array.from(checkboxes).map(cb => parseInt(cb.value)); try { const response = await fetch('/api/report/history/batch-delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: ids }) }); const result = await response.json(); if (result.success) { showToast(result.message); loadHistoryWithDate(); } else { showToast('删除失败: ' + result.message); } } catch (error) { showToast('删除失败: ' + error.message); } } // 批量导出当前筛选条件下的所有记录 - 已合并到 batchDownloadHistory 函数 // 该函数已被移除,功能整合到统一的 batchDownloadHistory 中