feat: 新增设备位置追踪与网页管理页面
- 更新服务端默认Lua版本与客户端程序版本至 v1.5.16 - 新增后端API接口用于提交和查询设备位置数据 - 为售票机和检票机添加后台GPS位置同步任务,定期上报设备坐标 - 服务端新增设备位置缓存,并自动清理超过5分钟未更新的过期数据 - 新增网页端设备位置地图页面,支持实时展示、点击查看详情与每分钟自动刷新
This commit is contained in:
@@ -64,6 +64,9 @@
|
||||
<div class="nav-item" :class="{active: currentView === 'faremap'}" @click="currentView = 'faremap'">
|
||||
<span class="nav-icon"><i class="fas fa-map"></i></span> 票价地图
|
||||
</div>
|
||||
<div class="nav-item" :class="{active: currentView === 'machinemap'}" @click="currentView = 'machinemap'">
|
||||
<span class="nav-icon"><i class="fas fa-map-marker-alt"></i></span> 机器位置
|
||||
</div>
|
||||
<div class="nav-item" :class="{active: currentView === 'tickets'}" @click="currentView = 'tickets'">
|
||||
<span class="nav-icon"><i class="fas fa-ticket-alt"></i></span> 车票记录
|
||||
</div>
|
||||
@@ -641,6 +644,55 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 机器位置 -->
|
||||
<div v-if="currentView === 'machinemap'">
|
||||
<div class="card">
|
||||
<h3>设备分布图</h3>
|
||||
<p class="text-muted">机器位置与状态 (每 60 秒自动更新)</p>
|
||||
<div class="map-container" style="position: relative; width: 100%; height: 600px; background: #1a1a1a; border-radius: 8px; overflow: hidden; border: 1px solid rgba(255,255,255,0.1);">
|
||||
<!-- We can use a simple coordinate mapping here. -->
|
||||
<!-- First, calculate bounds to normalize coordinates -->
|
||||
<div v-if="Object.keys(machineLocations).length === 0" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #666;">
|
||||
暂无机器坐标数据
|
||||
</div>
|
||||
<template v-else>
|
||||
<!-- Map markers -->
|
||||
<div v-for="(loc, id) in machineLocations" :key="id"
|
||||
class="map-marker"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: getMapX(loc.x) + '%',
|
||||
top: getMapY(loc.z) + '%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
cursor: 'pointer',
|
||||
zIndex: selectedMachineId === id ? 10 : 1
|
||||
}"
|
||||
@click="selectedMachineId = id"
|
||||
>
|
||||
<div :class="['marker-icon', loc.type === 'Gate' ? 'gate' : 'ticket']"
|
||||
:style="{
|
||||
width: '20px', height: '20px', borderRadius: '50%',
|
||||
background: loc.type === 'Gate' ? '#ff9800' : '#4ade80',
|
||||
border: '2px solid #fff',
|
||||
boxShadow: '0 0 10px rgba(0,0,0,0.5)'
|
||||
}">
|
||||
</div>
|
||||
<div v-if="selectedMachineId === id" class="marker-popup"
|
||||
style="position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); background: #333; padding: 10px; border-radius: 6px; width: max-content; box-shadow: 0 4px 12px rgba(0,0,0,0.3); z-index: 20; color: white; text-align: center;">
|
||||
<div style="font-weight: bold; margin-bottom: 4px;">{{ loc.device_id || id }}</div>
|
||||
<div style="font-size: 0.85em; color: #aaa;">类型: {{ loc.type === 'Gate' ? '检票机' : '售票机' }}</div>
|
||||
<div v-if="loc.type === 'Gate'" style="font-size: 0.85em; color: #aaa;">
|
||||
模式: {{ loc.gate_mode === 'entry' ? '进站' : (loc.gate_mode === 'exit' ? '出站' : '双向') }}
|
||||
</div>
|
||||
<div style="font-size: 0.85em; color: #aaa;">站点: {{ loc.station || loc.station_code || '未知' }}</div>
|
||||
<div style="font-size: 0.8em; color: #888; margin-top: 4px;">坐标: {{ loc.x }}, {{ loc.y }}, {{ loc.z }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 车票记录 -->
|
||||
<div v-if="currentView === 'tickets'">
|
||||
<div class="grid">
|
||||
|
||||
Reference in New Issue
Block a user