From 6098590692c43338830a247e6476b498116a49d9 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Thu, 9 Jul 2026 23:58:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=8D=95=E7=A8=8B=E7=A5=A8=E4=BB=B7=E3=80=81=E7=AB=99?= =?UTF-8?q?=E7=82=B9=E5=90=8D=E7=A7=B0=E5=B1=95=E7=A4=BA=E5=8F=8A=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E4=BA=8B=E4=BB=B6=E7=B1=BB=E5=9E=8B=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端调整:支持单程订单使用客户端传入的自定义票价,无需通过系统自动计算 前端优化:新增中文事件类型支持,添加站点编码转中文名称功能,更新日志展示站点中文名称,自动拉取站点列表构建解析映射 --- server/routes/public.js | 16 +++++++++++++--- web/ic-card-admin.js | 26 +++++++++++++++++++++----- web/ic-card-search.js | 34 +++++++++++++++++++++++++++------- web/index.js | 21 ++++++++++++++------- web/ticket-route.js | 10 ++++++---- web/ticket-search.js | 38 +++++++++++++++++++++++++++----------- 6 files changed, 108 insertions(+), 37 deletions(-) diff --git a/server/routes/public.js b/server/routes/public.js index 5ecbf55..1e38931 100644 --- a/server/routes/public.js +++ b/server/routes/public.js @@ -257,9 +257,19 @@ router.post('/orders', async (req, res) => { return res.status(400).json({ ok: false, error: 'missing start/terminal/ride_date' }); } const resolveStation = buildStationResolver(); - const fromCode = resolveStation(from); - const toCode = resolveStation(to); - const priceSingle = LogicService.computePrice(fromCode, toCode, type); + let fromCode = resolveStation(from); + let toCode = resolveStation(to); + + let priceSingle = 0; + if (type === 'single') { + priceSingle = Math.max(0, Number(req.body.price) || 0); + // keep from and to as whatever string was passed (e.g. line and fare) + fromCode = from; + toCode = to; + } else { + priceSingle = LogicService.computePrice(fromCode, toCode, type); + } + const price = Math.max(0, priceSingle * t); const code = LogicService.genVoucherCode(); const rec = { code, start: fromCode, terminal: toCode, train_type: type, trips: t, ride_date: date, price, created_ts: Date.now(), consumed: false }; diff --git a/web/ic-card-admin.js b/web/ic-card-admin.js index 6c87391..5c10a7d 100644 --- a/web/ic-card-admin.js +++ b/web/ic-card-admin.js @@ -199,18 +199,23 @@ const cardType = ev?.detail?.card_type || ev?.card_type || ev?.["类型"] || '储值卡'; 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; + const code = ev?.station_code || ev?.["站点编号"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); + if (code !== '---' || ev?.station_name) { + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};充值数额: ${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?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); 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?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } @@ -355,7 +360,18 @@ }, 240); }); + let stationMap = {}; + window.getStationName = (code) => stationMap[code] || code; + (async () => { + try { + const res = await fetch('/api/public/stations'); + const list = await res.json(); + if (Array.isArray(list)) { + list.forEach(s => { stationMap[s.code] = s.name || s.cn_name; }); + } + } catch(e) {} + await pingServer(); await refreshList(false); if (state.cards[0]) await loadCard(state.cards[0].card_id); diff --git a/web/ic-card-search.js b/web/ic-card-search.js index bcd29c8..f63ca17 100644 --- a/web/ic-card-search.js +++ b/web/ic-card-search.js @@ -100,16 +100,21 @@ } if (type === 'topup' || type === 'refill' || action === 'topup' || type === '充值') { const amount = ev?.amount ?? ev?.["充值数额"] ?? 0; + const code = ev?.station_code || ev?.["站点编号"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); + if (code !== '---' || ev?.station_name) { + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};充值数额: ${amount};余额: ${balance};时间: ${timeStr}`; + } return `机器ID: ${deviceId};充值数额: ${amount};余额: ${balance};时间: ${timeStr}`; } if (action === 'entry' || type === 'entry' || type === '进站') { const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } if (action === 'exit' || type === 'exit' || type === '出站') { const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } @@ -261,10 +266,25 @@ const params = new URLSearchParams(location.search); const q = params.get('q'); - if (q) { - inputEl.value = q; - doQuery().catch((error) => renderQueryError(error.message || String(error))); - } else { - loadAllCards().catch((error) => renderQueryError(error.message || String(error))); + + let stationMap = {}; + window.getStationName = (code) => stationMap[code] || code; + + async function init() { + try { + const res = await fetch('/api/public/stations'); + const list = await res.json(); + if (Array.isArray(list)) { + list.forEach(s => { stationMap[s.code] = s.name || s.cn_name; }); + } + } catch(e) {} + + if (q) { + inputEl.value = q; + doQuery().catch((error) => renderQueryError(error.message || String(error))); + } else { + loadAllCards().catch((error) => renderQueryError(error.message || String(error))); + } } + init(); })(); diff --git a/web/index.js b/web/index.js index a4e1fb0..c77d682 100644 --- a/web/index.js +++ b/web/index.js @@ -353,22 +353,24 @@ createApp({ if (type === 'sale' || type === '售票') { const cost = ev?.cost ?? ev?.price ?? ev?.amount ?? ev?.["票价"] ?? ev?.["售票额"] ?? '0'; - return `机器ID: ${deviceId};票价: ${cost};时间: ${timeStr}`; + const code = ev?.station_code || ev?.["站点编号"] || '---'; + const name = ev?.station_name || ev?.["售票站"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};票价: ${cost};时间: ${timeStr}`; } if (type === 'entry' || action === 'entry') { const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } if (type === 'exit' || action === 'exit') { const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } // fallback const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; }; @@ -459,18 +461,23 @@ createApp({ const cardType = ev?.detail?.card_type || ev?.card_type || ev?.["类型"] || '储值卡'; 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; + const code = ev?.station_code || ev?.["站点编号"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); + if (code !== '---' || ev?.station_name) { + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};充值数额: ${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?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); 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?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};余额: ${balance};时间: ${timeStr}`; } diff --git a/web/ticket-route.js b/web/ticket-route.js index 7e8d7b0..3f03f56 100644 --- a/web/ticket-route.js +++ b/web/ticket-route.js @@ -233,22 +233,24 @@ createApp({ if (type === 'sale' || type === '售票') { const cost = ev?.cost ?? ev?.price ?? ev?.amount ?? ev?.["票价"] ?? ev?.["售票额"] ?? '0'; - return `机器ID: ${deviceId};票价: ${cost};时间: ${timeStr}`; + const code = ev?.station_code || ev?.["站点编号"] || '---'; + const name = ev?.station_name || ev?.["售票站"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};票价: ${cost};时间: ${timeStr}`; } if (type === 'entry' || action === 'entry') { const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } if (type === 'exit' || action === 'exit') { const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } // fallback const code = ev?.station_code || ev?.["站点编号"] || '---'; - const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---'; + const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; }; diff --git a/web/ticket-search.js b/web/ticket-search.js index cf041e8..3070bcf 100644 --- a/web/ticket-search.js +++ b/web/ticket-search.js @@ -115,22 +115,24 @@ if (type === 'sale' || type === '售票') { const cost = ev.cost ?? ev.price ?? ev.amount ?? ev["票价"] ?? ev["售票额"] ?? '0'; - return `机器ID: ${deviceId};票价: ${cost};时间: ${timeStr}`; + const code = ev.station_code || ev["站点编号"] || '---'; + const name = ev.station_name || ev["售票站"] || ev["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); + return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};票价: ${cost};时间: ${timeStr}`; } if (type === 'entry' || action === 'entry') { const code = ev.station_code || ev["站点编号"] || '---'; - const name = ev.station_name || ev["站点中文"] || ev["发生站"] || '---'; + const name = ev.station_name || ev["站点中文"] || ev["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } if (type === 'exit' || action === 'exit') { const code = ev.station_code || ev["站点编号"] || '---'; - const name = ev.station_name || ev["站点中文"] || ev["发生站"] || '---'; + const name = ev.station_name || ev["站点中文"] || ev["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; } // fallback const code = ev.station_code || ev["站点编号"] || '---'; - const name = ev.station_name || ev["站点中文"] || ev["发生站"] || '---'; + const name = ev.station_name || ev["站点中文"] || ev["发生站"] || (typeof getStationName === 'function' && code !== '---' ? getStationName(code) : code); return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`; }; @@ -302,13 +304,27 @@ const sp = new URLSearchParams(location.search); const qid = sp.get('id'); - if (qid) { - qEl.value = qid; - loadDetail(qid); - doSearch(); - } else { - doSearch(); + let stationMap = {}; + window.getStationName = (code) => stationMap[code] || code; + + async function init() { + try { + const res = await fetch('/api/public/stations'); + const list = await res.json(); + if (Array.isArray(list)) { + list.forEach(s => { stationMap[s.code] = s.name || s.cn_name; }); + } + } catch(e) {} + + if (qid) { + qEl.value = qid; + loadDetail(qid); + doSearch(); + } else { + doSearch(); + } + loadPopular(); } - loadPopular(); + init(); })();