fix: 修复 JS 文件语法错误和函数缺失问题

Coze-Commit-Type: user
Coze-User-ID: 3722323274763196
Coze-Conversation-ID: 9894087
This commit is contained in:
user9994793890
2026-07-13 16:10:13 +08:00
parent a8dd660231
commit 6a98bb0cfa
3 changed files with 344 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -66,3 +66,347 @@ function showCreateModal() {
const mergeTelecomGroup = document.getElementById('merge-telecom-group'); const mergeTelecomGroup = document.getElementById('merge-telecom-group');
if (mergeTelecomCheckbox) mergeTelecomCheckbox.checked = false; if (mergeTelecomCheckbox) mergeTelecomCheckbox.checked = false;
if (telecomVehicleNoInput) telecomVehicleNoInput.value = ''; 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 `
<div class="checkbox-item ${isSelected ? 'checked' : ''} ${isRequired ? 'required-field' : ''}" onclick="${isRequired ? '' : 'toggleCheckboxItem(this)'}">
<input type="checkbox" value="${field.key}"
${isSelected ? 'checked' : ''}
${isRequired ? 'disabled checked' : ''}
onchange="onFieldCheckboxChange(this)">
<label>${field.display}${isRequired ? ' <span class="required-badge">必选</span>' : ''}</label>
</div>
`;
}).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;
// 如果传入了自定义名头,更新全局变量
if (customNames !== null) {
fieldCustomNames = {...customNames};
}
// 根据是否有已选字段,显示/隐藏字段顺序区域
if (orderGroup) {
orderGroup.style.display = selectedFields.length > 0 ? 'block' : 'none';
}
if (selectedFields.length === 0) {
orderList.innerHTML = '<small style="color:#999;text-align:center;padding:8px;">请先在上方勾选字段</small>';
} 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 `
<div class="selected-field-item" data-field="${fieldKey}">
<span class="field-index">#${index + 1}</span>
<span class="field-name" title="${displayName}">${displayName}</span>
<input type="text" class="field-custom-name" placeholder="自定义报表名头" value="${customName}"
onchange="updateFieldCustomName('${fieldKey}', this.value)"
style="flex: 1; min-width: 100px; padding: 4px 8px; font-size: 12px;">
<div class="field-actions">
<button type="button" class="btn-move" onclick="moveFieldUp(${index})" ${isFirst ? 'disabled' : ''} title="上移">↑</button>
<button type="button" class="btn-move" onclick="moveFieldDown(${index})" ${isLast ? 'disabled' : ''} title="下移">↓</button>
</div>
</div>
`;
}).join('');
}
}
// 更新字段自定义名头
function updateFieldCustomName(fieldKey, customName) {
if (customName && customName.trim()) {
fieldCustomNames[fieldKey] = customName.trim();
} else {
delete fieldCustomNames[fieldKey];
}
}
// 获取字段自定义名头
function getFieldCustomNames() {
return {...fieldCustomNames};
}
// 字段上移
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 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 = '<small style="color:#999;padding:8px;">请先在上方勾选数值类型的字段</small>';
} else {
sumFieldsList.innerHTML = availableSumFields.map(field => `
<div class="checkbox-item ${currentSumFields.includes(field.key) ? 'checked' : ''}" onclick="toggleCheckboxItem(this)">
<input type="checkbox" value="${field.key}"
${currentSumFields.includes(field.key) ? 'checked' : ''}>
<label>${field.display}</label>
</div>
`).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 += `<div class="tp-hour-label">${h}</div>`;
}
hoursRow.innerHTML = hoursHtml;
// 渲染时段行
let rowsHtml = '';
TIME_PERIODS.forEach(period => {
rowsHtml += `
<div class="tp-period-row" data-period="${period.key}">
<div class="tp-period-label ${period.key}">${period.label}时</div>
<div class="tp-period-cells" id="tp-cells-${period.key}">
${Array.from({length: 24}, (_, h) =>
`<div class="tp-cell" data-hour="${h}" data-period="${period.key}" onclick="toggleTimePeriodCell(${h}, '${period.key}')"></div>`
).join('')}
</div>
</div>
`;
});
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 `<span style="color:${p.color}"><b>${p.label}</b>: ${count} (${hoursStr})</span>`;
});
summaryEl.innerHTML = parts.join('');
}
// 设置时段配置(编辑时加载)

View File

@@ -222,4 +222,3 @@ async function loadHistoryWithDate(page, startDate, endDate) {
// 初始化 // 初始化
loadConfigs(); loadConfigs();
loadFields(); loadFields();
</script>