增加尖峰平谷找平功能,修改消息提示

This commit is contained in:
2026-08-02 10:50:38 +08:00
parent 17b7cd27b9
commit c5fcda3511
10 changed files with 433 additions and 95 deletions

View File

@@ -60,6 +60,13 @@ def create_config():
show_custom_service_fee = 1 if data.get('show_custom_service_fee') else 0
show_total_amount = 1 if data.get('show_total_amount') else 0
total_amount_name = data.get('total_amount_name')
# 找平电量开关默认开启1=开启0=关闭)
# 当数据中的'未在前端传递时默认为1开启保持与历史配置的兼容性
balance_electricity = data.get('balance_electricity')
if balance_electricity is None:
balance_electricity = 1
else:
balance_electricity = 1 if balance_electricity else 0
if not config_name or not split_type or not split_value:
log_warning('[配置API] 创建配置失败:缺少必填字段', 'api')
@@ -81,9 +88,9 @@ def create_config():
(id, config_name, split_type, split_value, split_name, selected_fields,
sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names,
show_monthly_total, custom_service_fee_price, custom_service_fee_name,
show_custom_service_fee, show_total_amount, total_amount_name,
show_custom_service_fee, show_total_amount, total_amount_name, balance_electricity,
is_active, sort_order, create_time, update_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1, %s, NOW(), NOW())
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1, %s, NOW(), NOW())
"""
params = (
@@ -104,6 +111,7 @@ def create_config():
show_custom_service_fee,
show_total_amount,
total_amount_name if total_amount_name else None,
balance_electricity,
sort_order
)
@@ -148,6 +156,12 @@ def update_config(config_id):
show_custom_service_fee = 1 if data.get('show_custom_service_fee') else 0
show_total_amount = 1 if data.get('show_total_amount') else 0
total_amount_name = data.get('total_amount_name')
# 找平电量开关默认开启1=开启0=关闭)
balance_electricity = data.get('balance_electricity')
if balance_electricity is None:
balance_electricity = 1
else:
balance_electricity = 1 if balance_electricity else 0
# 查询原配置,检查拆分是否变化
old_configs = execute_query('SELECT * FROM t_daily_report_config WHERE id = %s', (config_id,))
@@ -162,6 +176,7 @@ def update_config(config_id):
merge_telecom = %s, telecom_vehicle_no = %s, field_custom_names = %s,
show_monthly_total = %s, custom_service_fee_price = %s, custom_service_fee_name = %s,
show_custom_service_fee = %s, show_total_amount = %s, total_amount_name = %s,
balance_electricity = %s,
update_time = NOW()
WHERE id = %s
"""
@@ -183,6 +198,7 @@ def update_config(config_id):
show_custom_service_fee,
show_total_amount,
total_amount_name if total_amount_name else None,
balance_electricity,
config_id
)
@@ -257,8 +273,8 @@ def copy_config(config_id):
# 插入新配置
execute_insert(
'''INSERT INTO t_daily_report_config
(id, config_name, split_type, split_value, split_name, selected_fields, sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names, show_monthly_total, custom_service_fee_price, custom_service_fee_name, show_custom_service_fee, show_total_amount, total_amount_name, is_active, sort_order, create_time, update_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())''',
(id, config_name, split_type, split_value, split_name, selected_fields, sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names, show_monthly_total, custom_service_fee_price, custom_service_fee_name, show_custom_service_fee, show_total_amount, total_amount_name, balance_electricity, is_active, sort_order, create_time, update_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())''',
(
new_id,
new_name,
@@ -277,6 +293,8 @@ def copy_config(config_id):
original.get('show_custom_service_fee', 0),
original.get('show_total_amount', 0),
original.get('total_amount_name'),
# 找平电量复制原配置的设置若原配置无此字段NULL则默认开启(1)
original.get('balance_electricity') if original.get('balance_electricity') is not None else 1,
original['is_active'],
sort_order
)

View File

@@ -182,6 +182,15 @@ def init_database_tables():
"total_amount_name VARCHAR(100) NULL COMMENT '实收金额报表表头名称'",
'total_amount_name'
)
# 给配置表增加 balance_electricity 字段(是否找平电量)
# 当尖+峰+平+谷之和不等于充电电量时,以充电电量为准进行找平
# 找平顺序:谷 → 平 → 峰 → 尖(加到第一个有值的时段上)
_add_column_if_not_exists(
't_daily_report_config',
"balance_electricity TINYINT NULL DEFAULT 1 COMMENT '是否找平电量(0=否,1=是)'",
'balance_electricity'
)
# 创建全局配置表
create_global_config_table = """

View File

@@ -285,6 +285,70 @@ def calculate_time_period_electricity(orders, time_periods_config):
return result
def balance_time_period_electricity(orders):
"""
找平分时段电量
正常情况下:充电电量(charge_degree) = 尖电量 + 峰电量 + 平电量 + 谷电量
但平台导出数据时,四者之和可能不等于充电电量。
本函数以充电电量(charge_degree)为准,将差额加到对应时段上。
找平原则(按优先级顺序):
1. 有谷电量(>0) → 差额加到谷上
2. 没谷有平电量(>0) → 差额加到平上
3. 没平有峰电量(>0) → 差额加到峰上
4. 都没有 → 差额加到尖上
Args:
orders: 订单数据列表(会原地修改 sharp/peak/flat/valley_electricity 字段)
Returns:
int: 实际找平的订单数
"""
balanced_count = 0
for order in orders:
# 获取充电电量(作为基准)
charge_degree = order.get('charge_degree')
if charge_degree is None or charge_degree == '':
continue
try:
charge_degree = float(charge_degree)
except (ValueError, TypeError):
continue
# 获取各时段电量
sharp = float(order.get('sharp_electricity', 0) or 0)
peak = float(order.get('peak_electricity', 0) or 0)
flat = float(order.get('flat_electricity', 0) or 0)
valley = float(order.get('valley_electricity', 0) or 0)
# 计算四时段之和
period_sum = round(sharp + peak + flat + valley, 3)
# 计算差额
diff = round(charge_degree - period_sum, 3)
# 如果差额为0不需要找平
if abs(diff) < 0.001:
continue
# 按优先级找平:谷 → 平 → 峰 → 尖
if valley > 0:
order['valley_electricity'] = round(valley + diff, 3)
elif flat > 0:
order['flat_electricity'] = round(flat + diff, 3)
elif peak > 0:
order['peak_electricity'] = round(peak + diff, 3)
else:
order['sharp_electricity'] = round(sharp + diff, 3)
balanced_count += 1
return balanced_count
def get_beijing_time():
"""获取北京时间"""
beijing_tz = pytz.timezone('Asia/Shanghai')
@@ -592,6 +656,19 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
log_info(f"[日报生成] 已计算 {len(time_period_electricity)} 条订单的分时段电量", 'report')
# 找平分时段电量
# 当配置开启了找平电量功能时,以充电电量(charge_degree)为准,
# 将尖+峰+平+谷之和与充电电量的差额,按 谷→平→峰→尖 的优先级加到对应时段上
balance_electricity_enabled = config.get('balance_electricity')
# 兼容历史配置NULL 或未设置时默认开启
if balance_electricity_enabled is None:
balance_electricity_enabled = 1
if balance_electricity_enabled == 1:
balanced_count = balance_time_period_electricity(orders)
if balanced_count > 0:
log_info(f"[日报生成] 找平电量:共处理 {balanced_count} 条订单的分时段电量差额", 'report')
# 计算虚拟字段(自定义服务费、自定义实收总金额)并添加到订单数据中
# 这样求和数据才能正确计算这些字段的总和
has_custom_service_fee = config.get('custom_service_fee_price') is not None and str(config.get('custom_service_fee_price', '')).strip() != ''

View File

@@ -1106,6 +1106,118 @@ color: #666;
flex-shrink: 0;
}
/* ============== Toast 消息提示 ============== */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
max-width: 400px;
}
.toast {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 16px;
border-radius: 8px;
background: #fff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
border-left: 4px solid #909399;
font-size: 14px;
line-height: 1.5;
color: #333;
pointer-events: auto;
animation: toast-slide-in 0.3s ease;
word-break: break-word;
}
.toast.toast-success {
border-left-color: #67c23a;
background: #f0f9eb;
}
.toast.toast-error {
border-left-color: #f56c6c;
background: #fef0f0;
}
.toast.toast-warning {
border-left-color: #e6a23c;
background: #fdf6ec;
}
.toast.toast-info {
border-left-color: #409eff;
background: #ecf5ff;
}
.toast-icon {
flex-shrink: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: bold;
}
.toast.toast-success .toast-icon { color: #67c23a; }
.toast.toast-error .toast-icon { color: #f56c6c; }
.toast.toast-warning .toast-icon { color: #e6a23c; }
.toast.toast-info .toast-icon { color: #409eff; }
.toast-content {
flex: 1;
min-width: 0;
}
.toast-close {
flex-shrink: 0;
background: none;
border: none;
color: #c0c4cc;
cursor: pointer;
font-size: 16px;
padding: 0;
line-height: 1;
}
.toast-close:hover {
color: #909399;
}
.toast.toast-fade-out {
animation: toast-slide-out 0.3s ease forwards;
}
@keyframes toast-slide-in {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes toast-slide-out {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
.generate-item.running .generate-item-status {
color: #17a2b8;
}

View File

@@ -4,6 +4,113 @@ window.onerror = function(msg, url, line, col, error) {
return true; // 阻止错误传播
};
// ============== Toast 消息提示系统 ==============
// 非阻塞消息提示,替代 alert() 弹窗
// 用法: showToast('消息内容', 'success'); // type: success/error/warning/info
// showToast('消息内容'); // 默认 auto 自动判断类型
const TOAST_ICONS = {
success: '✓',
error: '✕',
warning: '!',
info: 'i'
};
let toastContainer = null;
function getToastContainer() {
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.className = 'toast-container';
document.body.appendChild(toastContainer);
}
return toastContainer;
}
/**
* 显示消息提示(非阻塞)
* @param {string} message - 消息内容
* @param {string} type - 类型: success/error/warning/info/auto
* @param {number} duration - 显示时长(毫秒),默认 3000mserror 默认 5000ms
*/
function showToast(message, type = 'auto', duration = null) {
// 自动判断类型
if (type === 'auto') {
const msg = String(message);
if (/成功|完成|已保存|已删除|已更新|已启用|已禁用|已复制|已导入|已导出|已采集/.test(msg)) {
type = 'success';
} else if (/失败|错误|异常|无法|不能|出错/.test(msg)) {
type = 'error';
} else if (/请|警告|注意|必须|需要/.test(msg)) {
type = 'warning';
} else {
type = 'info';
}
}
// 默认时长error 5000ms其他 3000ms
if (duration === null) {
duration = type === 'error' ? 5000 : 3000;
}
const container = getToastContainer();
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
const icon = TOAST_ICONS[type] || '';
toast.innerHTML = `
<span class="toast-icon">${icon}</span>
<span class="toast-content"></span>
<button class="toast-close">&times;</button>
`;
// 安全设置消息内容(防 XSS
toast.querySelector('.toast-content').textContent = String(message);
// 关闭按钮
toast.querySelector('.toast-close').addEventListener('click', function() {
removeToast(toast);
});
container.appendChild(toast);
// 自动消失
if (duration > 0) {
setTimeout(function() {
removeToast(toast);
}, duration);
}
return toast;
}
function removeToast(toast) {
if (!toast || !toast.parentNode) return;
toast.classList.add('toast-fade-out');
setTimeout(function() {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 300);
}
// 便捷方法
function showSuccess(message, duration) {
return showToast(message, 'success', duration);
}
function showError(message, duration) {
return showToast(message, 'error', duration);
}
function showWarning(message, duration) {
return showToast(message, 'warning', duration);
}
function showInfo(message, duration) {
return showToast(message, 'info', duration);
}
// 全局变量
let editingConfigId = null;
let allFields = [];
@@ -93,7 +200,7 @@ async function handleLogin(event) {
const password = document.getElementById('login-password').value.trim();
if (!username || !password) {
alert('请输入用户名和密码');
showToast('请输入用户名和密码');
return;
}
@@ -119,10 +226,10 @@ async function handleLogin(event) {
// 登录成功后加载数据
loadConfigs();
} else {
alert(result.message || '登录失败');
showToast(result.message || '登录失败');
}
} catch (error) {
alert('登录失败: ' + error.message);
showToast('登录失败: ' + error.message);
} finally {
btn.disabled = false;
btn.textContent = '登 录';
@@ -161,17 +268,17 @@ async function saveChangePassword() {
const confirmPassword = document.getElementById('confirm-password').value.trim();
if (!oldPassword || !newPassword || !confirmPassword) {
alert('请填写完整信息');
showToast('请填写完整信息');
return;
}
if (newPassword.length < 6) {
alert('新密码长度不能少于6位');
showToast('新密码长度不能少于6位');
return;
}
if (newPassword !== confirmPassword) {
alert('两次输入的新密码不一致');
showToast('两次输入的新密码不一致');
return;
}
@@ -188,15 +295,15 @@ async function saveChangePassword() {
const result = await response.json();
if (result.success) {
alert('密码修改成功,请重新登录');
showToast('密码修改成功,请重新登录');
closeChangePasswordModal();
clearAuth();
showLoginPage();
} else {
alert(result.message || '修改失败');
showToast(result.message || '修改失败');
}
} catch (error) {
alert('修改失败: ' + error.message);
showToast('修改失败: ' + error.message);
}
}
@@ -241,16 +348,6 @@ function formatDateTime(dateStr) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 显示错误提示
function showError(message) {
alert('错误:' + message);
}
// 显示成功提示
function showSuccess(message) {
alert('成功:' + message);
}
// 页面加载时自动加载配置列表
document.addEventListener('DOMContentLoaded', function() {
loadConfigs();

View File

@@ -11,10 +11,10 @@ async function loadConfigs() {
currentConfigList = result.data;
renderConfigList(currentConfigList);
} else {
alert('加载配置失败:' + result.message);
showToast('加载配置失败:' + result.message);
}
} catch (error) {
alert('加载配置失败:' + error.message);
showToast('加载配置失败:' + error.message);
}
}
@@ -89,6 +89,10 @@ function showCreateModal() {
const showMonthlyTotalCheckbox = document.getElementById('show-monthly-total');
if (showMonthlyTotalCheckbox) showMonthlyTotalCheckbox.checked = false;
// 重置找平电量开关(默认开启)
const balanceElectricityCheckbox = document.getElementById('balance-electricity');
if (balanceElectricityCheckbox) balanceElectricityCheckbox.checked = true;
// 重置自定义服务费单价
const customServiceFeePriceInput = document.getElementById('custom-service-fee-price');
if (customServiceFeePriceInput) customServiceFeePriceInput.value = '';
@@ -593,6 +597,9 @@ async function saveConfig() {
// 获取月度总计开关
const showMonthlyTotal = document.getElementById('show-monthly-total')?.checked || false;
// 获取找平电量开关(默认开启)
const balanceElectricity = document.getElementById('balance-electricity')?.checked || false;
// 获取自定义服务费单价
const customServiceFeePriceInput = document.getElementById('custom-service-fee-price');
const customServiceFeePrice = customServiceFeePriceInput?.value !== '' ? parseFloat(customServiceFeePriceInput.value) : null;
@@ -612,12 +619,12 @@ async function saveConfig() {
const totalAmountName = totalAmountNameInput?.value.trim() || null;
if (!configName || !splitType || !splitValue) {
alert('请填写所有必填字段');
showToast('请填写所有必填字段');
return;
}
if (selectedFields.length === 0) {
alert('请至少选择一个字段');
showToast('请至少选择一个字段');
return;
}
@@ -628,11 +635,11 @@ async function saveConfig() {
// 验证合并特来电配置
if (mergeTelecom && splitType !== 'company_id') {
alert('合并特来电数据仅支持按企业拆分');
showToast('合并特来电数据仅支持按企业拆分');
return;
}
if (mergeTelecom && !telecomVehicleNo) {
alert('请输入特来电车量自编号');
showToast('请输入特来电车量自编号');
return;
}
@@ -651,6 +658,7 @@ async function saveConfig() {
telecom_vehicle_no: telecomVehicleNo,
field_custom_names: fieldCustomNamesData,
show_monthly_total: showMonthlyTotal,
balance_electricity: balanceElectricity,
custom_service_fee_price: customServiceFeePrice,
custom_service_fee_name: customServiceFeeName,
show_custom_service_fee: showCustomServiceFee,
@@ -677,14 +685,14 @@ async function saveConfig() {
const result = await response.json();
if (result.success) {
alert(editingConfigId ? '配置更新成功' : '配置创建成功');
showToast(editingConfigId ? '配置更新成功' : '配置创建成功');
closeModal();
loadConfigs();
} else {
alert('保存失败: ' + result.message);
showToast('保存失败: ' + result.message);
}
} catch (error) {
alert('保存失败: ' + error.message);
showToast('保存失败: ' + error.message);
}
}
@@ -736,6 +744,16 @@ async function editConfig(configId) {
showMonthlyTotalCheckbox.checked = config.show_monthly_total === 1 || config.show_monthly_total === true;
}
// 加载找平电量开关兼容历史配置NULL/未设置时默认开启)
const balanceElectricityCheckbox = document.getElementById('balance-electricity');
if (balanceElectricityCheckbox) {
// balance_electricity 为 null/undefined 时默认开启,为 0 时关闭
balanceElectricityCheckbox.checked = config.balance_electricity === null ||
config.balance_electricity === undefined ||
config.balance_electricity === 1 ||
config.balance_electricity === true;
}
// 加载自定义服务费单价
const customServiceFeePriceInput = document.getElementById('custom-service-fee-price');
if (customServiceFeePriceInput) {
@@ -770,7 +788,7 @@ async function editConfig(configId) {
}
}
} catch (error) {
alert('加载配置失败: ' + error.message);
showToast('加载配置失败: ' + error.message);
}
}
@@ -913,14 +931,14 @@ async function copyConfig(configId) {
const result = await response.json();
if (result.success) {
alert('配置复制成功!');
showToast('配置复制成功!');
loadConfigs();
} else {
alert('复制失败: ' + (result.message || result.error || '未知错误'));
showToast('复制失败: ' + (result.message || result.error || '未知错误'));
}
} catch (error) {
console.error('复制配置失败:', error);
alert('复制失败,请稍后重试');
showToast('复制失败,请稍后重试');
}
}
@@ -937,13 +955,13 @@ async function deleteConfig(configId) {
const result = await response.json();
if (result.success) {
alert('配置删除成功');
showToast('配置删除成功');
loadConfigs();
} else {
alert('删除失败: ' + result.message);
showToast('删除失败: ' + result.message);
}
} catch (error) {
alert('删除失败: ' + error.message);
showToast('删除失败: ' + error.message);
}
}
@@ -958,10 +976,10 @@ async function toggleConfig(configId) {
if (result.success) {
loadConfigs();
} else {
alert('操作失败: ' + result.message);
showToast('操作失败: ' + result.message);
}
} catch (error) {
alert('操作失败: ' + error.message);
showToast('操作失败: ' + error.message);
}
}
@@ -1000,7 +1018,7 @@ async function moveConfig(configId, direction) {
currentConfigList[rollbackTarget] = temp;
renderConfigList(currentConfigList);
}
alert('移动失败: ' + result.message);
showToast('移动失败: ' + result.message);
}
} catch (error) {
// 网络错误也回滚
@@ -1012,7 +1030,7 @@ async function moveConfig(configId, direction) {
currentConfigList[rollbackTarget] = temp;
renderConfigList(currentConfigList);
}
alert('移动失败: ' + error.message);
showToast('移动失败: ' + error.message);
}
}
@@ -1045,7 +1063,7 @@ function exportConfigs() {
try {
window.open('/api/config/export', '_blank');
} catch (error) {
alert('导出失败: ' + error.message);
showToast('导出失败: ' + error.message);
}
}
@@ -1054,7 +1072,7 @@ function downloadTemplate() {
try {
window.open('/api/config/template', '_blank');
} catch (error) {
alert('模板下载失败: ' + error.message);
showToast('模板下载失败: ' + error.message);
}
}
@@ -1065,7 +1083,7 @@ async function importConfigs(event) {
if (!file) return;
if (!file.name.endsWith('.xlsx') && !file.name.endsWith('.xls')) {
alert('只支持 Excel 格式(.xlsx/.xls的配置文件');
showToast('只支持 Excel 格式(.xlsx/.xls的配置文件');
fileInput.value = '';
return;
}
@@ -1090,13 +1108,13 @@ async function importConfigs(event) {
if (result.data && result.data.fail_messages && result.data.fail_messages.length > 0) {
message += '\n\n失败详情\n' + result.data.fail_messages.join('\n');
}
alert(message);
showToast(message);
loadConfigs();
} else {
alert('导入失败: ' + (result.message || '未知错误'));
showToast('导入失败: ' + (result.message || '未知错误'));
}
} catch (error) {
alert('导入失败: ' + error.message);
showToast('导入失败: ' + error.message);
}
fileInput.value = '';

View File

@@ -37,7 +37,7 @@ async function batchDownloadHistory() {
const successCount = checkboxes.length - failedCount;
if (successCount === 0) {
alert('选中的记录都是生成失败的,无法下载');
showToast('选中的记录都是生成失败的,无法下载');
return;
}
@@ -59,7 +59,7 @@ async function batchDownloadHistory() {
});
await handleDownloadResponse(response);
} catch (error) {
alert('下载失败: ' + error.message);
showToast('下载失败: ' + error.message);
}
} else {
// 模式2按筛选条件下载全部匹配的成功记录
@@ -94,7 +94,7 @@ async function batchDownloadHistory() {
}
if (matchCount === 0) {
alert(`没有符合条件的成功记录可导出。\n筛选条件: ${filterDesc}`);
showToast(`没有符合条件的成功记录可导出。\n筛选条件: ${filterDesc}`);
return;
}
@@ -115,7 +115,7 @@ async function batchDownloadHistory() {
});
await handleDownloadResponse(response);
} catch (error) {
alert('下载失败: ' + error.message);
showToast('下载失败: ' + error.message);
}
}
}
@@ -125,9 +125,9 @@ async function handleDownloadResponse(response) {
if (!response.ok) {
try {
const result = await response.json();
alert('下载失败: ' + (result.message || '未知错误'));
showToast('下载失败: ' + (result.message || '未知错误'));
} catch (e) {
alert('下载失败: 服务器返回错误');
showToast('下载失败: 服务器返回错误');
}
return;
}
@@ -161,7 +161,7 @@ async function handleDownloadResponse(response) {
msg += `\n跳过失败记录: ${skipped}`;
}
if (missing > 0 || skipped > 0) {
alert(msg);
showToast(msg);
}
}
}
@@ -397,10 +397,10 @@ async function loadHistoryWithDate(page) {
renderHistory(result.data.list);
renderPagination(result.data.page, result.data.total, result.data.page_size);
} else {
alert('加载失败: ' + (result.error || result.message || '未知错误'));
showToast('加载失败: ' + (result.error || result.message || '未知错误'));
}
} catch (error) {
alert('加载失败: ' + error.message);
showToast('加载失败: ' + error.message);
}
}
@@ -427,13 +427,13 @@ async function deleteHistory(id) {
});
const result = await response.json();
if (result.success) {
alert('删除成功');
showToast('删除成功');
loadHistoryWithDate();
} else {
alert('删除失败: ' + result.message);
showToast('删除失败: ' + result.message);
}
} catch (error) {
alert('删除失败: ' + error.message);
showToast('删除失败: ' + error.message);
}
}
@@ -441,7 +441,7 @@ async function deleteHistory(id) {
async function batchDeleteHistory() {
const checkboxes = document.querySelectorAll('.history-checkbox:checked');
if (checkboxes.length === 0) {
alert('请至少选择一条记录');
showToast('请至少选择一条记录');
return;
}
@@ -459,13 +459,13 @@ async function batchDeleteHistory() {
});
const result = await response.json();
if (result.success) {
alert(result.message);
showToast(result.message);
loadHistoryWithDate();
} else {
alert('删除失败: ' + result.message);
showToast('删除失败: ' + result.message);
}
} catch (error) {
alert('删除失败: ' + error.message);
showToast('删除失败: ' + error.message);
}
}

View File

@@ -286,12 +286,12 @@ async function generateReport() {
const endTime = document.getElementById('end-time').value;
if (checkboxes.length === 0) {
alert('请至少选择一个配置');
showToast('请至少选择一个配置');
return;
}
if (!startTime || !endTime) {
alert('请选择时间范围');
showToast('请选择时间范围');
return;
}

View File

@@ -253,7 +253,7 @@ async function collectSumDataFromHistory() {
const endDate = document.getElementById('sum-data-end-date').value;
if (!startDate || !endDate) {
alert('请先选择日期范围');
showToast('请先选择日期范围');
return;
}
@@ -273,14 +273,14 @@ async function collectSumDataFromHistory() {
});
const result = await response.json();
if (result.success) {
alert(result.message);
showToast(result.message);
loadSumData(1);
} else {
alert('采集失败: ' + (result.message || '未知错误'));
showToast('采集失败: ' + (result.message || '未知错误'));
loadSumData(1);
}
} catch (error) {
alert('采集失败: ' + error.message);
showToast('采集失败: ' + error.message);
loadSumData(1);
}
}
@@ -319,14 +319,14 @@ async function showEditSumDataGroup(ids) {
const result = await response.json();
if (!result.success) {
alert('加载详情失败');
showToast('加载详情失败');
return;
}
const groupItems = result.data || [];
if (groupItems.length === 0) {
alert('未找到该记录');
showToast('未找到该记录');
return;
}
@@ -349,7 +349,7 @@ async function showEditSumDataGroup(ids) {
document.getElementById('sum-data-remark-input').value = chargeItem.remark || '';
document.getElementById('sum-data-modal').classList.add('active');
} catch (error) {
alert('加载详情失败: ' + error.message);
showToast('加载详情失败: ' + error.message);
}
}
@@ -415,7 +415,7 @@ async function saveSumData() {
// 编辑时修改原因为必填
if (editingGroupIds && editingGroupIds.length > 0 && !remark.trim()) {
alert('请填写修改原因');
showToast('请填写修改原因');
return;
}
@@ -524,11 +524,11 @@ async function saveSumData() {
}
if (successCount > 0) {
alert(`更新成功(${successCount} 个字段)`);
showToast(`更新成功(${successCount} 个字段)`);
closeSumDataModal();
loadSumData(sumDataCurrentPage);
} else {
alert('更新失败');
showToast('更新失败');
}
return;
}
@@ -543,7 +543,7 @@ async function saveSumData() {
const valleyTime = document.getElementById('sum-data-valley-time-input').value;
if (!chargeDegree && !peakDegree && !peakTime && !normalTime && !valleyTime) {
alert('请至少输入一个求和字段值');
showToast('请至少输入一个求和字段值');
return;
}
@@ -595,7 +595,7 @@ async function saveSumData() {
};
if (!data.report_date) {
alert('请选择报表日期');
showToast('请选择报表日期');
return;
}
@@ -607,14 +607,14 @@ async function saveSumData() {
const result = await response.json();
if (result.success) {
alert('添加成功');
showToast('添加成功');
closeSumDataModal();
loadSumData(sumDataCurrentPage);
} else {
alert('保存失败: ' + (result.message || '未知错误'));
showToast('保存失败: ' + (result.message || '未知错误'));
}
} catch (error) {
alert('保存失败: ' + error.message);
showToast('保存失败: ' + error.message);
}
}
@@ -654,13 +654,13 @@ async function deleteSumDataGroup(ids) {
}
if (success) {
alert('删除成功');
showToast('删除成功');
loadSumData(sumDataCurrentPage);
} else {
alert('部分删除失败');
showToast('部分删除失败');
}
} catch (error) {
alert('删除失败: ' + error.message);
showToast('删除失败: ' + error.message);
}
}
@@ -673,7 +673,7 @@ function deleteSumData(id) {
async function batchDeleteSumData() {
const checkboxes = document.querySelectorAll('.sum-data-checkbox:checked');
if (checkboxes.length === 0) {
alert('请选择要删除的记录');
showToast('请选择要删除的记录');
return;
}
@@ -697,13 +697,13 @@ async function batchDeleteSumData() {
});
const result = await response.json();
if (result.success) {
alert('删除成功');
showToast('删除成功');
loadSumData(sumDataCurrentPage);
} else {
alert('删除失败: ' + (result.message || '未知错误'));
showToast('删除失败: ' + (result.message || '未知错误'));
}
} catch (error) {
alert('删除失败: ' + error.message);
showToast('删除失败: ' + error.message);
}
}
@@ -725,7 +725,7 @@ async function exportSumData() {
if (result.success) {
if (result.total === 0) {
alert('没有可导出的数据');
showToast('没有可导出的数据');
return;
}
const a = document.createElement('a');
@@ -736,10 +736,10 @@ async function exportSumData() {
a.click();
document.body.removeChild(a);
} else {
alert('导出失败: ' + (result.message || '未知错误'));
showToast('导出失败: ' + (result.message || '未知错误'));
}
} catch (error) {
alert('导出失败: ' + error.message);
showToast('导出失败: ' + error.message);
}
}
@@ -837,14 +837,14 @@ async function saveSumDataConfig() {
const result = await response.json();
if (result.success) {
alert('配置保存成功');
showToast('配置保存成功');
sumDataConfigFields = selectedFields;
closeSumDataConfigModal();
loadSumData(sumDataCurrentPage);
} else {
alert('保存失败: ' + (result.message || '未知错误'));
showToast('保存失败: ' + (result.message || '未知错误'));
}
} catch (error) {
alert('保存失败: ' + error.message);
showToast('保存失败: ' + error.message);
}
}

View File

@@ -337,6 +337,13 @@
报表底部显示当月充电量累计总计(数据来源于求和采集表,对账后数据)
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox" id="balance-electricity" checked>
找平电量(当尖+峰+平+谷之和不等于充电电量时,以充电电量为准,将差额按 谷→平→峰→尖 的顺序加到对应时段上)
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn" onclick="closeModal()">取消</button>