// 加载生成配置列表 async function loadGenerateConfigs() { try { const response = await fetch('/api/config'); const result = await response.json(); if (result.success) { const container = document.getElementById('generate-config-list'); container.innerHTML = result.data.map(c => ` `).join(''); initGenerateState(); } } catch (error) { console.error('加载配置失败:', error); } } // 全选/取消全选配置 function toggleSelectAllConfigs() { const checkboxes = document.querySelectorAll('.config-checkbox'); if (checkboxes.length === 0) return; // 检查是否所有复选框都被选中 const allChecked = Array.from(checkboxes).every(cb => cb.checked); // 切换所有复选框的状态 checkboxes.forEach(cb => { cb.checked = !allChecked; }); } // 设置预设时间 function setPresetTime(preset) { const now = new Date(); let start, end; if (preset === 'yesterday') { end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0); start = new Date(end.getTime() - 24 * 60 * 60 * 1000); } else if (preset === 'last3days') { end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0); start = new Date(end.getTime() - 3 * 24 * 60 * 60 * 1000); } else if (preset === 'last7days') { end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0); start = new Date(end.getTime() - 7 * 24 * 60 * 60 * 1000); } document.getElementById('start-time').value = formatDateTimeLocal(start); document.getElementById('end-time').value = formatDateTimeLocal(end); } // 格式化日期时间(用于输入框) function formatDateTimeLocal(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day}T${hours}:${minutes}`; } // 格式化日期时间为数字格式(用于显示) function formatDateTimeDisplay(dateTimeStr) { if (!dateTimeStr) return '-'; // 如果是字符串,直接返回(已经是数字格式) if (typeof dateTimeStr === 'string') { // 如果是 ISO 格式或其他格式,转换为 YYYY-MM-DD HH:MM:SS const date = new Date(dateTimeStr); if (!isNaN(date.getTime())) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } return dateTimeStr; } return String(dateTimeStr); } // 格式化日期为数字格式(只显示日期,用于显示) function formatDateDisplay(dateStr) { if (!dateStr) return '-'; const date = new Date(dateStr); if (!isNaN(date.getTime())) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } return String(dateStr); } let isGenerating = false; let generatePollTimer = null; // 保存生成状态到 localStorage function saveGenerateState(configIds, startTime) { const state = { configIds: configIds, startTime: startTime }; localStorage.setItem('report_generate_state', JSON.stringify(state)); } // 从 localStorage 读取生成状态 function getGenerateState() { const stateStr = localStorage.getItem('report_generate_state'); if (!stateStr) return null; try { return JSON.parse(stateStr); } catch (e) { return null; } } // 清除生成状态 function clearGenerateState() { localStorage.removeItem('report_generate_state'); } // 检查生成是否完成 async function checkGenerateComplete() { const state = getGenerateState(); if (!state) { stopGeneratePolling(); setGenerateControlsDisabled(false); isGenerating = false; return; } const { configIds, startTime } = state; try { const response = await fetch('/api/report/history?page=1&page_size=50'); const result = await response.json(); if (result.success && result.data && result.data.list) { const historyList = result.data.list; let allDone = true; for (const configId of configIds) { const configHistory = historyList.filter(h => h.config_id === configId); if (configHistory.length === 0) { allDone = false; break; } const latest = configHistory[0]; const latestTime = new Date(latest.start_time).getTime(); if (latestTime < startTime) { allDone = false; break; } } if (allDone) { stopGeneratePolling(); clearGenerateState(); setGenerateControlsDisabled(false); isGenerating = false; const resultDiv = document.getElementById('generate-result'); if (resultDiv && !resultDiv.innerHTML.includes('生成完成')) { resultDiv.innerHTML = '
检测到报表已生成完成,请在历史记录中查看。
'; } } } } catch (e) { console.error('检查生成状态失败:', e); } } // 开始轮询 function startGeneratePolling() { stopGeneratePolling(); generatePollTimer = setInterval(checkGenerateComplete, 3000); } // 停止轮询 function stopGeneratePolling() { if (generatePollTimer) { clearInterval(generatePollTimer); generatePollTimer = null; } } // 页面加载时检查生成状态 function initGenerateState() { const state = getGenerateState(); if (state) { const { startTime, configIds } = state; const now = Date.now(); const elapsed = now - startTime; if (elapsed > 30 * 60 * 1000) { clearGenerateState(); return; } isGenerating = true; setGenerateControlsDisabled(true); startGeneratePolling(); const resultDiv = document.getElementById('generate-result'); if (resultDiv) { let html = '
'; html += '
'; html += '
'; html += '检测到有正在生成的报表'; html += `${configIds.length}个配置`; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '正在后台生成中,完成后将自动恢复...'; html += '
'; html += '
'; resultDiv.innerHTML = html; } } } // 渲染生成中动态效果 function renderGenerateLoading(configNames, currentIndex) { const total = configNames.length; const progress = Math.round((currentIndex / total) * 100); let html = '
'; html += '
'; html += '
'; html += '正在生成日报'; html += `${currentIndex}/${total}`; html += '
'; html += '
'; html += `
`; html += '
'; html += '
'; configNames.forEach((name, index) => { let status = 'pending'; let icon = '○'; let statusText = '等待中'; if (index < currentIndex) { status = 'success'; icon = '✓'; statusText = '已完成'; } else if (index === currentIndex) { status = 'running'; icon = '●'; statusText = '生成中...'; } html += `
`; html += `${icon}`; html += `${name}`; html += `${statusText}`; html += '
'; }); html += '
'; html += '
'; const resultDiv = document.getElementById('generate-result'); if (resultDiv) { resultDiv.innerHTML = html; } } // 生成日报(支持多选) async function generateReport() { if (isGenerating) { return; } const checkboxes = document.querySelectorAll('.config-checkbox:checked'); const startTime = document.getElementById('start-time').value; const endTime = document.getElementById('end-time').value; if (checkboxes.length === 0) { showToast('请至少选择一个配置'); return; } if (!startTime || !endTime) { showToast('请选择时间范围'); return; } isGenerating = true; setGenerateControlsDisabled(true); const configIds = Array.from(checkboxes).map(cb => parseInt(cb.value)); const configNames = Array.from(checkboxes).map(cb => cb.dataset.name); const generateStartTime = Date.now(); saveGenerateState(configIds, generateStartTime); startGeneratePolling(); renderGenerateLoading(configNames, 0); let successCount = 0; let failCount = 0; let results = []; for (let i = 0; i < checkboxes.length; i++) { const cb = checkboxes[i]; const configId = parseInt(cb.value); const configName = cb.dataset.name; renderGenerateLoading(configNames, i); try { const response = await fetch('/api/report/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ config_id: configId, start_time: startTime.replace('T', ' ') + ':00', end_time: endTime.replace('T', ' ') + ':00' }) }); const result = await response.json(); if (result.success) { successCount++; results.push({ name: configName, success: true, ...result }); } else { failCount++; results.push({ name: configName, success: false, message: result.message }); } } catch (error) { failCount++; results.push({ name: configName, success: false, message: error.message }); } } // 显示结果 let html = '
生成完成

'; html += `成功:${successCount} 个,失败:${failCount} 个

