refactor(web/route): 精简导航菜单 & 修复初始加载闪屏
1. 根据需求移除了“仪表盘”、“车票记录”、“系统设置”、“凭证管理”等多余的页面导航和对应代码。 2. 目前仅保留三个核心功能页:【线路规划】、【站点管理】、【票价地图】。 3. 修复了刷新页面时Vue未初始化完成导致的大段未解析模板闪屏的问题:引入了 v-cloak 并在 CSS 中隐藏未就绪的 DOM。 4. 确认了 /api/stations 请求本身已经直接对接数据库 DataService.saveStations,前端现也去掉了多余的视图判断,保证修改后立即反映。
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
[v-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg: #09090b;
|
||||
--surface: #18181b;
|
||||
|
||||
+2
-182
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
|
||||
<main class="jr-public-main jr-admin-main-shell">
|
||||
<div id="app" class="jr-admin-app">
|
||||
<div id="app" class="jr-admin-app" v-cloak>
|
||||
<div class="sidebar" :class="{ open: sidebarOpen }">
|
||||
<div class="jr-admin-sidebar-head">
|
||||
<span class="jr-kicker">ROUTE PLANNING</span>
|
||||
@@ -483,192 +483,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 凭证管理 -->
|
||||
<div v-if="currentView === 'vouchers'">
|
||||
<div class="card">
|
||||
<div class="flex between mb-4">
|
||||
<h4>凭证列表</h4>
|
||||
<div class="flex">
|
||||
<button @click="fetchOrders"><i class="fas fa-sync-alt"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ticket-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>凭证</th>
|
||||
<th>线路</th>
|
||||
<th>车型</th>
|
||||
<th>票价</th>
|
||||
<th>状态</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="o in orderList" :key="o.code">
|
||||
<td class="mono" style="font-weight:bold; font-size:1.1em;">{{ o.code }}</td>
|
||||
<td>{{ o.start_name }} <i class="fas fa-arrow-right text-muted"></i> {{
|
||||
o.terminal_name }}</td>
|
||||
<td>{{ formatTrainType(o.train_type) }}</td>
|
||||
<td>{{ o.price }}</td>
|
||||
<td><span class="badge" :class="formatTicketStatus(o.status).class">{{
|
||||
formatTicketStatus(o.status).text }}</span></td>
|
||||
<td>{{ formatTime(o.created_ts) }}</td>
|
||||
<td>
|
||||
<div class="flex" style="gap:4px;">
|
||||
<a :href="'token.html?code='+o.code" target="_blank" class="btn sm"
|
||||
title="查看"
|
||||
style="height:28px; width:28px; padding:0; display:flex; align-items:center; justify-content:center;"><i
|
||||
class="fas fa-eye"></i></a>
|
||||
<button class="danger sm" @click="deleteOrder(o.code)"
|
||||
style="height:28px; width:28px; padding:0; display:flex; align-items:center; justify-content:center;"><i
|
||||
class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 车票记录 -->
|
||||
<div v-if="currentView === 'tickets'">
|
||||
<div class="card mb-4">
|
||||
<div class="flex between mb-4">
|
||||
<h4>车票记录</h4>
|
||||
<div class="flex">
|
||||
<input v-model="ticketSearch" placeholder="搜索 Ticket ID / 站点" style="width: 200px;">
|
||||
<button @click="refreshData"><i class="fas fa-sync-alt"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ticket-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>起点</th>
|
||||
<th>终点</th>
|
||||
<th>类型</th>
|
||||
<th>状态</th>
|
||||
<th>时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="t in ticketList" :key="t.ticket_id" @click="viewTicketDetails(t)"
|
||||
class="clickable-row">
|
||||
<td><span class="mono">{{ t.ticket_id }}</span></td>
|
||||
<td>
|
||||
<div class="st-container">
|
||||
<div class="st-main-row">
|
||||
<span class="st-name">{{ getStationInfo(t.start).name }}</span>
|
||||
<span class="st-code">{{ t.start }}</span>
|
||||
</div>
|
||||
<div class="st-en">{{ getStationInfo(t.start).en_name }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="st-container">
|
||||
<div class="st-main-row">
|
||||
<span class="st-name">{{ getStationInfo(t.terminal).name }}</span>
|
||||
<span class="st-code">{{ t.terminal }}</span>
|
||||
</div>
|
||||
<div class="st-en">{{ getStationInfo(t.terminal).en_name }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ formatTrainType(t.type) }}</td>
|
||||
<td><span class="badge" :class="formatTicketStatus(t.status).class">{{
|
||||
formatTicketStatus(t.status).text }}</span></td>
|
||||
<td>{{ formatTime(t.ts) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 车票详情弹窗 -->
|
||||
<div v-if="showTicketModal" class="modal show" @click.self="closeTicketModal">
|
||||
<div class="modal-card" style="width: 600px;">
|
||||
<div class="flex between mb-4">
|
||||
<h4 class="modal-title">车票详情</h4>
|
||||
<button class="sm" @click="closeTicketModal" title="关闭"><i
|
||||
class="fas fa-times"></i></button>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedTicket">
|
||||
<div class="ticket-header mb-4"
|
||||
style="background: rgba(255,255,255,0.05); padding: 16px; border-radius: 8px;">
|
||||
<div class="flex between mb-2">
|
||||
<span class="mono text-muted">{{ selectedTicket.ticket_id }}</span>
|
||||
<span class="badge"
|
||||
:class="formatTicketStatus(selectedTicket.index.status).class">
|
||||
{{ formatTicketStatus(selectedTicket.index.status).text }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex between" style="font-size: 1.2rem; font-weight: bold;">
|
||||
<div class="st-container">
|
||||
<div class="st-main-row">
|
||||
<span class="st-name">{{ selectedTicket.index.start_name ||
|
||||
selectedTicket.index.start }}</span>
|
||||
<span class="st-code">{{ selectedTicket.index.start }}</span>
|
||||
</div>
|
||||
<div class="st-en">{{ selectedTicket.index.start_en || '' }}</div>
|
||||
</div>
|
||||
<i class="fas fa-arrow-right text-muted"></i>
|
||||
<div class="st-container" style="align-items: flex-end;">
|
||||
<div class="st-main-row">
|
||||
<span class="st-name">{{ selectedTicket.index.terminal_name ||
|
||||
selectedTicket.index.terminal }}</span>
|
||||
<span class="st-code">{{ selectedTicket.index.terminal }}</span>
|
||||
</div>
|
||||
<div class="st-en">{{ selectedTicket.index.terminal_en || '' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-muted mt-2" style="font-size: 0.9rem;">
|
||||
类型: {{ formatTrainType(selectedTicket.index.type ||
|
||||
selectedTicket.index.train_type) }} | 票价: {{ selectedTicket.index.price ||
|
||||
selectedTicket.index.cost }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5>行程记录</h5>
|
||||
<div class="timeline">
|
||||
<div v-for="ev in selectedTicket.events" :key="ev.ts || ev['时间戳']" class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="flex between">
|
||||
<span style="font-weight: 600;">{{ formatTicketEvent(ev) }}</span>
|
||||
</div>
|
||||
<div class="text-muted" style="font-size: 0.9rem; margin-top: 4px;">
|
||||
{{ formatTicketEventUnified(ev) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="loading">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设置 -->
|
||||
<div v-if="currentView === 'settings'">
|
||||
<div class="card mb-4">
|
||||
<h4>优惠设置</h4>
|
||||
<div class="mb-4">
|
||||
<label style="display:block; margin-bottom:8px; font-weight:600;">优惠活动</label>
|
||||
<div class="flex">
|
||||
<input v-model="config.promotion.name" placeholder="活动名称">
|
||||
<input v-model.number="config.promotion.discount" type="number" step="0.1"
|
||||
placeholder="折扣 (0.1-1.0)">
|
||||
<button @click="saveConfig"><i class="fas fa-save"></i> 保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">粤ICP备2025450093号</a>
|
||||
<span class="version">v1.0.12</span>
|
||||
</footer>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+3
-13
@@ -15,11 +15,8 @@ createApp({
|
||||
const sidebarOpen = ref(false);
|
||||
const viewTitle = computed(() => {
|
||||
const map = {
|
||||
dashboard: '仪表盘',
|
||||
management: '线路与票价管理',
|
||||
stations: '站点管理',
|
||||
tickets: '车票记录',
|
||||
settings: '系统设置',
|
||||
faremap: '票价地图'
|
||||
};
|
||||
return map[currentView.value] || '线路规划系统';
|
||||
@@ -715,10 +712,7 @@ createApp({
|
||||
}
|
||||
|
||||
Object.assign(newStation, { code: '', name: '', en_name: '' });
|
||||
if (currentView.value === 'stations') {
|
||||
showAddStation.value = false;
|
||||
await fetchData();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1030,13 +1024,9 @@ createApp({
|
||||
const deleteStationGlobal = async (code) => {
|
||||
await deleteStation(code);
|
||||
};
|
||||
if (!ticketSearch.value) return tickets.value.slice(0, 50);
|
||||
const q = ticketSearch.value.toLowerCase();
|
||||
return tickets.value.filter(t =>
|
||||
t.ticket_id.toLowerCase().includes(q) ||
|
||||
(t.start && t.start.toLowerCase().includes(q)) ||
|
||||
(t.terminal && t.terminal.toLowerCase().includes(q))
|
||||
).slice(0, 50);
|
||||
|
||||
const ticketList = computed(() => {
|
||||
return [];
|
||||
});
|
||||
|
||||
const exportFareMap = () => {
|
||||
|
||||
Reference in New Issue
Block a user