807 lines
32 KiB
JavaScript
807 lines
32 KiB
JavaScript
// 求和数据管理
|
||
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 = '<tr><td colspan="10" class="empty">暂无数据</td></tr>';
|
||
return;
|
||
}
|
||
|
||
// 获取全局配置的允许显示字段(charge_degree 始终显示)
|
||
// 如果没有配置,则显示所有字段(向后兼容)
|
||
const hasConfig = sumDataConfigFields && sumDataConfigFields.length > 0;
|
||
const allowedFields = hasConfig ? new Set(sumDataConfigFields) : null;
|
||
|
||
// 收集所有动态求和字段(除了充电电量 charge_degree,且只收集允许显示的字段)
|
||
const fieldMap = {};
|
||
rawList.forEach(item => {
|
||
const key = item.sum_field_key;
|
||
if (key && key !== 'charge_degree' && !fieldMap[key]) {
|
||
// 如果配置了允许字段,则只收集允许的字段;否则收集所有字段
|
||
if (!hasConfig || allowedFields.has(key)) {
|
||
fieldMap[key] = item.sum_field_name || key;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 排序:尖峰平谷按顺序
|
||
const fieldOrder = ['sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity'];
|
||
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 = `<tr><td colspan="${totalColSpan}" class="empty">暂无数据</td></tr>`;
|
||
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 `<td>${val}</td>`;
|
||
}).join('');
|
||
|
||
const firstId = chargeDegree ? chargeDegree.id : (group.ids[0] || 0);
|
||
|
||
return `
|
||
<tr>
|
||
<td><input type="checkbox" class="sum-data-checkbox" value="${group.ids.join(',')}"></td>
|
||
<td>${group.report_date || '-'}</td>
|
||
<td>${group.config_name || '-'}</td>
|
||
<td title="${group.split_name || ''}">${group.split_name || group.split_value || '-'}</td>
|
||
<td style="background: #f8fafc; font-weight: 600; color: #1d4ed8;">${chargeValue}</td>
|
||
${dynamicCells}
|
||
<td>${group.total_orders || 0}</td>
|
||
<td><span class="status-badge ${dataSourceClass}">${dataSource}</span></td>
|
||
<td title="${remark}" style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${remark}</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-primary" onclick='showEditSumDataGroup(${JSON.stringify(group.ids)})'>编辑</button>
|
||
<button class="btn btn-sm btn-danger" onclick='deleteSumDataGroup(${JSON.stringify(group.ids)})'>删除</button>
|
||
</td>
|
||
</tr>
|
||
`;
|
||
}).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 += `<button class="btn btn-sm" onclick="loadSumData(${sumDataCurrentPage - 1})">上一页</button>`;
|
||
}
|
||
html += `<span class="page-info">第 ${sumDataCurrentPage} / ${totalPages} 页,共 ${total} 条</span>`;
|
||
if (sumDataCurrentPage < totalPages) {
|
||
html += `<button class="btn btn-sm" onclick="loadSumData(${sumDataCurrentPage + 1})">下一页</button>`;
|
||
}
|
||
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 = '<tr><td colspan="12" class="loading">加载中...</td></tr>';
|
||
}
|
||
|
||
// 先加载全局配置
|
||
try {
|
||
const configResponse = await fetch('/api/global-config/sum-data-fields?_t=' + Date.now());
|
||
const configResult = await configResponse.json();
|
||
if (configResult.success) {
|
||
sumDataConfigFields = configResult.data || [];
|
||
}
|
||
} catch (error) {
|
||
console.log('加载全局配置失败:', error);
|
||
}
|
||
|
||
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 = '<tr><td colspan="12" class="empty">加载失败: ' + (result.message || '未知错误') + '</td></tr>';
|
||
}
|
||
}
|
||
} catch (error) {
|
||
if (listTbody) {
|
||
listTbody.innerHTML = '<tr><td colspan="12" class="empty">加载失败: ' + error.message + '</td></tr>';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 清除查询
|
||
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() {
|
||
const startDate = document.getElementById('sum-data-start-date').value;
|
||
const endDate = document.getElementById('sum-data-end-date').value;
|
||
|
||
if (!startDate || !endDate) {
|
||
alert('请先选择日期范围');
|
||
return;
|
||
}
|
||
|
||
if (!confirm(`确定要从 ${startDate} 到 ${endDate} 的历史记录中采集求和数据吗?\n(会先清除该日期范围内旧的自动采集数据)`)) return;
|
||
|
||
const resultDiv = document.getElementById('sum-data-list');
|
||
resultDiv.innerHTML = '<tr><td colspan="12" class="empty">正在采集中,请稍候...</td></tr>';
|
||
|
||
try {
|
||
const response = await fetch('/api/sum-data/collect', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
start_date: startDate,
|
||
end_date: endDate
|
||
})
|
||
});
|
||
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 = '<div style="margin-bottom: 15px;"><label style="display: block; margin-bottom: 8px; font-weight: 600;">求和字段值:</label>';
|
||
fieldsHtml += '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">';
|
||
|
||
// 先放充电电量
|
||
const chargeItem = items.find(i => i.sum_field_key === 'charge_degree');
|
||
if (chargeItem) {
|
||
fieldsHtml += `
|
||
<div class="form-group" style="margin-bottom: 0;">
|
||
<label>${chargeItem.sum_field_name || chargeItem.sum_field_key} (必选):</label>
|
||
<input type="number" step="0.001" class="form-control"
|
||
data-field-key="${chargeItem.sum_field_key}"
|
||
data-field-name="${chargeItem.sum_field_name || chargeItem.sum_field_key}"
|
||
data-field-id="${chargeItem.id}"
|
||
value="${chargeItem.sum_value || 0}">
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// 再放其他字段
|
||
items.filter(i => i.sum_field_key !== 'charge_degree').forEach(item => {
|
||
fieldsHtml += `
|
||
<div class="form-group" style="margin-bottom: 0;">
|
||
<label>${item.sum_field_name || item.sum_field_key}:</label>
|
||
<input type="number" step="0.001" class="form-control"
|
||
data-field-key="${item.sum_field_key}"
|
||
data-field-name="${item.sum_field_name || item.sum_field_key}"
|
||
data-field-id="${item.id}"
|
||
value="${item.sum_value || 0}">
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
fieldsHtml += '</div></div>';
|
||
|
||
// 插入到订单数前面
|
||
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) {
|
||
// 先获取分组信息,确定需要删除的自动采集记录
|
||
if (editingGroupData && editingGroupData.length > 0) {
|
||
const chargeItem = editingGroupData.find(i => i.sum_field_key === 'charge_degree') || editingGroupData[0];
|
||
|
||
// 查询该分组下所有字段的ID(用于删除重复的自动采集记录)
|
||
const deleteResponse = await fetch('/api/sum-data/batch-query', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
conditions: {
|
||
report_date: chargeItem.report_date,
|
||
config_id: chargeItem.config_id,
|
||
split_type: chargeItem.split_type,
|
||
split_value: chargeItem.split_value,
|
||
data_source: 'auto'
|
||
}
|
||
})
|
||
});
|
||
const deleteResult = await deleteResponse.json();
|
||
|
||
// 删除所有自动采集的记录(避免重复)
|
||
if (deleteResult.success && deleteResult.data && deleteResult.data.length > 0) {
|
||
const autoIds = deleteResult.data.map(d => d.id);
|
||
for (const autoId of autoIds) {
|
||
await fetch(`/api/sum-data/${autoId}`, { method: 'DELETE' });
|
||
}
|
||
console.log(`已删除 ${autoIds.length} 条自动采集记录`);
|
||
}
|
||
}
|
||
|
||
// 读取动态字段值(这些是手动编辑后的记录)
|
||
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,
|
||
data_source: 'manual'
|
||
})
|
||
});
|
||
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: 'sharp_electricity',
|
||
sum_field_name: '尖时电量(kWh)',
|
||
sum_value: peakDegree
|
||
});
|
||
}
|
||
if (peakTime) {
|
||
fieldsData.push({
|
||
sum_field_key: 'peak_electricity',
|
||
sum_field_name: '峰时电量(kWh)',
|
||
sum_value: peakTime
|
||
});
|
||
}
|
||
if (normalTime) {
|
||
fieldsData.push({
|
||
sum_field_key: 'flat_electricity',
|
||
sum_field_name: '平时电量(kWh)',
|
||
sum_value: normalTime
|
||
});
|
||
}
|
||
if (valleyTime) {
|
||
fieldsData.push({
|
||
sum_field_key: 'valley_electricity',
|
||
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);
|
||
}
|
||
}
|
||
|
||
// 求和数据可配置的采集字段列表
|
||
const SUM_DATA_AVAILABLE_FIELDS = [
|
||
{ key: 'sharp_electricity', name: '尖时电量(kWh)' },
|
||
{ key: 'peak_electricity', name: '峰时电量(kWh)' },
|
||
{ key: 'flat_electricity', name: '平时电量(kWh)' },
|
||
{ key: 'valley_electricity', name: '谷时电量(kWh)' },
|
||
{ key: 'charge_elecfee_amount', name: '实收电费(元)' },
|
||
{ key: 'charge_servicefee_amount', name: '实收服务费(元)' },
|
||
{ key: 'charge_amount', name: '实收总金额(元)' },
|
||
{ key: 'charge_frequency', name: '充电次数' },
|
||
{ key: 'custom_service_fee', name: '自定义服务费(元)' },
|
||
{ key: 'custom_total_amount', name: '自定义实收总金额(元)' }
|
||
];
|
||
|
||
let sumDataConfigFields = [];
|
||
|
||
// 初始化全局配置
|
||
async function initSumDataConfig() {
|
||
try {
|
||
const response = await fetch('/api/global-config/sum-data-fields?_t=' + Date.now());
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
sumDataConfigFields = result.data || [];
|
||
}
|
||
} catch (error) {
|
||
console.log('初始化全局配置失败:', error);
|
||
}
|
||
}
|
||
|
||
// 页面加载时初始化
|
||
document.addEventListener('DOMContentLoaded', initSumDataConfig);
|
||
|
||
// 显示求和数据全局配置模态框
|
||
async function showSumDataConfigModal() {
|
||
const modal = document.getElementById('sum-data-config-modal');
|
||
const fieldsList = document.getElementById('sum-data-config-fields-list');
|
||
fieldsList.innerHTML = '<div class="loading">加载中...</div>';
|
||
|
||
try {
|
||
const response = await fetch('/api/global-config/sum-data-fields?_t=' + Date.now());
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
sumDataConfigFields = result.data || [];
|
||
renderSumDataConfigFields(sumDataConfigFields);
|
||
} else {
|
||
fieldsList.innerHTML = '<div class="empty">加载失败</div>';
|
||
}
|
||
} catch (error) {
|
||
fieldsList.innerHTML = '<div class="empty">加载失败: ' + error.message + '</div>';
|
||
}
|
||
|
||
modal.classList.add('active');
|
||
}
|
||
|
||
// 关闭求和数据全局配置模态框
|
||
function closeSumDataConfigModal() {
|
||
document.getElementById('sum-data-config-modal').classList.remove('active');
|
||
}
|
||
|
||
// 渲染求和数据配置字段列表
|
||
function renderSumDataConfigFields(selectedFields) {
|
||
const fieldsList = document.getElementById('sum-data-config-fields-list');
|
||
fieldsList.innerHTML = SUM_DATA_AVAILABLE_FIELDS.map(field => `
|
||
<div class="checkbox-item ${selectedFields.includes(field.key) ? 'checked' : ''}" onclick="toggleSumDataConfigField(this)">
|
||
<input type="checkbox" value="${field.key}"
|
||
${selectedFields.includes(field.key) ? 'checked' : ''}>
|
||
<label>${field.name} <span class="field-key-name">(${field.key})</span></label>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// 切换求和数据配置字段选中状态
|
||
function toggleSumDataConfigField(item) {
|
||
const checkbox = item.querySelector('input[type="checkbox"]');
|
||
checkbox.checked = !checkbox.checked;
|
||
item.classList.toggle('checked', checkbox.checked);
|
||
}
|
||
|
||
// 保存求和数据全局配置
|
||
async function saveSumDataConfig() {
|
||
const checkboxes = document.querySelectorAll('#sum-data-config-fields-list input[type="checkbox"]');
|
||
const selectedFields = Array.from(checkboxes)
|
||
.filter(cb => cb.checked)
|
||
.map(cb => cb.value);
|
||
|
||
try {
|
||
const response = await fetch('/api/global-config/sum-data-fields', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ sum_data_fields: selectedFields })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('配置保存成功');
|
||
sumDataConfigFields = selectedFields;
|
||
closeSumDataConfigModal();
|
||
loadSumData(sumDataCurrentPage);
|
||
} else {
|
||
alert('保存失败: ' + (result.message || '未知错误'));
|
||
}
|
||
} catch (error) {
|
||
alert('保存失败: ' + error.message);
|
||
}
|
||
}
|