diff --git a/public/static/js/common.js b/public/static/js/common.js
index 9bc381f..f5b332a 100644
--- a/public/static/js/common.js
+++ b/public/static/js/common.js
@@ -4,6 +4,7 @@ window.onerror = function(msg, url, line, col, error) {
return true; // 阻止错误传播
};
+// 全局变量
let editingConfigId = null;
let allFields = [];
let currentPage = 1;
@@ -13,416 +14,44 @@ let currentSearch = '';
let entityPage = 1;
let entityPageSize = 20;
let selectedEntityIds = new Set();
-
-async function loadConfigs() {
- try {
- const response = await fetch('/api/config');
- const result = await response.json();
-
- if (result.success) {
- renderConfigList(result.data);
- } else {
- alert('加载配置失败: ' + result.message);
- }
- } catch (error) {
- alert('加载配置失败: ' + error.message);
- }
-}
-
-// 渲染配置列表
-function renderConfigList(configs) {
- const tbody = document.getElementById('config-list');
-
- if (configs.length === 0) {
- tbody.innerHTML = '
| 暂无配置,请点击"创建配置"按钮 |
';
- return;
- }
-
- tbody.innerHTML = configs.map(config => `
-
- | ${config.config_name} |
- ${config.split_type === 'company_id' ? '按企业ID' : '按用户ID'} |
- ${config.split_name || config.split_value} |
- ${config.selected_fields.length} |
-
-
- ${config.is_active ? '已启用' : '已禁用'}
-
- |
-
-
-
-
-
-
-
-
-
- |
-
- `).join('');
-}
-
-// 显示创建模态框
-function showCreateModal() {
- editingConfigId = null;
- document.getElementById('modal-title').textContent = '创建配置';
- document.getElementById('config-name').value = '';
- document.getElementById('split-type').value = '';
- document.getElementById('split-value').innerHTML = '';
-
- // 重置字段自定义名头
- fieldCustomNames = {};
-
- // 重置合并特来电配置
- const mergeTelecomCheckbox = document.getElementById('merge-telecom');
- const telecomVehicleNoInput = document.getElementById('telecom-vehicle-no');
- const mergeTelecomGroup = document.getElementById('merge-telecom-group');
- if (mergeTelecomCheckbox) mergeTelecomCheckbox.checked = false;
- if (telecomVehicleNoInput) telecomVehicleNoInput.value = '';
- if (mergeTelecomGroup) mergeTelecomGroup.style.display = 'none';
- toggleTelecomVehicleNo();
-
- loadFields();
- initTimePeriodEditor();
- clearAllTimePeriods();
- document.getElementById('config-modal').classList.add('active');
-}
-
-// 加载字段列表
-async function loadFields(selectedFields = [], sumFields = []) {
- try {
- const response = await fetch('/api/fields');
- const result = await response.json();
-
- if (result.success) {
- allFields = result.data;
- renderFields(selectedFields, sumFields);
- }
- } catch (error) {
- console.error('加载字段失败:', error);
- }
-}
-
-// 可求和的数值类型字段
-const SUMMABLE_FIELD_KEYS = [
- 'charge_degree', 'charge_ah', 'charge_begin_soc', 'charge_end_soc',
- 'receivable_electric_fee', 'receivable_service_fee', 'receivable_total_fee',
- 'charge_elecfee_amount', 'charge_elecfee_cost_amount', 'charge_service_amount',
- 'charging_amt', 'actual_pay_amount', 'pay_amount',
- 'settle_electric_fee', 'settle_service_fee', 'settle_fee',
- 'coupon_amount', 'coupon_total_amount', 'elecfee_coupon_amount', 'servicefee_coupon_amount',
- 'invoice_fee', 'activity_electric_fee', 'activity_service_fee', 'activity_total_fee',
- 'total_money',
- // 分时段电量
- 'sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity'
-];
-
-// 渲染字段列表
-function renderFields(selectedFields = [], sumFields = []) {
- const fieldsList = document.getElementById('fields-list');
- const sumFieldsList = document.getElementById('sum-fields-list');
-
- fieldsList.innerHTML = allFields.map(field => {
- const isSelected = selectedFields.includes(field.key);
- const isRequired = field.key === 'charge_degree'; // 充电量为必选字段
- return `
-
-
-
-
- `;
- }).join('');
-
- // 初始化求和字段列表(只显示已选字段中的可求和字段)
- refreshSumFieldsList(selectedFields, sumFields);
-}
-
-// 点击 checkbox-item 容器时切换选中状态
-function toggleCheckboxItem(item) {
- const cb = item.querySelector('input[type="checkbox"]');
- if (event.target === cb) return; // 如果点击的就是 checkbox 本身,不重复触发
- cb.checked = !cb.checked;
- item.classList.toggle('checked', cb.checked);
- onFieldCheckboxChange(cb);
-}
-
-// 字段 checkbox 变化时更新求和字段列表和已选字段顺序
-function onFieldCheckboxChange(cb) {
- // 防止取消选择必选字段(充电量)
- if (cb.value === 'charge_degree' && !cb.checked) {
- cb.checked = true;
- return;
- }
-
- const item = cb.closest('.checkbox-item');
- if (item) item.classList.toggle('checked', cb.checked);
-
- const fieldsList = document.getElementById('fields-list');
- const checkedFields = Array.from(fieldsList.querySelectorAll('input:checked')).map(c => c.value);
-
- // 保留当前求和字段的选择状态
- const sumFieldsList = document.getElementById('sum-fields-list');
- const currentSumFields = sumFieldsList ? Array.from(sumFieldsList.querySelectorAll('input:checked')).map(c => c.value) : [];
-
- refreshSumFieldsList(checkedFields, currentSumFields);
- refreshSelectedFieldsOrder(checkedFields);
-}
-
-// 字段自定义名头存储
let fieldCustomNames = {};
-// 刷新已选字段顺序列表
-function refreshSelectedFieldsOrder(selectedFields, customNames = null) {
- const orderGroup = document.getElementById('selected-fields-order-group');
- const orderList = document.getElementById('selected-fields-order');
- if (!orderList) return;
+// 标签切换
+function switchTab(tabName) {
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
- // 如果传入了自定义名头,更新全局变量
- if (customNames !== null) {
- fieldCustomNames = {...customNames};
- }
+ document.querySelector(`.tab[onclick="switchTab('${tabName}')"]`).classList.add('active');
+ document.getElementById(`${tabName}-tab`).classList.add('active');
- // 根据是否有已选字段,显示/隐藏字段顺序区域
- if (orderGroup) {
- orderGroup.style.display = selectedFields.length > 0 ? 'block' : 'none';
- }
-
- if (selectedFields.length === 0) {
- orderList.innerHTML = '请先在上方勾选字段';
- } else {
- orderList.innerHTML = selectedFields.map((fieldKey, index) => {
- const field = allFields.find(f => f.key === fieldKey);
- const displayName = field ? field.display : fieldKey;
- const customName = fieldCustomNames[fieldKey] || '';
- const isFirst = index === 0;
- const isLast = index === selectedFields.length - 1;
- return `
-
-
#${index + 1}
-
${displayName}
-
-
-
-
-
-
- `;
- }).join('');
+ if (tabName === 'config') {
+ loadConfigs();
+ } else if (tabName === 'generate') {
+ loadGenerateConfigs();
+ } else if (tabName === 'history') {
+ loadHistory();
}
}
-// 更新字段自定义名头
-function updateFieldCustomName(fieldKey, customName) {
- if (customName && customName.trim()) {
- fieldCustomNames[fieldKey] = customName.trim();
- } else {
- delete fieldCustomNames[fieldKey];
- }
+// 格式化日期时间
+function formatDateTime(dateStr) {
+ if (!dateStr) return '';
+ const date = new Date(dateStr);
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+ const hours = String(date.getHours()).padStart(2, '0');
+ const minutes = String(date.getMinutes()).padStart(2, '0');
+ const seconds = String(date.getSeconds()).padStart(2, '0');
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
-// 获取字段自定义名头
-function getFieldCustomNames() {
- return {...fieldCustomNames};
+// 显示错误提示
+function showError(message) {
+ alert('错误:' + message);
}
-// 字段上移
-function moveFieldUp(index) {
- if (index <= 0) return;
- const orderList = document.getElementById('selected-fields-order');
- const items = Array.from(orderList.querySelectorAll('.selected-field-item'));
- const item = items[index];
- const prevItem = items[index - 1];
- orderList.insertBefore(item, prevItem);
- updateFieldIndexes();
+// 显示成功提示
+function showSuccess(message) {
+ alert('成功:' + message);
}
-
-// 字段下移
-function moveFieldDown(index) {
- const orderList = document.getElementById('selected-fields-order');
- const items = Array.from(orderList.querySelectorAll('.selected-field-item'));
- if (index >= items.length - 1) return;
- const item = items[index];
- const nextItem = items[index + 1];
- orderList.insertBefore(nextItem, item);
- updateFieldIndexes();
-}
-
-// 更新字段序号
-function updateFieldIndexes() {
- const orderList = document.getElementById('selected-fields-order');
- const items = orderList.querySelectorAll('.selected-field-item');
- items.forEach((item, index) => {
- const indexSpan = item.querySelector('.field-index');
- if (indexSpan) indexSpan.textContent = `#${index + 1}`;
-
- // 更新按钮状态
- const upBtn = item.querySelector('.btn-move[title="上移"]');
- const downBtn = item.querySelector('.btn-move[title="下移"]');
- if (upBtn) upBtn.disabled = index === 0;
- if (downBtn) downBtn.disabled = index === items.length - 1;
- });
-}
-
-// 获取当前字段顺序
-function getSelectedFieldsOrder() {
- const orderList = document.getElementById('selected-fields-order');
- if (!orderList) return [];
- const items = orderList.querySelectorAll('.selected-field-item');
- return Array.from(items).map(item => item.dataset.field);
-}
-
-// 刷新求和字段列表(仅显示已选字段中的数值类型字段)
-function refreshSumFieldsList(selectedFields, currentSumFields) {
- const sumFieldsList = document.getElementById('sum-fields-list');
- const availableSumFields = allFields.filter(f =>
- selectedFields.includes(f.key) && SUMMABLE_FIELD_KEYS.includes(f.key)
- );
-
- if (availableSumFields.length === 0) {
- sumFieldsList.innerHTML = '请先在上方勾选数值类型的字段';
- } else {
- sumFieldsList.innerHTML = availableSumFields.map(field => `
-
-
-
-
- `).join('');
- }
-}
-
-// 切换特来电车量自编号输入框显示
-function toggleTelecomVehicleNo() {
- const mergeTelecom = document.getElementById('merge-telecom').checked;
- const vehicleNoGroup = document.getElementById('telecom-vehicle-no-group');
- if (vehicleNoGroup) {
- vehicleNoGroup.style.display = mergeTelecom ? 'block' : 'none';
- }
-}
-
-// 更新已选实体计数
-function updateSelectedCount() {
- const select = document.getElementById('split-value');
- const countEl = document.getElementById('selected-entity-count');
- const count = select.selectedOptions.length;
- if (count > 0) {
- countEl.textContent = '已选 ' + count + ' 项';
- countEl.style.display = 'inline';
- } else {
- countEl.style.display = 'none';
- }
-}
-
-// ========== 时段编辑器 ==========
-const TIME_PERIODS = [
- { key: 'sharp', label: '尖', color: '#e74c3c' },
- { key: 'peak', label: '峰', color: '#f39c12' },
- { key: 'flat', label: '平', color: '#27ae60' },
- { key: 'valley', label: '谷', color: '#3498db' }
-];
-
-// 时段配置: { sharp: [11,12], peak: [9,10], flat: [7,8], valley: [0,1,2,3] }
-let timePeriodConfig = { sharp: [], peak: [], flat: [], valley: [] };
-let currentTpTool = 'select'; // 'select' or period key
-
-// 初始化时段编辑器
-function initTimePeriodEditor() {
- const hoursRow = document.getElementById('tp-hours-row');
- const periodRows = document.getElementById('tp-period-rows');
-
- // 渲染小时标题行
- let hoursHtml = '';
- for (let h = 0; h < 24; h++) {
- hoursHtml += `${h}
`;
- }
- hoursRow.innerHTML = hoursHtml;
-
- // 渲染时段行
- let rowsHtml = '';
- TIME_PERIODS.forEach(period => {
- rowsHtml += `
-
-
${period.label}时
-
- ${Array.from({length: 24}, (_, h) =>
- `
`
- ).join('')}
-
-
- `;
- });
- periodRows.innerHTML = rowsHtml;
-
- updateTimePeriodSummary();
-}
-
-// 切换时段格子
-function toggleTimePeriodCell(hour, periodKey) {
- console.log('toggleTimePeriodCell called:', hour, periodKey);
- // 检查该小时是否已被其他时段占用
- const occupiedBy = Object.entries(timePeriodConfig).find(([k, v]) => k !== periodKey && v.includes(hour));
-
- if (occupiedBy) {
- // 已被其他时段占用,移除占用并分配给当前时段
- const [otherKey] = occupiedBy;
- timePeriodConfig[otherKey] = timePeriodConfig[otherKey].filter(h => h !== hour);
- updateCellDisplay(hour, otherKey);
- }
-
- // 切换当前时段
- if (timePeriodConfig[periodKey].includes(hour)) {
- timePeriodConfig[periodKey] = timePeriodConfig[periodKey].filter(h => h !== hour);
- } else {
- timePeriodConfig[periodKey].push(hour);
- timePeriodConfig[periodKey].sort((a, b) => a - b);
- }
-
- updateCellDisplay(hour, periodKey);
- updateTimePeriodSummary();
-}
-
-// 更新格子显示
-function updateCellDisplay(hour, periodKey) {
- // 清除所有时段中该小时的样式
- TIME_PERIODS.forEach(p => {
- const cell = document.querySelector(`.tp-cell[data-hour="${hour}"][data-period="${p.key}"]`);
- if (cell) {
- cell.className = 'tp-cell';
- cell.textContent = '';
- }
- });
-
- // 检查该小时属于哪个时段,并设置对应的样式
- for (const p of TIME_PERIODS) {
- if (timePeriodConfig[p.key].includes(hour)) {
- const cell = document.querySelector(`.tp-cell[data-hour="${hour}"][data-period="${p.key}"]`);
- if (cell) {
- cell.classList.add(p.key);
- cell.textContent = '✓';
- }
- break; // 每个小时只能属于一个时段
- }
- }
-}
-
-// 更新时段摘要
-function updateTimePeriodSummary() {
- const summaryEl = document.getElementById('tp-summary');
- const parts = TIME_PERIODS.map(p => {
- const hours = timePeriodConfig[p.key];
- const count = hours.length;
- const hoursStr = count > 0 ? hours.map(h => `${h}时`).join(',') : '未设置';
- return `${p.label}时: ${count}个 (${hoursStr})`;
- });
- summaryEl.innerHTML = parts.join('');
-}
-
-// 设置时段配置(编辑时加载)
diff --git a/public/static/js/config.js b/public/static/js/config.js
index c092833..14dfe8a 100644
--- a/public/static/js/config.js
+++ b/public/static/js/config.js
@@ -1,3 +1,53 @@
+// 加载配置列表
+async function loadConfigs() {
+ try {
+ const response = await fetch('/api/config');
+ const result = await response.json();
+
+ if (result.success) {
+ renderConfigList(result.data);
+ } else {
+ alert('加载配置失败:' + result.message);
+ }
+ } catch (error) {
+ alert('加载配置失败:' + error.message);
+ }
+}
+
+// 渲染配置列表
+function renderConfigList(configs) {
+ const tbody = document.getElementById('config-list');
+
+ if (configs.length === 0) {
+ tbody.innerHTML = '| 暂无配置,请点击"创建配置"按钮 |
';
+ return;
+ }
+
+ tbody.innerHTML = configs.map(config => `
+
+ | ${config.config_name} |
+ ${config.split_type === 'company_id' ? '按企业 ID' : config.split_type === 'user_id' ? '按用户 ID' : '按场站 ID'} |
+ ${config.split_name || config.split_value} |
+ ${config.selected_fields.length} |
+
+
+ ${config.is_active ? '已启用' : '已禁用'}
+
+ |
+
+
+
+
+
+
+
+ |
+
+ `).join('');
+}
+
function showCreateModal() {
editingConfigId = null;
document.getElementById('modal-title').textContent = '创建配置';