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

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

@@ -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 - 显示时长(毫秒),默认 3000ms,error 默认 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);
}
}