You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
444 lines
17 KiB
444 lines
17 KiB
<script setup>
|
|
import {onMounted, ref, onUnmounted, reactive, watch, computed} from "vue";
|
|
import * as Taro from "@tarojs/taro";
|
|
import * as turf from '@turf/turf';
|
|
import LeftSide from './LeftSide.vue';
|
|
import RightSide from './RightSide.vue';
|
|
import BottomSide from './BottomSide.vue';
|
|
import { creatEightShaped } from '../flightMap/utils';
|
|
// import ResultModal from './ResultModal.vue';
|
|
import { useAirFieldsStore, useStandardStore, useSupervisionStore } from "../../stores";
|
|
import { useDroneMarker } from "./useDroneMarker";
|
|
import { storeToRefs } from 'pinia';
|
|
import { GPS2GCJ } from '../../utils/helpers';
|
|
|
|
const { markers, rotate, circles, createCircle, showEight, polygons, setCallBack, polyline, distanceText } = useDroneMarker();
|
|
const { getExamList, getEnvList } = useStandardStore()
|
|
const { wsDroneData } = useSupervisionStore();
|
|
const { position, attitude, battery, gps, tip, deviation, connectLoading, connectDrone, droneOnLine } = storeToRefs(useSupervisionStore());
|
|
const { getAirFieldsOfStudent } = useAirFieldsStore();
|
|
const { formData } = storeToRefs(useAirFieldsStore());
|
|
|
|
const { examList, envList } = storeToRefs(useStandardStore());
|
|
|
|
const envItem = computed(() => {
|
|
return envList.value.find((item) => item.id === formData.value.envGradeId) || {};
|
|
|
|
});
|
|
const standardData = computed(() => {
|
|
return examList.value.find((item) => item.name === '8字圆圈半径' && item.licenseLevelId === formData.value.licenseGradeId && item.envGrade === envItem.value.gradeName) || {};
|
|
});
|
|
|
|
const standardDiffData = computed(() => {
|
|
return examList.value.find((item) => item.name === '8字水平偏差阈值' && item.licenseLevelId === formData.value.licenseGradeId && item.envGrade === envItem.value.gradeName) || {};
|
|
});
|
|
const point3standardData = computed(() => {
|
|
return examList.value.find((item) => item.name === '点3中心筒范围-内' && item.licenseLevelId === formData.value.licenseGradeId && item.envGrade === envItem.value.gradeName) || {};
|
|
});
|
|
|
|
getExamList().catch(({ msg }) => {
|
|
if (msg) openToast('warn', msg);
|
|
});
|
|
|
|
const { params } = Taro.useRouter();
|
|
console.log('params', params);
|
|
|
|
// 地图上下文
|
|
let mapContext = null;
|
|
|
|
const show = ref(true);
|
|
|
|
// 初始化地图
|
|
onMounted(() => {
|
|
mapContext = Taro.createMapContext('map');
|
|
show.value = false;
|
|
connect();
|
|
// init();
|
|
// showEight();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
// eventBus.off('show-teaching-track-replay')
|
|
if (ws) {
|
|
confirmClose.value = true;
|
|
ws.close();
|
|
ws = null;
|
|
}
|
|
})
|
|
|
|
// 多边形配置
|
|
// const polygons = ref([]);
|
|
// 圆形标记配置
|
|
// const circles = ref([]);
|
|
// 标记点配置
|
|
const markerPoints = ref([]);
|
|
// 地图旋转角度
|
|
const bearing = ref(0);
|
|
|
|
let ws;
|
|
const confirmClose = ref(false);
|
|
function connect() {
|
|
// if (!params?.droneSn) {
|
|
// openToast('warn', '未获取到无人机编号,请重新选择无人机。');
|
|
// return;
|
|
// }
|
|
wsDroneData(params?.droneSn).then(wsTask => {
|
|
ws = wsTask;
|
|
wsTask.onMessage( () => {
|
|
if (!center.value.lat || !center.value.lng) {
|
|
center.value.lat = position.value.lat;
|
|
center.value.lng = position.value.lng;
|
|
}
|
|
// if (firstMarker.value && position.value?.lng) {
|
|
// initDevice({ ...position.value, ...attitude.value }, bearing.value);
|
|
// firstMarker.value = false;
|
|
// } else {
|
|
// moveDevice({ ...position.value, ...attitude.value }, bearing.value)
|
|
// }
|
|
});
|
|
|
|
wsTask.onError(() => {
|
|
// clearDevice()
|
|
// setTimeout(() => {
|
|
// sum += 1;
|
|
// connect();
|
|
// }, 3000);
|
|
ws.close()
|
|
});
|
|
|
|
wsTask.onClose(() => {
|
|
// initDevice();
|
|
if (confirmClose.value) return;
|
|
ws.close();
|
|
setTimeout(() => {
|
|
// sum += 1;
|
|
connect();
|
|
}, 3000);
|
|
})
|
|
});
|
|
}
|
|
|
|
const enableRotate = ref(false);
|
|
// function init() {
|
|
// // 获取飞行详情和轨迹数据
|
|
// Promise.all([
|
|
// getExamList(),
|
|
// getEnvList(),
|
|
// // getAirFieldsDetail('9' || '10'),
|
|
// // getAirFieldsOfStudent(),
|
|
// ]).then(([examlist, envlist, airFieldsData]) => {
|
|
// console.log('air', airFieldsData);
|
|
// // const airfield = airFieldsData?.data || {};
|
|
// const { records = [] } = examlist || {}
|
|
// const envItem = (envlist || []).find((item) => item.id === airfield.envGradeId) || {}
|
|
// const standardData = records.find(
|
|
// (item) =>
|
|
// item.name === '8字圆圈半径' &&
|
|
// item.licenseLevelId === airfield.licenseGradeId &&
|
|
// item.envGrade === envItem.gradeName,
|
|
// )
|
|
// const standardDiffData = records.find(
|
|
// (item) =>
|
|
// item.name === '8字水平偏差阈值' &&
|
|
// item.licenseLevelId === airfield.licenseGradeId &&
|
|
// item.envGrade === envItem.gradeName,
|
|
// )
|
|
// const point3standardData = records.find(
|
|
// (item) =>
|
|
// item.name === '点3中心筒范围-内' &&
|
|
// item.licenseLevelId === airfield.licenseGradeId &&
|
|
// item.envGrade === envItem.gradeName,
|
|
// );
|
|
//
|
|
// const center1 = GPS2GCJ([airfield.circle1Lng, airfield.circle1Lat]);
|
|
// const center2 = GPS2GCJ([airfield.circle2Lng, airfield.circle2Lat]);
|
|
//
|
|
// const { polygons: shapePolygons, circles: shapeCircles, markers: shapeMarkers } = creatEightShaped(
|
|
// {
|
|
// center: center1,
|
|
// radius: standardData.value,
|
|
// radiusDiff: standardDiffData.value,
|
|
// centerWidth: 0.1,
|
|
// },
|
|
// {
|
|
// center: center2,
|
|
// radius: standardData.value,
|
|
// radiusDiff: standardDiffData.value,
|
|
// centerWidth: 0.1,
|
|
// },
|
|
// [6, 0, 1, 3, 4, 5, 2],
|
|
// { radius: point3standardData.value },
|
|
// );
|
|
//
|
|
// // // 更新地图显示数据
|
|
// // polygons.value = shapePolygons;
|
|
// // setTimeout(() => {
|
|
// // circles.value = shapeCircles;
|
|
// // markerPoints.value = shapeMarkers;
|
|
// // }, 500);
|
|
//
|
|
// // 设置地图视野
|
|
// mapContext.includePoints({
|
|
// points: [
|
|
// { latitude: center1[1], longitude: center1[0] },
|
|
// { latitude: center2[1], longitude: center2[0] }
|
|
// ],
|
|
// // padding: [130, 50, 130, 10],
|
|
// success: (res) => {
|
|
// setTimeout(() => {
|
|
// // 计算两个圆心之间的方位角并转换为地图旋转角度
|
|
// const angle = turf.bearing(center1, center2);
|
|
// // 调整偏移量使八字飞行路径保持垂直显示
|
|
// rotate.value = 90 - angle;
|
|
// bearing.value = 90 - angle;
|
|
// connect();
|
|
// }, 500);
|
|
// // console.log('地图视野设置成功', res);
|
|
// },
|
|
// fail: (err) => {
|
|
// enableRotate.value = true;
|
|
// console.error('地图视野设置失败', err);
|
|
// }
|
|
// });
|
|
// }).catch(({ msg }) => {
|
|
// if (msg) openToast('warn', msg);
|
|
// })
|
|
// }
|
|
|
|
const center = ref({
|
|
lat: 0,
|
|
lng: 0,
|
|
})
|
|
|
|
setCallBack((index) => {
|
|
openToast('success', `成功打点圆${index}`);
|
|
})
|
|
const state = reactive({
|
|
msg: '错误提示',
|
|
type: 'warn',
|
|
show: false,
|
|
cover: true,
|
|
// title: '',
|
|
// bottom: '',
|
|
center: true,
|
|
});
|
|
|
|
function openToast(type = 'warn', msg = '错误提示') {
|
|
state.msg = msg;
|
|
state.type = type;
|
|
state.show = true;
|
|
}
|
|
|
|
function onHandleCreateCircle(index) {
|
|
if (gps.value.fixType !== 'GPS_FIX_TYPE_RTK_FIXED' && gps.value.fixType !== 'GPS_FIX_TYPE_RTK_FLOAT') {
|
|
openToast('warn', '请确保无人机处于RTK模式下');
|
|
return;
|
|
}
|
|
// let next;
|
|
if (index === 2 && formData.value.circle1Lat) {
|
|
// const center1 = [position.value.lng, position.value.lat];
|
|
// const center2 = GPS2GCJ([formData.value.circle2Lng, formData.value.circle2Lat]);
|
|
const center1 = turf.point([position.value?.lng, position.value?.lat]);
|
|
const center2 = turf.point(GPS2GCJ([formData.value.circle1Lng, formData.value.circle1Lat]));
|
|
const dis = turf.distance(center1, center2, { units: 'meters' }).toFixed(1);
|
|
const tmp = (standardData.value?.value || 0) * 2;
|
|
console.log('ada', dis, tmp);
|
|
if (Math.abs(tmp - dis) > 0.5) {
|
|
openToast('warn', '请确保误差处于50公分内');
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (index === 1 && formData.value.circle2Lat) {
|
|
// const center1 = [position.value.lng, position.value.lat];
|
|
// const center2 = GPS2GCJ([formData.value.circle2Lng, formData.value.circle2Lat]);
|
|
const center1 = turf.point([position.value?.lng, position.value?.lat]);
|
|
const center2 = turf.point(GPS2GCJ([formData.value.circle2Lng, formData.value.circle2Lat]));
|
|
const dis = turf.distance(center1, center2, { units: 'meters' }).toFixed(1);
|
|
const tmp = (standardData.value?.value || 0) * 2;
|
|
if (Math.abs(tmp - dis) > 0.5) {
|
|
openToast('warn', '请确保误差处于50公分内');
|
|
return;
|
|
}
|
|
}
|
|
|
|
createCircle(index);
|
|
const { circle1Lat, circle1Lng, circle2Lat, circle2Lng } = formData.value;
|
|
if (circle1Lat && circle1Lng && circle2Lng && circle2Lat) {
|
|
const center1 = GPS2GCJ([formData.value.circle1Lng, formData.value.circle1Lat]);
|
|
const center2 = GPS2GCJ([formData.value.circle2Lng, formData.value.circle2Lat]);
|
|
bearing.value = 90 - turf.bearing(center1, center2);
|
|
rotate.value = bearing.value;
|
|
showEight();
|
|
}
|
|
}
|
|
|
|
watch([() => formData.value.envGradeId, () => formData.value.licenseGradeId], () => {
|
|
const { circle1Lat, circle1Lng, circle2Lat, circle2Lng } = formData.value;
|
|
if (circle1Lat && circle1Lng && circle2Lng && circle2Lat) {
|
|
const center1 = GPS2GCJ([formData.value.circle1Lng, formData.value.circle1Lat]);
|
|
const center2 = GPS2GCJ([formData.value.circle2Lng, formData.value.circle2Lat]);
|
|
bearing.value = 90 - turf.bearing(center1, center2);
|
|
rotate.value = bearing.value;
|
|
showEight();
|
|
}
|
|
})
|
|
|
|
// function showEight() {
|
|
// const center1 = GPS2GCJ([formData.value.circle1Lng, formData.value.circle1Lat]);
|
|
// const center2 = GPS2GCJ([formData.value.circle2Lng, formData.value.circle2Lat]);
|
|
//
|
|
// // const center1 = [-122.3895127, 37.6280898];
|
|
// // const center2 = [-122.3894255, 37.6281725];
|
|
// console.log(center1, center2);
|
|
// const { polygons: shapePolygons, circles: shapeCircles, markers: shapeMarkers } = creatEightShaped({
|
|
// center: center1,
|
|
// radius: standardData.value.value,
|
|
// radiusDiff: standardDiffData.value.value,
|
|
// centerWidth: 0.1,
|
|
// }, {
|
|
// center: center2,
|
|
// radius: standardData.value.value,
|
|
// radiusDiff: standardDiffData.value.value,
|
|
// centerWidth: 0.1,
|
|
// },
|
|
// [6, 0, 1, 3, 4, 5, 2],
|
|
// { radius: point3standardData.value });
|
|
// extCircles.value = [...shapeCircles];
|
|
// polygons.value = [...shapePolygons];
|
|
// extMarker.value = [...shapeMarkers];
|
|
// // }
|
|
// }
|
|
|
|
const extCircles = ref([])
|
|
// const extCircles = ref([])
|
|
const extMarker = ref([])
|
|
</script>
|
|
|
|
<template>
|
|
<view :class="s.root">
|
|
<view class="mapBox">
|
|
<map
|
|
id="map"
|
|
:markers="[...markerPoints, ...markers, ...extMarker, ...distanceText]"
|
|
:longitude="center.lng"
|
|
:latitude="center.lat"
|
|
:polygons="polygons"
|
|
:polyline="[...polyline]"
|
|
:scale="20"
|
|
:circles="[...circles, ...extCircles]"
|
|
:enable-rotate="enableRotate"
|
|
:rotate="bearing"
|
|
:enable-satellite="true"
|
|
:show-compass="true"
|
|
/>
|
|
</view>
|
|
<RightSide @create="onHandleCreateCircle" />
|
|
<BottomSide :info="{ ...position, ...attitude, ...gps, ...battery, connectLoading, connectDrone, droneOnLine }"/>
|
|
</view>
|
|
|
|
<nut-toast :msg="state.msg" v-model:visible="state.show" :type="state.type" :cover="state.cover" :duration="2000" />
|
|
</template>
|
|
|
|
<style lang="less" module="s">
|
|
page {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.root {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
:global {
|
|
.mapBox {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
|
|
#map {
|
|
width: 100%;
|
|
height: 100%;
|
|
transform: scale(2);
|
|
}
|
|
|
|
.exam-result-modal {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 1000;
|
|
|
|
.modal-content {
|
|
background-color: rgba(0, 0, 0, 0.8);
|
|
padding: 20px;
|
|
border-radius: 12px;
|
|
min-width: 280px;
|
|
position: relative;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
.close-btn {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
width: 24px;
|
|
height: 24px;
|
|
line-height: 24px;
|
|
text-align: center;
|
|
font-size: 20px;
|
|
color: #999;
|
|
cursor: pointer;
|
|
border-radius: 50%;
|
|
transition: all 0.3s;
|
|
|
|
&:hover {
|
|
background-color: #f5f5f5;
|
|
color: #666;
|
|
}
|
|
}
|
|
|
|
.result-status {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
margin-bottom: 16px;
|
|
color: #ff4d4f;
|
|
|
|
&.pass {
|
|
color: #52c41a;
|
|
}
|
|
}
|
|
|
|
.info-item {
|
|
margin: 8px 0;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.label {
|
|
color: #838383;
|
|
margin-right: 8px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.value {
|
|
color: #ffffff;
|
|
font-weight: 500;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.real-time-data {
|
|
z-index: 1;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|