From 06a89c678ac5e618e0d0ae248a6fbe4ac64d1ec9 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Sun, 5 Jul 2026 15:34:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(web,server):=20=E4=BF=AE=E5=A4=8D=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF=E5=B9=B6=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=A4=9A=E9=A1=B9=E4=BD=93=E9=AA=8C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复多处JS文件中的模板字符串转义错误,将错误的\\`替换为标准反引号 为IC卡搜索页面新增中文事件类型匹配,支持充值、进站、出站场景 重构票务订单页面的线路绘制逻辑,添加站点交替波浪偏移并使用贝塞尔曲线优化线路显示 优化SVG票价表格渲染,新增不可达站点的红色叉号提示,重构代码提升可读性 --- old-ticket-order.js | Bin 0 -> 57072 bytes server/services/svg-generator.js | 64 +++++++++++++++++-------------- web/ic-card-search.js | 16 ++++---- web/index.js | 10 ++--- web/ticket-order.js | 24 +++++++++--- web/ticket-search.js | 8 ++-- 6 files changed, 70 insertions(+), 52 deletions(-) create mode 100644 old-ticket-order.js diff --git a/old-ticket-order.js b/old-ticket-order.js new file mode 100644 index 0000000000000000000000000000000000000000..4986de31e136cb4ba021c8b990da020a5008efcc GIT binary patch literal 57072 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 } else { - const val = Math.round(Number(distAll[code][codes[j]] || 0)); - const intensity = maxFare > 0 ? Math.min(1, val / maxFare) : 0; - - // Get route colors - const routeColors = colorsForRoute(code, codes[j]); - - // Draw background strips for multiple lines - if (routeColors.length > 0) { - const stripW = cellW / routeColors.length; - for (let k = 0; k < routeColors.length; k++) { - const c = routeColors[k] || '#93a2b7'; - const stripX = x0 + k * stripW; - // Use fill-opacity for robustness against any color format - svg += ``; - } + const distVal = distAll[code][codes[j]]; + if (distVal === Infinity || !Number.isFinite(distVal)) { + // Unreachable station + svg += `×`; } else { - svg += ``; - } - - // Add cell border - svg += ``; - - svg += `${escapeHTML(String(val))}`; - - // Show small color indicators at top left - const dots = routeColors.slice(0, 4); - for (let k = 0; k < dots.length; k++) { - const cx = x0 + 8 + k * 10; const cy = y0 + 10; - const dotColor = dots[k] || '#93a2b7'; - svg += ``; + const val = Math.round(Number(distVal || 0)); + const intensity = maxFare > 0 ? Math.min(1, val / maxFare) : 0; + + // Get route colors + const routeColors = colorsForRoute(code, codes[j]); + + // Draw background strips for multiple lines + if (routeColors.length > 0) { + const stripW = cellW / routeColors.length; + for (let k = 0; k < routeColors.length; k++) { + const c = routeColors[k] || '#93a2b7'; + const stripX = x0 + k * stripW; + // Use fill-opacity for robustness against any color format + svg += ``; + } + } else { + svg += ``; + } + + // Add cell border + svg += ``; + + svg += `${escapeHTML(String(val))}`; + + // Show small color indicators at top left + const dots = routeColors.slice(0, 4); + for (let k = 0; k < dots.length; k++) { + const cx = x0 + 8 + k * 10; const cy = y0 + 10; + const dotColor = dots[k] || '#93a2b7'; + svg += ``; + } } } } diff --git a/web/ic-card-search.js b/web/ic-card-search.js index dc32056..bcd29c8 100644 --- a/web/ic-card-search.js +++ b/web/ic-card-search.js @@ -96,25 +96,25 @@ if (type === 'create' || type === 'order_created' || type === 'activated') { const cardType = ev?.detail?.card_type || ev?.card_type || ev?.["类型"] || '储值卡'; - return \`[${cardType}];余额: \${balance};时间: \${timeStr}\`; + return `[${cardType}];余额: ${balance};时间: ${timeStr}`; } - if (type === 'topup' || type === 'refill' || action === 'topup') { + if (type === 'topup' || type === 'refill' || action === 'topup' || type === '充值') { const amount = ev?.amount ?? ev?.["充值数额"] ?? 0; - return \`机器ID: \${deviceId};充值数额: \${amount};余额: \${balance};时间: \${timeStr}\`; + return `机器ID: ${deviceId};充值数额: ${amount};余额: ${balance};时间: ${timeStr}`; } - if (action === 'entry' || type === 'entry') { + if (action === 'entry' || type === 'entry' || type === '进站') { const code = ev?.station_code || ev?.["站点编号"] || '---'; const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};余额: \${balance};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } - if (action === 'exit' || type === 'exit') { + if (action === 'exit' || type === 'exit' || type === '出站') { const code = ev?.station_code || ev?.["站点编号"] || '---'; const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};余额: \${balance};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } // fallback - return \`时间: \${timeStr};\${typeof ev?.detail === 'string' ? ev.detail : JSON.stringify(ev?.detail || {}, null, 2)}\`; + return `时间: ${timeStr};${typeof ev?.detail === 'string' ? ev.detail : JSON.stringify(ev?.detail || {}, null, 2)}`; }; const buildEventsHtml = (events) => { diff --git a/web/index.js b/web/index.js index 7826ec7..a4e1fb0 100644 --- a/web/index.js +++ b/web/index.js @@ -457,25 +457,25 @@ createApp({ if (type === 'create' || type === 'order_created' || type === 'activated') { const cardType = ev?.detail?.card_type || ev?.card_type || ev?.["类型"] || '储值卡'; - return \`[${cardType}];余额: \${balance};时间: \${timeStr}\`; + return `[${cardType}];余额: ${balance};时间: ${timeStr}`; } if (type === 'topup' || type === 'refill' || action === 'topup') { const amount = ev?.amount ?? ev?.["充值数额"] ?? 0; - return \`机器ID: \${deviceId};充值数额: \${amount};余额: \${balance};时间: \${timeStr}\`; + return `机器ID: ${deviceId};充值数额: ${amount};余额: ${balance};时间: ${timeStr}`; } if (action === 'entry' || type === 'entry') { const code = ev?.station_code || ev?.["站点编号"] || '---'; const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};余额: \${balance};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } if (action === 'exit' || type === 'exit') { const code = ev?.station_code || ev?.["站点编号"] || '---'; const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};余额: \${balance};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } // fallback - return \`时间: \${timeStr};\${typeof ev?.detail === 'string' ? ev.detail : JSON.stringify(ev?.detail || {}, null, 2)}\`; + return `时间: ${timeStr};${typeof ev?.detail === 'string' ? ev.detail : JSON.stringify(ev?.detail || {}, null, 2)}`; }; const getStationName = (code) => { diff --git a/web/ticket-order.js b/web/ticket-order.js index a2a7b72..e140f25 100644 --- a/web/ticket-order.js +++ b/web/ticket-order.js @@ -190,10 +190,13 @@ } stationYByCanonical = {}; + let stationIndex = 0; for (const c of Object.keys(primaryLineByStation)) { const li = primaryLineByStation[c]; if (li == null) continue; - stationYByCanonical[c] = baseY + li * lineGapY; + const waveOffset = (stationIndex % 2 === 0) ? -12 : 12; + stationYByCanonical[c] = baseY + li * lineGapY + waveOffset; + stationIndex++; } const allStations = Object.keys(primaryLineByStation); @@ -219,11 +222,20 @@ svgContent += `${li.name}`; if (li.stops.length >= 2) { - const firstX = stationXByCanonical[stationCanonicalByCode[li.stops[0]] || li.stops[0]]; - let d = `M ${firstX} ${yLine}`; - for (let i = 1; i < li.stops.length; i++) { - const x = stationXByCanonical[stationCanonicalByCode[li.stops[i]] || li.stops[i]]; - d += ` L ${x} ${yLine}`; + const pts = li.stops.map(code => { + const canonical = stationCanonicalByCode[code] || code; + return { + x: stationXByCanonical[canonical] || 0, + y: stationYByCanonical[code] ?? yLine + }; + }); + + let d = `M ${pts[0].x} ${pts[0].y}`; + for (let i = 1; i < pts.length; i++) { + const prev = pts[i-1]; + const curr = pts[i]; + const midX = (prev.x + curr.x) / 2; + d += ` C ${midX} ${prev.y}, ${midX} ${curr.y}, ${curr.x} ${curr.y}`; } svgContent += ``; } diff --git a/web/ticket-search.js b/web/ticket-search.js index 11f8eff..cf041e8 100644 --- a/web/ticket-search.js +++ b/web/ticket-search.js @@ -115,23 +115,23 @@ if (type === 'sale' || type === '售票') { const cost = ev.cost ?? ev.price ?? ev.amount ?? ev["票价"] ?? ev["售票额"] ?? '0'; - return \`机器ID: \${deviceId};票价: \${cost};时间: \${timeStr}\`; + return `机器ID: ${deviceId};票价: ${cost};时间: ${timeStr}`; } if (type === 'entry' || action === 'entry') { const code = ev.station_code || ev["站点编号"] || '---'; const name = ev.station_name || ev["站点中文"] || ev["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } if (type === 'exit' || action === 'exit') { const code = ev.station_code || ev["站点编号"] || '---'; const name = ev.station_name || ev["站点中文"] || ev["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } // fallback const code = ev.station_code || ev["站点编号"] || '---'; const name = ev.station_name || ev["站点中文"] || ev["发生站"] || '---'; - return \`机器ID: \${deviceId};站点编号: \${code};站点中文: \${name};时间: \${timeStr}\`; + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; }; function renderList(items) {