// 求和数据管理
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 = '';
// 插入到订单数前面
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);
}
}