`; results.forEach(r => { if (r.success) { html += `
`; html += `✓ ${r.name}
`; html += `订单数量:${r.total_orders} 条,总电量:${r.total_degree ? parseFloat(r.total_degree).toFixed(3) : '0.000'} kWh
`; html += `📥 下载`; html += `
`; } else { html += `
`; html += `✗ ${r.name}
`; html += `错误:${r.message}`; html += `
`; } }); html += '
'; const resultDiv = document.getElementById('generate-result'); if (resultDiv) { resultDiv.innerHTML = html; } isGenerating = false; setGenerateControlsDisabled(false); clearGenerateState(); stopGeneratePolling(); } // 设置生成相关控件的禁用状态 function setGenerateControlsDisabled(disabled) { const generateBtn = document.getElementById('generate-btn'); if (generateBtn) { generateBtn.disabled = disabled; generateBtn.style.opacity = disabled ? '0.6' : '1'; generateBtn.style.cursor = disabled ? 'not-allowed' : 'pointer'; } document.querySelectorAll('.config-checkbox').forEach(cb => { cb.disabled = disabled; }); document.getElementById('start-time').disabled = disabled; document.getElementById('end-time').disabled = disabled; document.querySelectorAll('[onclick^="setPresetTime"]').forEach(btn => { btn.style.opacity = disabled ? '0.6' : '1'; btn.style.pointerEvents = disabled ? 'none' : 'auto'; }); }