refactor(web): 重构全站界面与事件日志,优化功能与样式

1. 重构并统一票务、IC卡的事件日志格式化展示逻辑,替换原有零散的格式化函数,提升事件展示的一致性与可读性
2. 为AI助手面板新增折叠按钮与折叠逻辑,支持收起/展开面板,优化界面空间利用率
3. 移除多个页面的冗余介绍、提醒区块,精简页面结构
4. 修复IC卡购卡页面的充值选项按钮布局问题,将button元素改为div适配样式规范
5. 新增移动端卡片布局的横向滚动适配,优化小屏设备上的卡片展示与浏览体验
6. 更新后台管理页面的导航样式、文案与统计数据展示,新增车票、凭证的统计卡片
7. 统一全站品牌文案与页面格式,修复部分页面的DOCTYPE多余空格问题
8. 简化车票订单页面的地图加载逻辑,修复原有的冗余代码与加载异常问题
This commit is contained in:
2026-07-05 14:55:12 +08:00
parent ef9926dc58
commit 95893a5431
19 changed files with 483 additions and 793 deletions
+72 -25
View File
@@ -345,23 +345,31 @@ createApp({
return String(value);
};
const formatTicketEventExtra = (event) => {
const type = getTicketEventType(event);
const amount = event?.["售票额"] ?? event?.amount ?? event?.price ?? event?.cost;
const stationEn = event?.["站点英文"] || event?.station_en || '';
const deviceId = event?.["设备编号"] || event?.device_id || event?.device || '';
const attachment = formatTicketEventAttachment(
event?.["附加信息"] ?? event?.extra ?? event?.info ?? event?.meta ?? event?.detail
);
const parts = [];
if ((type === 'sale' || type === '售票') && amount != null && amount !== '') {
parts.push(`票价:¥ ${amount}`);
const formatTicketEventUnified = (ev) => {
const type = getTicketEventType(ev);
const action = getTicketEventAction(ev);
const deviceId = ev?.device || ev?.device_id || ev?.["设备编号"] || ev?.["机器ID"] || '---';
const timeStr = formatTime(ev?.ts || ev?.["时间戳"]);
if (type === 'sale' || type === '售票') {
const cost = ev?.cost ?? ev?.price ?? ev?.amount ?? ev?.["票价"] ?? ev?.["售票额"] ?? '0';
return `机器ID: ${deviceId};票价: ${cost};时间: ${timeStr}`;
}
if (stationEn && deviceId) parts.push(`${stationEn} (${deviceId})`);
else if (deviceId) parts.push(`设备:${deviceId}`);
else if (stationEn) parts.push(stationEn);
if (attachment) parts.push(attachment);
return parts.join(' | ');
if (type === 'entry' || action === 'entry') {
const code = ev?.station_code || ev?.["站点编号"] || '---';
const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---';
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}`;
}
// fallback
const code = ev?.station_code || ev?.["站点编号"] || '---';
const name = ev?.station_name || ev?.["站点中文"] || ev?.["发生站"] || '---';
return `机器ID: ${deviceId};站点编号: ${code};站点中文: ${name};时间: ${timeStr}`;
};
const formatLogDetail = (l) => {
@@ -440,14 +448,34 @@ createApp({
return map[String(event?.type || '').toLowerCase()] || (event?.type || '事件');
};
const formatIcEventDetail = (event) => {
const detail = event?.detail != null ? event.detail : event;
if (typeof detail === 'string') return detail;
try {
return JSON.stringify(detail || {}, null, 2);
} catch (_) {
return String(detail || '');
const formatIcEventDetail = (ev) => {
const type = String(ev?.type || '').toLowerCase();
const action = String(ev?.action || '').toLowerCase();
const deviceId = ev?.device || ev?.device_id || ev?.["设备编号"] || ev?.["机器ID"] || '---';
const timeStr = formatTime(ev?.ts);
const balance = ev?.balance ?? ev?.["余额"] ?? 0;
if (type === 'create' || type === 'order_created' || type === 'activated') {
const cardType = ev?.detail?.card_type || ev?.card_type || ev?.["类型"] || '储值卡';
return \`[${cardType}];余额: \${balance};时间: \${timeStr}\`;
}
if (type === 'topup' || type === 'refill' || action === 'topup') {
const amount = ev?.amount ?? ev?.["充值数额"] ?? 0;
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}\`;
}
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}\`;
}
// fallback
return \`时间: \${timeStr};\${typeof ev?.detail === 'string' ? ev.detail : JSON.stringify(ev?.detail || {}, null, 2)}\`;
};
const getStationName = (code) => {
@@ -1540,6 +1568,25 @@ createApp({
(t.terminal && t.terminal.toLowerCase().includes(q))
).slice(0, 50);
});
const ticketStats = computed(() => {
const all = tickets.value;
return {
total: all.length,
valid: all.filter(t => t.status === 'valid').length,
used: all.filter(t => t.status === 'used').length
};
});
const orderStats = computed(() => {
const all = orders.value;
return {
total: all.length,
valid: all.filter(o => o.status === 'valid').length,
used: all.filter(o => o.status === 'used').length,
expired: all.filter(o => o.status === 'expired').length
};
});
const exportFareMap = () => {
const svgData = fareMapSvg.value;
@@ -1582,7 +1629,7 @@ createApp({
return {
currentView, viewTitle, sidebarOpen,
loadingState, isViewBusy, lastSyncText, currentViewSummary,
stations, lines, fares, stats, config, recentLogs, ticketList,
stations, lines, fares, stats, config, recentLogs, ticketList, ticketStats, orderStats,
logs, logCategory, logTypeFilter, logQuery, logMax, logLoading, fetchLogs,
orders, orderList, fetchOrders, deleteOrder,
icCards, icCardSearch, icSelectedId, icSelectedCard, icSelectedEvents, icCreateForm, icDetailForm, icCardStats,
@@ -1602,7 +1649,7 @@ createApp({
showLineModal, lineForm, openLineModal, saveLineSettings, closeLineModal,
// Tickets
showTicketModal, selectedTicket, viewTicketDetails, closeTicketModal, formatTicketStatus, formatTicketEvent, formatTicketEventLocation, formatTicketEventExtra, formatLogType, formatTrainType,
showTicketModal, selectedTicket, viewTicketDetails, closeTicketModal, formatTicketStatus, formatTicketEvent, formatTicketEventLocation, formatTicketEventUnified, formatLogType, formatTrainType,
saveCurrentFare, deleteCurrentFare, closeFareModal,