refactor: 重构文件存储路径并清理冗余代码

- 将项目文件存储从 public/uploads 迁移至项目根目录专用目录
- 更新所有前端页面的路径提示文本与API请求路径
- 移除冗余的列车详情展开收起功能及相关代码
- 重构服务器端文件路径处理逻辑,增强路径访问安全性
- 清理旧注释和未使用的变量与函数
This commit is contained in:
2026-07-31 19:41:26 +08:00
parent d6e5ce5a93
commit f530d7e045
7 changed files with 79 additions and 269 deletions
+4 -100
View File
@@ -857,7 +857,6 @@
// --- State ---
let allFiles = [];
let currentPath = 'root';
let expandedTrains = new Set();
let stationCodeMap = {};
let broadcastingStations = new Set();
@@ -951,101 +950,26 @@
if (!state.activeTrains || state.activeTrains.length === 0) list.innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-muted);font-size:0.8rem">No active trains</div>';
state.activeTrains.forEach(tr => {
const isExpanded = expandedTrains.has(tr.trainId);
const div = document.createElement('div');
div.className = 'status-item';
let detailsHtml = '';
if (tr.tripLogs && tr.tripLogs.length > 0) {
detailsHtml = tr.tripLogs.map(log => {
const time = new Date(log.time).toLocaleTimeString();
const stCode = stationCodeMap[log.station] || log.station;
return `
<div class="trip-log">
<span>${time}</span>
<span>${stCode}</span>
<span>${log.action}</span>
</div>
`;
}).join('');
} else {
detailsHtml = '<div style="font-style:italic">No trip history</div>';
}
const currentStCode = stationCodeMap[tr.currentStation] || tr.currentStation || '-';
div.innerHTML = `
<div class="status-main" onclick="toggleTrain('${tr.trainId}')">
div.innerHTML = `
<div class="status-main">
<div class="item-info">
<div class="item-title">${tr.trainId}</div>
<div class="item-sub">${currentStCode}</div>
<div class="item-sub">${currentStCode} <span style="margin-left:8px;color:var(--text-muted)">${tr.status || '-'}</span></div>
</div>
<div style="display:flex;align-items:center;gap:10px">
<div class="status-dot ${tr.status === 'At Station' ? 'dot-online' : 'dot-warn'}"></div>
<div style="color:var(--text-muted)">${isExpanded ? ICONS.chevronUp : ICONS.chevronDown}</div>
</div>
</div>
<div class="status-details ${isExpanded ? 'open' : ''}" id="details-${tr.trainId}">
<div style="margin-bottom:8px;font-weight:600">Trip Logs</div>
${detailsHtml}
<div style="margin-top:10px;text-align:right">
<button class="btn-icon" onclick="event.stopPropagation(); deleteTrain('${tr.trainId}')" style="display:inline-flex">${ICONS.trash}</button>
<button class="btn-icon" onclick="deleteTrain('${tr.trainId}')" style="display:inline-flex">${ICONS.trash}</button>
</div>
</div>
`;
list.appendChild(div);
});
// Network Statistics
const statsDiv = document.createElement('div');
statsDiv.className = 'status-item';
const isStatsExpanded = expandedTrains.has('__STATS__');
let statsHtml = '<div style="padding:10px;text-align:center;color:var(--text-muted)">No data</div>';
if (state.travelTimes) {
const keys = Object.keys(state.travelTimes).sort();
if (keys.length > 0) {
statsHtml = keys.map(key => {
const times = state.travelTimes[key];
const [from, to] = key.split('|');
const fromCode = stationCodeMap[from] || from;
const toCode = stationCodeMap[to] || to;
const avg = Math.ceil(times.reduce((a, b) => a + b, 0) / times.length);
return `
<div class="trip-log">
<span>${fromCode} &rarr; ${toCode}</span>
<span style="text-align:right; color:var(--success)">${avg}s</span>
<span style="text-align:right; font-size:0.75em; opacity:0.7">(${times.length})</span>
</div>
`;
}).join('');
}
}
statsDiv.innerHTML = `
<div class="status-main" onclick="toggleTrain('__STATS__')" style="background:var(--bg-body); border-top:1px solid var(--bg-border)">
<div class="item-info">
<div class="item-title" style="font-size:0.85rem; color:var(--text-muted)">NETWORK STATISTICS</div>
</div>
<div style="display:flex;align-items:center;gap:10px">
<button class="btn-icon" onclick="event.stopPropagation(); deleteStats()" style="display:inline-flex" title="Clear Statistics">${ICONS.trash}</button>
<div style="color:var(--text-muted)">${isStatsExpanded ? ICONS.chevronUp : ICONS.chevronDown}</div>
</div>
</div>
<div class="status-details ${isStatsExpanded ? 'open' : ''}" id="details-__STATS__">
<div style="display:grid; grid-template-columns: 1fr 40px 30px; gap:10px; margin-bottom:5px; font-weight:600; font-size:0.75rem; color:var(--text-muted)">
<span>ROUTE</span>
<span style="text-align:right">AVG</span>
<span style="text-align:right">CNT</span>
</div>
${statsHtml}
</div>
`;
list.appendChild(statsDiv);
}
}
/* Removed separate Travel Times renderer */
function showRadioStream() {
document.getElementById('file-manager-view').style.display = 'none';
@@ -1120,18 +1044,6 @@
});
}
function toggleTrain(id) {
if (expandedTrains.has(id)) expandedTrains.delete(id);
else expandedTrains.add(id);
// We need to re-render to update icons/state, but since state updates often,
// just toggling the DOM element is smoother until next update
const details = document.getElementById(`details-${id}`);
if (details) {
details.classList.toggle('open');
// Force re-render on next update will respect the Set
}
}
function addLog(logText) {
const container = document.getElementById('console-logs');
const div = document.createElement('div');
@@ -1334,14 +1246,6 @@
await fetch(`/api/trains/${encodeURIComponent(id)}`, { method: 'DELETE' });
}
async function deleteStats() {
if (!confirm('Are you sure you want to clear all network travel time statistics?')) return;
const res = await fetch('/api/stats', { method: 'DELETE' });
const data = await res.json();
if (data.success) showToast('Success', 'Network statistics cleared');
else showToast('Error', data.message);
}
function triggerUpload() {
if (currentPath === 'config') document.getElementById('upload-config').click();
else if (currentPath === 'general') document.getElementById('upload-general').click();