feat: 新增设备位置追踪与网页管理页面
- 更新服务端默认Lua版本与客户端程序版本至 v1.5.16 - 新增后端API接口用于提交和查询设备位置数据 - 为售票机和检票机添加后台GPS位置同步任务,定期上报设备坐标 - 服务端新增设备位置缓存,并自动清理超过5分钟未更新的过期数据 - 新增网页端设备位置地图页面,支持实时展示、点击查看详情与每分钟自动刷新
This commit is contained in:
+62
-1
@@ -24,6 +24,7 @@ createApp({
|
||||
dashboard: '仪表盘',
|
||||
management: '线路与票价管理',
|
||||
faremap: '票价地图',
|
||||
machinemap: '机器位置',
|
||||
tickets: '车票记录',
|
||||
vouchers: '凭证管理',
|
||||
iccards: 'IC 卡管理',
|
||||
@@ -78,12 +79,14 @@ createApp({
|
||||
let logDataLoaded = false;
|
||||
let assetsLoaded = false;
|
||||
let fareMapLoaded = false;
|
||||
let machineLocationsLoaded = false;
|
||||
const loadingState = reactive({
|
||||
core: false,
|
||||
tickets: false,
|
||||
orders: false,
|
||||
logs: false,
|
||||
iccards: false
|
||||
iccards: false,
|
||||
machinemap: false
|
||||
});
|
||||
const lastSyncAt = ref(0);
|
||||
|
||||
@@ -1116,6 +1119,52 @@ createApp({
|
||||
}
|
||||
};
|
||||
|
||||
const machineLocations = ref({});
|
||||
const selectedMachineId = ref('');
|
||||
const getMapX = (x) => {
|
||||
if (typeof x !== 'number') return 50;
|
||||
// Assuming typical MC coords are between -10000 and +10000.
|
||||
// We can auto-scale based on actual data bounds instead.
|
||||
let min = 0, max = 0;
|
||||
const vals = Object.values(machineLocations.value).map(l => l.x).filter(v => typeof v === 'number');
|
||||
if (vals.length > 0) {
|
||||
min = Math.min(...vals) - 50;
|
||||
max = Math.max(...vals) + 50;
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
if (max === min) return 50;
|
||||
return ((x - min) / (max - min)) * 90 + 5; // 5% to 95% margin
|
||||
};
|
||||
const getMapY = (z) => {
|
||||
if (typeof z !== 'number') return 50;
|
||||
let min = 0, max = 0;
|
||||
const vals = Object.values(machineLocations.value).map(l => l.z).filter(v => typeof v === 'number');
|
||||
if (vals.length > 0) {
|
||||
min = Math.min(...vals) - 50;
|
||||
max = Math.max(...vals) + 50;
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
if (max === min) return 50;
|
||||
return ((z - min) / (max - min)) * 90 + 5;
|
||||
};
|
||||
|
||||
const fetchMachineLocations = async () => {
|
||||
loadingState.machinemap = true;
|
||||
try {
|
||||
const res = await requestJson('/api/machine-locations');
|
||||
if (res && res.data) {
|
||||
machineLocations.value = res.data;
|
||||
}
|
||||
machineLocationsLoaded = true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
loadingState.machinemap = false;
|
||||
}
|
||||
};
|
||||
|
||||
const ensureViewData = async (view = currentView.value, { force = false } = {}) => {
|
||||
await fetchCoreData({ force });
|
||||
if (view === 'tickets' && (force || !ticketDataLoaded)) await fetchTicketData();
|
||||
@@ -1129,6 +1178,7 @@ createApp({
|
||||
}
|
||||
if (view === 'faremap' && (force || !fareMapLoaded)) await loadFareMap({ force });
|
||||
if (view === 'assets' && (!assetsLoaded || force)) await fetchAssetsManifest();
|
||||
if (view === 'machinemap') await fetchMachineLocations();
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
@@ -1564,6 +1614,8 @@ createApp({
|
||||
syncSelectedIcCard().catch(console.error);
|
||||
});
|
||||
|
||||
let machineMapTimer = null;
|
||||
|
||||
watch(currentView, (v) => {
|
||||
sidebarOpen.value = false;
|
||||
if (v === 'iccards') {
|
||||
@@ -1571,6 +1623,14 @@ createApp({
|
||||
} else {
|
||||
stopIcCardSync();
|
||||
}
|
||||
if (v === 'machinemap') {
|
||||
if (!machineMapTimer) machineMapTimer = setInterval(fetchMachineLocations, 60000);
|
||||
} else {
|
||||
if (machineMapTimer) {
|
||||
clearInterval(machineMapTimer);
|
||||
machineMapTimer = null;
|
||||
}
|
||||
}
|
||||
ensureViewData(v).catch(console.error);
|
||||
const sp = new URLSearchParams(location.search);
|
||||
if (v === 'dashboard') sp.delete('view');
|
||||
@@ -1720,6 +1780,7 @@ createApp({
|
||||
};
|
||||
|
||||
return {
|
||||
machineLocations, selectedMachineId, getMapX, getMapY,
|
||||
currentView, viewTitle, sidebarOpen,
|
||||
loadingState, isViewBusy, lastSyncText, currentViewSummary,
|
||||
stations, lines, fares, stats, config, recentLogs, ticketList, ticketStats, orderStats,
|
||||
|
||||
Reference in New Issue
Block a user