// 求和数据管理 let sumDataCurrentPage = 1; let sumDataPageSize = 10; let editingSumDataId = null; let sumDataGroupedList = []; let sumDataDynamicFields = []; // 分组并渲染求和数据列表 function renderSumDataList(rawList) { const tbody = document.getElementById('sum-data-list'); const totalColCount = 7 + (rawList ? 0 : 0) + 3; // 占位,后面重新计算 if (!rawList || rawList.length === 0) { // 清空动态表头 const chargeTh = document.getElementById('th-charge-degree'); const totalOrdersTh = document.getElementById('th-total-orders'); let nextSibling = chargeTh.nextElementSibling; while (nextSibling && nextSibling !== totalOrdersTh) { const toRemove = nextSibling; nextSibling = nextSibling.nextElementSibling; toRemove.remove(); } tbody.innerHTML = '暂无数据'; return; } // 收集所有动态求和字段(除了充电电量 charge_degree) const fieldMap = {}; rawList.forEach(item => { const key = item.sum_field_key; if (key && key !== 'charge_degree' && !fieldMap[key]) { fieldMap[key] = item.sum_field_name || key; } }); // 排序:峰平谷按顺序 const fieldOrder = ['sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity', 'peak_degree', 'peak_time_degree', 'normal_time_degree', 'valley_time_degree']; sumDataDynamicFields = Object.keys(fieldMap).sort((a, b) => { const idxA = fieldOrder.indexOf(a); const idxB = fieldOrder.indexOf(b); if (idxA >= 0 && idxB >= 0) return idxA - idxB; if (idxA >= 0) return -1; if (idxB >= 0) return 1; return a.localeCompare(b); }); // 渲染动态列表头(在充电电量列之后插入) const chargeTh = document.getElementById('th-charge-degree'); const totalOrdersTh = document.getElementById('th-total-orders'); // 先移除旧的动态 th(充电电量和订单数之间的) let nextSibling = chargeTh.nextElementSibling; while (nextSibling && nextSibling !== totalOrdersTh) { const toRemove = nextSibling; nextSibling = nextSibling.nextElementSibling; toRemove.remove(); } // 插入新的动态 th sumDataDynamicFields.forEach(key => { const name = fieldMap[key]; const th = document.createElement('th'); th.style.minWidth = '110px'; th.textContent = name; totalOrdersTh.before(th); }); // 按 报表日期 + 配置ID + 拆分值 分组 const groupMap = {}; rawList.forEach(item => { const groupKey = `${item.report_date}_${item.config_id}_${item.split_value}`; if (!groupMap[groupKey]) { groupMap[groupKey] = { report_date: item.report_date, config_id: item.config_id, config_name: item.config_name, split_type: item.split_type, split_value: item.split_value, split_name: item.split_name, sort_order: item.sort_order || 9999, total_orders: item.total_orders || 0, data_source: item.data_source, remark: item.remark || '', fields: {}, ids: [] }; } groupMap[groupKey].fields[item.sum_field_key] = { value: item.sum_value, id: item.id, name: item.sum_field_name }; groupMap[groupKey].ids.push(item.id); // 取最新的备注和数据来源 if (item.remark) groupMap[groupKey].remark = item.remark; if (item.data_source === 'manual') groupMap[groupKey].data_source = 'manual'; }); sumDataGroupedList = Object.values(groupMap).sort((a, b) => { if (a.report_date !== b.report_date) return b.report_date.localeCompare(a.report_date); return (a.sort_order || 9999) - (b.sort_order || 9999); }); // 分页处理 const total = sumDataGroupedList.length; const start = (sumDataCurrentPage - 1) * sumDataPageSize; const pageData = sumDataGroupedList.slice(start, start + sumDataPageSize); const totalColSpan = 9 + sumDataDynamicFields.length; // 复选+日期+配置+拆分值+充电电量+动态列+订单数+来源+备注+操作 if (pageData.length === 0) { tbody.innerHTML = `暂无数据`; renderSumDataPagination(total); return; } tbody.innerHTML = pageData.map(group => { const chargeDegree = group.fields['charge_degree']; const chargeValue = chargeDegree && chargeDegree.value !== null && chargeDegree.value !== undefined ? Number(chargeDegree.value).toFixed(3) : '-'; const dataSource = group.data_source === 'auto' ? '自动采集' : '手动编辑'; const dataSourceClass = group.data_source === 'auto' ? 'status-active' : 'status-inactive'; const remark = group.remark || '-'; const dynamicCells = sumDataDynamicFields.map(key => { const field = group.fields[key]; const val = field && field.value !== null && field.value !== undefined ? Number(field.value).toFixed(3) : '-'; return `${val}`; }).join(''); const firstId = chargeDegree ? chargeDegree.id : (group.ids[0] || 0); return ` ${group.report_date || '-'} ${group.config_name || '-'} ${group.split_name || group.split_value || '-'} ${chargeValue} ${dynamicCells} ${group.total_orders || 0} ${dataSource} ${remark} `; }).join(''); renderSumDataPagination(total); } // 渲染分页 function renderSumDataPagination(total) { const pagination = document.getElementById('sum-data-pagination'); const totalPages = Math.ceil(total / sumDataPageSize); if (totalPages <= 1) { pagination.innerHTML = ''; return; } let html = ''; if (sumDataCurrentPage > 1) { html += ``; } html += `第 ${sumDataCurrentPage} / ${totalPages} 页,共 ${total} 条`; if (sumDataCurrentPage < totalPages) { html += ``; } pagination.innerHTML = html; } // 加载求和数据 async function loadSumData(page = 1) { sumDataCurrentPage = page; const startDate = document.getElementById('sum-data-start-date').value; const endDate = document.getElementById('sum-data-end-date').value; const configName = document.getElementById('sum-data-config-name').value; const dataSource = document.getElementById('sum-data-source').value; const listTbody = document.getElementById('sum-data-list'); if (listTbody) { listTbody.innerHTML = '加载中...'; } const params = new URLSearchParams(); params.append('page_size', '1000'); if (startDate) params.append('start_date', startDate); if (endDate) params.append('end_date', endDate); if (configName) params.append('config_name', configName); if (dataSource) params.append('data_source', dataSource); try { const response = await fetch(`/api/sum-data?${params.toString()}&_t=${Date.now()}`); const result = await response.json(); if (result.success) { renderSumDataList(result.data.list || []); } else { if (listTbody) { listTbody.innerHTML = '加载失败: ' + (result.message || '未知错误') + ''; } } } catch (error) { if (listTbody) { listTbody.innerHTML = '加载失败: ' + error.message + ''; } } } // 清除查询 function clearSumDataQuery() { document.getElementById('sum-data-start-date').value = ''; document.getElementById('sum-data-end-date').value = ''; document.getElementById('sum-data-config-name').value = ''; document.getElementById('sum-data-source').value = ''; loadSumData(1); } // 全选/取消全选 function toggleSelectAllSumData() { const checked = document.getElementById('select-all-sum-data').checked; document.querySelectorAll('.sum-data-checkbox').forEach(cb => { cb.checked = checked; }); } // 从历史记录采集 async function collectSumDataFromHistory() { if (!confirm('确定要从所有成功的历史记录中重新采集求和数据吗?\n(会先清除旧的自动采集数据)')) return; const resultDiv = document.getElementById('sum-data-list'); resultDiv.innerHTML = '正在采集中,请稍候...'; try { const response = await fetch('/api/sum-data/collect', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); const result = await response.json(); if (result.success) { alert(result.message); loadSumData(1); } else { alert('采集失败: ' + (result.message || '未知错误')); loadSumData(1); } } catch (error) { alert('采集失败: ' + error.message); loadSumData(1); } } // 显示添加模态框 function showAddSumDataModal() { editingSumDataId = null; document.getElementById('sum-data-modal-title').textContent = '手动添加求和数据'; document.getElementById('sum-data-id').value = ''; document.getElementById('sum-data-report-date-input').value = ''; document.getElementById('sum-data-config-name-input').value = ''; document.getElementById('sum-data-split-type-input').value = 'company_id'; document.getElementById('sum-data-split-value-input').value = ''; document.getElementById('sum-data-split-name-input').value = ''; document.getElementById('sum-data-charge-degree-input').value = ''; document.getElementById('sum-data-peak-degree-input').value = ''; document.getElementById('sum-data-peak-time-input').value = ''; document.getElementById('sum-data-normal-time-input').value = ''; document.getElementById('sum-data-valley-time-input').value = ''; document.getElementById('sum-data-total-orders-input').value = 0; document.getElementById('sum-data-remark-input').value = ''; document.getElementById('sum-data-modal').classList.add('active'); } // 显示编辑分组模态框 let editingGroupIds = []; let editingGroupData = null; async function showEditSumDataGroup(ids) { try { const response = await fetch('/api/sum-data/batch-query', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: ids }) }); const result = await response.json(); if (!result.success) { alert('加载详情失败'); return; } const groupItems = result.data || []; if (groupItems.length === 0) { alert('未找到该记录'); return; } editingGroupIds = ids; editingGroupData = groupItems; const chargeItem = groupItems.find(i => i.sum_field_key === 'charge_degree') || groupItems[0]; document.getElementById('sum-data-modal-title').textContent = '编辑求和数据'; document.getElementById('sum-data-id').value = chargeItem.id; document.getElementById('sum-data-report-date-input').value = chargeItem.report_date || ''; document.getElementById('sum-data-config-name-input').value = chargeItem.config_name || ''; document.getElementById('sum-data-split-type-input').value = chargeItem.split_type || 'company_id'; document.getElementById('sum-data-split-value-input').value = chargeItem.split_value || ''; document.getElementById('sum-data-split-name-input').value = chargeItem.split_name || ''; renderEditGroupFields(groupItems); document.getElementById('sum-data-total-orders-input').value = chargeItem.total_orders || 0; document.getElementById('sum-data-remark-input').value = chargeItem.remark || ''; document.getElementById('sum-data-modal').classList.add('active'); } catch (error) { alert('加载详情失败: ' + error.message); } } // 渲染编辑分组时的动态字段输入 function renderEditGroupFields(items) { // 隐藏静态字段输入区域(整个求和字段值区域) const chargeDegreeInput = document.getElementById('sum-data-charge-degree-input'); if (chargeDegreeInput) { chargeDegreeInput.parentElement.parentElement.parentElement.style.display = 'none'; } // 先构造动态字段区域 let fieldsHtml = '
'; fieldsHtml += '
'; // 先放充电电量 const chargeItem = items.find(i => i.sum_field_key === 'charge_degree'); if (chargeItem) { fieldsHtml += `
`; } // 再放其他字段 items.filter(i => i.sum_field_key !== 'charge_degree').forEach(item => { fieldsHtml += `
`; }); fieldsHtml += '
'; // 插入到订单数前面 const totalOrdersGroup = document.getElementById('sum-data-total-orders-input').parentElement; // 检查是否已经有动态区域了 let dynamicArea = document.getElementById('sum-data-dynamic-fields'); if (!dynamicArea) { dynamicArea = document.createElement('div'); dynamicArea.id = 'sum-data-dynamic-fields'; totalOrdersGroup.parentElement.insertBefore(dynamicArea, totalOrdersGroup); } dynamicArea.innerHTML = fieldsHtml; } // 保存求和数据(支持分组编辑) async function saveSumData() { const remark = document.getElementById('sum-data-remark-input').value; // 编辑时修改原因为必填 if (editingGroupIds && editingGroupIds.length > 0 && !remark.trim()) { alert('请填写修改原因'); return; } const totalOrders = document.getElementById('sum-data-total-orders-input').value; try { // 分组编辑模式 if (editingGroupIds && editingGroupIds.length > 0) { // 读取动态字段值 const fieldInputs = document.querySelectorAll('#sum-data-dynamic-fields input[data-field-key]'); let successCount = 0; for (const input of fieldInputs) { const fieldId = input.getAttribute('data-field-id'); const fieldValue = input.value; console.log(`更新字段ID: ${fieldId}, 值: ${fieldValue}`); const response = await fetch(`/api/sum-data/${fieldId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sum_value: fieldValue, total_orders: totalOrders, remark: remark }) }); const result = await response.json(); console.log(`更新结果: ${JSON.stringify(result)}`); if (result.success) successCount++; } if (successCount > 0) { alert(`更新成功(${successCount} 个字段)`); closeSumDataModal(); loadSumData(sumDataCurrentPage); } else { alert('更新失败'); } return; } // 单条新增模式(批量添加多个字段) const fieldsData = []; const chargeDegree = document.getElementById('sum-data-charge-degree-input').value; const peakDegree = document.getElementById('sum-data-peak-degree-input').value; const peakTime = document.getElementById('sum-data-peak-time-input').value; const normalTime = document.getElementById('sum-data-normal-time-input').value; const valleyTime = document.getElementById('sum-data-valley-time-input').value; if (!chargeDegree && !peakDegree && !peakTime && !normalTime && !valleyTime) { alert('请至少输入一个求和字段值'); return; } if (chargeDegree) { fieldsData.push({ sum_field_key: 'charge_degree', sum_field_name: '充电电量(kWh)', sum_value: chargeDegree }); } if (peakDegree) { fieldsData.push({ sum_field_key: 'peak_degree', sum_field_name: '尖时电量(kWh)', sum_value: peakDegree }); } if (peakTime) { fieldsData.push({ sum_field_key: 'peak_time_degree', sum_field_name: '峰时电量(kWh)', sum_value: peakTime }); } if (normalTime) { fieldsData.push({ sum_field_key: 'normal_time_degree', sum_field_name: '平时电量(kWh)', sum_value: normalTime }); } if (valleyTime) { fieldsData.push({ sum_field_key: 'valley_time_degree', sum_field_name: '谷时电量(kWh)', sum_value: valleyTime }); } const data = { report_date: document.getElementById('sum-data-report-date-input').value, config_name: document.getElementById('sum-data-config-name-input').value, split_type: document.getElementById('sum-data-split-type-input').value, split_value: document.getElementById('sum-data-split-value-input').value, split_name: document.getElementById('sum-data-split-name-input').value, total_orders: totalOrders, remark: remark, fields: fieldsData }; if (!data.report_date) { alert('请选择报表日期'); return; } const response = await fetch('/api/sum-data/batch-add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { alert('添加成功'); closeSumDataModal(); loadSumData(sumDataCurrentPage); } else { alert('保存失败: ' + (result.message || '未知错误')); } } catch (error) { alert('保存失败: ' + error.message); } } // 关闭模态框 function closeSumDataModal() { document.getElementById('sum-data-modal').classList.remove('active'); editingGroupIds = []; editingGroupData = null; // 清空新增字段输入 document.getElementById('sum-data-charge-degree-input').value = ''; document.getElementById('sum-data-peak-degree-input').value = ''; document.getElementById('sum-data-peak-time-input').value = ''; document.getElementById('sum-data-normal-time-input').value = ''; document.getElementById('sum-data-valley-time-input').value = ''; // 恢复静态字段区域显示 const chargeDegreeInput = document.getElementById('sum-data-charge-degree-input'); if (chargeDegreeInput) { chargeDegreeInput.parentElement.parentElement.parentElement.style.display = ''; } // 清理动态区域 const dynamicArea = document.getElementById('sum-data-dynamic-fields'); if (dynamicArea) { dynamicArea.remove(); } } // 删除单条(分组) async function deleteSumDataGroup(ids) { if (!confirm(`确定要删除这组求和数据吗?(共 ${ids.length} 条字段记录)`)) return; try { let success = true; for (const id of ids) { const response = await fetch(`/api/sum-data/${id}`, { method: 'DELETE' }); const result = await response.json(); if (!result.success) success = false; } if (success) { alert('删除成功'); loadSumData(sumDataCurrentPage); } else { alert('部分删除失败'); } } catch (error) { alert('删除失败: ' + error.message); } } // 兼容旧函数名 function deleteSumData(id) { deleteSumDataGroup([id]); } // 批量删除 async function batchDeleteSumData() { const checkboxes = document.querySelectorAll('.sum-data-checkbox:checked'); if (checkboxes.length === 0) { alert('请选择要删除的记录'); return; } const ids = []; checkboxes.forEach(cb => { const val = cb.value; if (val.indexOf(',') >= 0) { val.split(',').forEach(id => ids.push(parseInt(id))); } else { ids.push(parseInt(val)); } }); if (!confirm(`确定要删除选中的 ${ids.length} 条求和数据吗?`)) return; try { const response = await fetch('/api/sum-data/batch-delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: ids }) }); const result = await response.json(); if (result.success) { alert('删除成功'); loadSumData(sumDataCurrentPage); } else { alert('删除失败: ' + (result.message || '未知错误')); } } catch (error) { alert('删除失败: ' + error.message); } } async function exportSumData() { const startDate = document.getElementById('sum-data-start-date').value; const endDate = document.getElementById('sum-data-end-date').value; const configName = document.getElementById('sum-data-config-name').value; const dataSource = document.getElementById('sum-data-source').value; const params = new URLSearchParams(); if (startDate) params.append('start_date', startDate); if (endDate) params.append('end_date', endDate); if (configName) params.append('keyword', configName); if (dataSource) params.append('data_source', dataSource); try { const response = await fetch(`/api/sum-data/export?${params.toString()}`); const result = await response.json(); if (result.success) { if (result.total === 0) { alert('没有可导出的数据'); return; } const a = document.createElement('a'); a.href = result.file_path; a.download = result.filename || '求和数据导出.xlsx'; a.target = '_blank'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } else { alert('导出失败: ' + (result.message || '未知错误')); } } catch (error) { alert('导出失败: ' + error.message); } }