feat: 改造线路图上传后台并接入购票页总览图

This commit is contained in:
2026-07-17 03:17:03 +08:00
parent 289ed65e79
commit ec31c12130
3 changed files with 56 additions and 72 deletions
+23 -5
View File
@@ -41,7 +41,8 @@
let lineColors = [];
let linesByStation = new Map();
let lineIdxsByCanonical = {};
let activeLineIdx = 0;
let activeLineIdx = -1;
let routeMapUrl = '';
const piePath = (cx, cy, r, a0, a1) => {
const x0 = cx + r * Math.cos(a0);
@@ -77,6 +78,12 @@
return (yiq >= 140) ? '#0f172a' : '#ffffff';
};
const buildAssetUrl = (name, updatedAt) => {
if (!name) return '';
const version = updatedAt ? String(updatedAt) : String(Date.now());
return `/assets/${encodeURIComponent(name)}?v=${encodeURIComponent(version)}`;
};
function renderStationNode(x, y, code, i, forceColor = null) {
const canonical = stationCanonicalByCode[code] || code;
const codes = stationCodesByCanonical[canonical] || [canonical];
@@ -165,7 +172,7 @@
}
window.selectTvmLine = (idx) => {
activeLineIdx = idx;
activeLineIdx = (activeLineIdx === idx) ? -1 : idx;
renderTvmLines();
renderTvmSingleLine();
};
@@ -175,7 +182,11 @@
if (!container) return;
const li = lineStops[activeLineIdx];
if (!li) {
container.innerHTML = '';
if (routeMapUrl) {
container.innerHTML = `<div class="tvm-overview tvm-animate"><img src="${routeMapUrl}" alt="线路图总览"></div>`;
} else {
container.innerHTML = '<div class="tvm-overview-empty">暂未选择线路,且暂无已上传线路图。</div>';
}
return;
}
@@ -313,9 +324,10 @@
async function renderSystemMap() {
mapContainer.innerHTML = '<div class="loading">加载线路数据...</div>';
try {
const [linesData, stationsData] = await Promise.all([
const [linesData, stationsData, assetsData] = await Promise.all([
fetch('/api/public/lines').then(r=>r.json()),
fetch('/api/public/stations').then(r=>r.json())
fetch('/api/public/stations').then(r=>r.json()),
fetch('/api/assets/manifest', { cache: 'no-store' }).then(r => r.json()).catch(() => null)
]);
window.cachedStationsData = stationsData;
@@ -374,6 +386,8 @@
const uniq = Array.from(new Set(arr));
if (uniq.length >= 2) stationTransfer.add(c);
}
routeMapUrl = (assetsData && assetsData.routeMap) ? buildAssetUrl(assetsData.routeMap, assetsData.updatedAt) : '';
const fadeStyle = `<style>
@keyframes tvmFadeIn {
@@ -385,6 +399,9 @@
.tvm-lines { display: flex; flex-wrap: wrap; gap: 6px; margin-top: -8px; margin-bottom: 2px; }
.tvm-line-btn { flex: 0 0 auto; white-space: nowrap; }
.tvm-single-line, .tvm-route { overflow-x: auto; padding: 10px 0; min-height: 120px; display: flex; align-items: center; justify-content: flex-start; }
.tvm-overview { width: 100%; min-height: 120px; display: flex; align-items: center; justify-content: center; background: #f8fbf8; border: 1px solid #d7e0d3; }
.tvm-overview img { display: block; max-width: 100%; height: auto; background: #ffffff; }
.tvm-overview-empty { width: 100%; min-height: 120px; display: flex; align-items: center; justify-content: center; color: #64748b; background: #f8fbf8; border: 1px solid #d7e0d3; padding: 16px; text-align: center; }
.tvm-route-title { font-weight: bold; margin-bottom: -15px; color: #475569; display: none; font-size: 14px; }
.tvm-route { margin-top: -4px; }
.tvm-scroll-container { scroll-behavior: smooth; -webkit-overflow-scrolling: touch; }
@@ -395,6 +412,7 @@
.tvm-layout { gap: 12px; }
.tvm-lines { flex-wrap: nowrap; overflow-x: auto; gap: 5px; margin-top: -14px; margin-bottom: -2px; padding-bottom: 2px; }
.tvm-single-line, .tvm-route { min-height: 112px; padding: 6px 0 8px; }
.tvm-overview, .tvm-overview-empty { min-height: 112px; }
.tvm-route-title { margin-bottom: -12px; font-size: 13px; }
.tvm-route { margin-top: -6px; }
}