初步完成轨迹

This commit is contained in:
xuqiuyun
2025-12-11 17:32:36 +08:00
parent 5b44cdb3eb
commit 3ce9757eff
30 changed files with 799 additions and 1703 deletions

View File

@@ -49,10 +49,10 @@ export function inspectionList(data) {
});
}
// 查询百度鹰眼轨迹与停留点
export function getYingyanTrack(data) {
// 查询运单轨迹(基于车牌号,使用 OpenAPI
export function getDeliveryTrack(data) {
return request({
url: '/delivery/yingyan/track',
url: '/delivery/track',
method: 'POST',
data,
});

View File

@@ -369,14 +369,6 @@
<el-empty description="暂无轨迹数据" :image-size="100" />
</div>
<div v-if="yingyanMeta.entityName" class="track-meta-panel">
<el-descriptions :column="3" border>
<el-descriptions-item label="终端名称">{{ yingyanMeta.entityName }}</el-descriptions-item>
<el-descriptions-item label="查询开始">{{ formatTimestamp(yingyanMeta.startTime) }}</el-descriptions-item>
<el-descriptions-item label="查询结束">{{ formatTimestamp(yingyanMeta.endTime) }}</el-descriptions-item>
</el-descriptions>
</div>
<div v-if="latestPoint" class="latest-point-panel">
<h4>最新定位</h4>
<el-descriptions :column="3" border>
@@ -461,7 +453,7 @@ import { ref, reactive, computed, nextTick, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus';
import { Location, VideoPlay, Refresh, InfoFilled, Connection, DataLine, Loading } from '@element-plus/icons-vue';
import { BMPGL } from '@/utils/loadBmap.js';
import { pageDeviceList, getCollarLogs, getEarTagLogs, getHostLogs, getYingyanTrack, waybillDetail } from '@/api/abroad.js';
import { pageDeviceList, getCollarLogs, getEarTagLogs, getHostLogs, getDeliveryTrack, waybillDetail } from '@/api/abroad.js';
const dialogVisible = ref(false);
const warningData = reactive({
@@ -510,11 +502,6 @@ const trackBMapGL = ref(null); // 保存 BMapGL 实例,避免重复加载
const stayPoints = ref([]); // 停留点列表
const latestPoint = ref(null); // 最新轨迹点
const segmentStats = ref([]);
const yingyanMeta = reactive({
entityName: '',
startTime: null,
endTime: null
});
// 计算属性:判断预警类型
const isTemperatureWarning = computed(() => {
@@ -939,16 +926,8 @@ const handleTrackClick = async () => {
return;
}
await loadYingyanTrack();
if (trackPath.value.length === 0) {
// TODO: 接入新的轨迹服务
trackLoading.value = false;
return;
}
// 初始化地图
await nextTick();
await initTrackMap();
};
// 获取运送清单运输状态
@@ -981,16 +960,13 @@ const getDeliveryStatus = async () => {
}
};
// 加载百度鹰眼轨迹与停留点
// 加载运单轨迹(基于车牌号,使用 OpenAPI
const loadYingyanTrack = async () => {
stayPoints.value = [];
trackPath.value = [];
latestPoint.value = null;
segmentStats.value = [];
trackMapShow.value = false;
yingyanMeta.entityName = '';
yingyanMeta.startTime = null;
yingyanMeta.endTime = null;
if (!warningData.deliveryId) {
ElMessage.warning('运单ID缺失无法查询轨迹');
@@ -998,11 +974,10 @@ const loadYingyanTrack = async () => {
}
try {
const res = await getYingyanTrack({ deliveryId: warningData.deliveryId });
const res = await getDeliveryTrack({ deliveryId: warningData.deliveryId });
console.info('[TRACK] 后端轨迹接口响应', res);
if (res.code === 200 && res.data) {
segmentStats.value = Array.isArray(res.data.segmentStats) ? res.data.segmentStats : [];
console.info('[TRACK] 分段统计', segmentStats.value);
const rawPoints = Array.isArray(res.data.trackPoints) ? res.data.trackPoints : [];
trackPath.value = rawPoints
.map(item => {
@@ -1019,30 +994,18 @@ const loadYingyanTrack = async () => {
})
.filter(Boolean);
stayPoints.value = Array.isArray(res.data.stayPoints) ? res.data.stayPoints : [];
yingyanMeta.entityName = res.data.entityName || '';
yingyanMeta.startTime = res.data.startTime || null;
yingyanMeta.endTime = res.data.endTime || null;
latestPoint.value = parseLatestPoint(res.data.latestPoint);
console.info('[TRACK] 轨迹点数量', trackPath.value.length);
if (trackPath.value.length > 0) {
trackMapShow.value = true;
} else if (latestPoint.value) {
trackPath.value.push({
lng: latestPoint.value.lng,
lat: latestPoint.value.lat,
locTime: latestPoint.value.locTime || null,
});
trackMapShow.value = true;
} else {
ElMessage.warning('暂无有效轨迹点');
ElMessage.warning(res.data.message || '暂无有效轨迹点');
}
} else {
ElMessage.warning(res.msg || '暂无轨迹数据');
}
} catch (error) {
console.error('[TRACK] 加载百度鹰眼轨迹失败:', error);
console.error('[TRACK] 加载轨迹失败:', error);
ElMessage.error('加载轨迹数据失败');
}
};
@@ -1345,9 +1308,6 @@ const handleTrackDialogClose = () => {
trackPath.value = [];
trackMapShow.value = false;
stayPoints.value = [];
yingyanMeta.entityName = '';
yingyanMeta.startTime = null;
yingyanMeta.endTime = null;
};
// 组件卸载时清理

View File

@@ -142,9 +142,17 @@
<el-empty description="暂无轨迹数据" :image-size="100" />
</div>
<div v-else class="track-info">
<el-tag type="info" style="margin-bottom: 15px;">
轨迹点数{{ trackPath.length }}
</el-tag>
<div class="track-summary">
<el-tag type="info">轨迹点数{{ trackPath.length }}</el-tag>
<el-tag v-if="trackMileage" type="success">总里程{{ trackMileage }} km</el-tag>
<el-tag v-if="trackParkSize > 0" type="warning">停车次数{{ trackParkSize }}</el-tag>
</div>
<div v-if="trackParks.length > 0" class="park-list">
<span class="park-title">停车列表</span>
<span v-for="(p, idx) in trackParks" :key="idx" class="park-item">
{{ p.name }} ({{ p.lng }}, {{ p.lat }})
</span>
</div>
<div id="trackMap" style="width: 100%; height: 500px;"></div>
</div>
</div>
@@ -162,7 +170,7 @@ import baseSearch from '@/components/common/searchCustom/index.vue';
import Pagination from '@/components/Pagination/index.vue';
import { orderPageQuery, orderDelete, orderBatchImport } from '@/api/shipping.js';
import { memberListByType, userAdd } from '@/api/userManage.js';
import { getYingyanTrack, waybillDetail } from '@/api/abroad.js';
import { getDeliveryTrack, waybillDetail } from '@/api/abroad.js';
import { BMPGL } from '@/utils/loadBmap.js';
import { nextTick } from 'vue';
import OrderDialog from './orderDialog.vue';
@@ -178,6 +186,9 @@ const fileInputRef = ref();
const trackDialogVisible = ref(false);
const trackLoading = ref(false);
const trackPath = ref([]);
const trackMileage = ref(''); // 总里程 km
const trackParkSize = ref(0); // 停车次数
const trackParks = ref([]); // 停车列表
const deliveryStatus = ref(null);
const trackMapInstance = ref(null);
const formItemList = reactive([
@@ -398,34 +409,86 @@ const viewTrack = async (deliveryId, status) => {
trackDialogVisible.value = true;
trackLoading.value = true;
trackPath.value = [];
trackMileage.value = '';
trackParkSize.value = 0;
trackParks.value = [];
deliveryStatus.value = status;
try {
const res = await getYingyanTrack({ deliveryId: deliveryId });
const res = await getDeliveryTrack({ deliveryId: deliveryId });
console.info('[track] response data:', res?.data);
if (res.code === 200 && res.data) {
const rawPoints = Array.isArray(res.data.trackPoints) ? res.data.trackPoints : [];
trackPath.value = rawPoints
const data = res.data;
// 尝试解析原始响应(兜底 trackArray/parkArray
let rawTrackFromResp = [];
let rawParkFromResp = [];
if (!Array.isArray(data.trackArray) && data.rawResponse) {
try {
const rawObj = JSON.parse(data.rawResponse);
rawTrackFromResp = Array.isArray(rawObj?.result?.trackArray) ? rawObj.result.trackArray : [];
rawParkFromResp = Array.isArray(rawObj?.result?.parkArray) ? rawObj.result.parkArray : [];
console.info('[track] parsed rawResponse trackArray len:', rawTrackFromResp.length, 'parkArray len:', rawParkFromResp.length);
} catch (e) {
console.warn('解析 rawResponse 失败', e);
}
}
// 里程/停车
trackMileage.value = data.mileage || '';
trackParkSize.value = Number(data.parkSize || 0);
const parkArray = Array.isArray(data.parkArray) ? data.parkArray : rawParkFromResp;
trackParks.value = parkArray
.map((p, idx) => {
const lng = parseFloat(p.lon ?? p.lng ?? p.longitude ?? 0);
const lat = parseFloat(p.lat ?? p.latitude ?? 0);
return {
name: `停车点${idx + 1}`,
lng,
lat
};
})
.filter(p => !Number.isNaN(p.lng) && !Number.isNaN(p.lat) && p.lng !== 0 && p.lat !== 0);
// 轨迹
const rawTrack = Array.isArray(data.trackArray)
? data.trackArray
: (Array.isArray(data.trackPoints) ? data.trackPoints : rawTrackFromResp);
console.info('[track] source arrays len -> trackArray:', Array.isArray(data.trackArray) ? data.trackArray.length : 0,
'trackPoints:', Array.isArray(data.trackPoints) ? data.trackPoints.length : 0,
'rawTrackFromResp:', rawTrackFromResp.length);
trackPath.value = rawTrack
.map(item => {
const lng = parseFloat(item.longitude ?? item.lng ?? 0);
const lat = parseFloat(item.latitude ?? item.lat ?? 0);
let lng = parseFloat(item.lon ?? item.lng ?? item.longitude ?? 0);
let lat = parseFloat(item.lat ?? item.latitude ?? 0);
// 如果疑似放大(>180/90尝试除以600000
if (Math.abs(lng) > 180 || Math.abs(lat) > 90) {
lng = lng / 600000;
lat = lat / 600000;
}
if (Number.isNaN(lng) || Number.isNaN(lat) || lng === 0 || lat === 0) {
return null;
}
return {
lng,
lat,
locTime: item.locTime
locTime: item.locTime || item.timestamp || item.utc || item.gtm || '',
speed: item.spd || item.speed || '',
direction: item.agl || item.direction || '',
altitude: item.hgt || item.altitude || '',
mileage: item.mlg || ''
};
})
.filter(Boolean);
console.info('[track] normalized trackPath len:', trackPath.value.length,
'sample:', trackPath.value.slice(0, 3));
if (trackPath.value.length === 0) {
ElMessage.warning('暂无有效轨迹点');
ElMessage.warning(data.message || '暂无有效轨迹点');
trackLoading.value = false;
return;
}
// 初始化地图
await nextTick();
await initTrackMap();
} else {