params | params | ||||
}) | }) | ||||
} | } | ||||
/** | |||||
* 控制无人机 | |||||
* @param { } params | |||||
* @returns | |||||
*/ | |||||
export function controlAir(params) { | |||||
return request({ | |||||
url: `/airport/drone/control`, | |||||
method: 'POST', | |||||
params | |||||
}) | |||||
} | |||||
// 根据应急任务ID查询预警记录 | |||||
export function emergencyRecord(params) { | |||||
return request({ | |||||
url: `/warning/record/one/by/emergencyMissionId`, | |||||
method: 'GET', | |||||
params | |||||
}) | |||||
} | |||||
// 获取无人机数据 | |||||
export function uavInfo(id) { | |||||
return request({ | |||||
url: `/airport/drone/data/${id}`, | |||||
method: 'GET' | |||||
}) | |||||
} |
<span>飞行高度:</span> | <span>飞行高度:</span> | ||||
<n-slider v-model:value="flyHeight" :default-value="100" :format-tooltip="formatHeight" :max="600" @update:value="limitMin" /> | <n-slider v-model:value="flyHeight" :default-value="100" :format-tooltip="formatHeight" :max="600" @update:value="limitMin" /> | ||||
</p> | </p> | ||||
<p class="execute-btn" @click="test">立即执行</p> | |||||
<p class="execute-btn" @click="executeNow">立即执行</p> | |||||
<p class="alarm-title">任务记录</p> | <p class="alarm-title">任务记录</p> | ||||
<div class="task-log"> | <div class="task-log"> | ||||
<ul> | <ul> | ||||
import { reactive, toRefs, watch } from 'vue' | import { reactive, toRefs, watch } from 'vue' | ||||
import { EARLY_SOURCE, TASK_STATUS } from '@/utils/dictionary.js' | import { EARLY_SOURCE, TASK_STATUS } from '@/utils/dictionary.js' | ||||
import { getWarningRecord, getWarningInfo, ignoreWarning, confirmWarning, pointflight } from '@/api/dashboard/index.js' | import { getWarningRecord, getWarningInfo, ignoreWarning, confirmWarning, pointflight } from '@/api/dashboard/index.js' | ||||
import { useInspectionStore } from '@/store/modules/inspection.js' | |||||
// turf 用于简单的空间计算 | // turf 用于简单的空间计算 | ||||
import * as turf from '@turf/turf' | import * as turf from '@turf/turf' | ||||
export default { | export default { | ||||
}, | }, | ||||
emits: ['start'], | emits: ['start'], | ||||
setup(props, { emit }) { | setup(props, { emit }) { | ||||
const inspectionStore = useInspectionStore() | |||||
const data = reactive({ | const data = reactive({ | ||||
airpotOptions: [], | airpotOptions: [], | ||||
airport: {}, | airport: {}, | ||||
data.warningShow = false | data.warningShow = false | ||||
} | } | ||||
const test = async() => { | |||||
const executeNow = async() => { | |||||
var airportName = '' | var airportName = '' | ||||
data.airpotOptions?.map((item) => { | data.airpotOptions?.map((item) => { | ||||
if (item.value === data.airportId) { | if (item.value === data.airportId) { | ||||
if (res.code === 0) { | if (res.code === 0) { | ||||
getRecord(parseInt(data.warningInfo?.id)) | getRecord(parseInt(data.warningInfo?.id)) | ||||
handleExecute() | |||||
handleExecute(res.data?.emergencyMissionId) | |||||
} | } | ||||
} | } | ||||
return value + 'm' | return value + 'm' | ||||
} | } | ||||
// 立即执行 传入应急任务id | |||||
const handleExecute = (id) => { | const handleExecute = (id) => { | ||||
emit('start', id) | emit('start', id) | ||||
} | } | ||||
formatHeight, | formatHeight, | ||||
checkAirport, | checkAirport, | ||||
closeAirport, | closeAirport, | ||||
test, | |||||
executeNow, | |||||
closeWarning, | closeWarning, | ||||
ignore, | ignore, | ||||
confirm, | confirm, |
import { | import { | ||||
airportList, getWarning | airportList, getWarning | ||||
} from '@/api/dashboard/index.js' | } from '@/api/dashboard/index.js' | ||||
import { getQuestions } from '@/api/task/index.js' | |||||
import { getTaskDetail } from '@/api/task/index.js' | |||||
import { gcj02towgs84 } from '@/utils/coordinate-util.js' | import { gcj02towgs84 } from '@/utils/coordinate-util.js' | ||||
import AirInfo from './AirInfo.vue' | import AirInfo from './AirInfo.vue' | ||||
import ProblemInfo from './ProblemInfo.vue' | import ProblemInfo from './ProblemInfo.vue' | ||||
monitorVideo.value?.disposeVideo() | monitorVideo.value?.disposeVideo() | ||||
} | } | ||||
const handleExecute = () => { | |||||
data.drawerShow = true | |||||
const handleExecute = async(value) => { | |||||
const res = await getTaskDetail(value) | |||||
if (res.code === 0) { | |||||
inspectionStore.setList(res.data) | |||||
} | |||||
} | |||||
const refreshRecord = () => { | |||||
} | } | ||||
watch(() => inspectionStore.getList, (val) => { | watch(() => inspectionStore.getList, (val) => { | ||||
hideProblemInfo, | hideProblemInfo, | ||||
getMaxZOverlay, | getMaxZOverlay, | ||||
Warning, | Warning, | ||||
handleExecute | |||||
handleExecute, | |||||
refreshRecord | |||||
} | } | ||||
} | } | ||||
} | } |
import { XYZ, Vector as VectorSource } from 'ol/source' | import { XYZ, Vector as VectorSource } from 'ol/source' | ||||
import { transform, fromLonLat } from 'ol/proj' | import { transform, fromLonLat } from 'ol/proj' | ||||
import { getVectorContext } from 'ol/render' | import { getVectorContext } from 'ol/render' | ||||
import VectorContext from 'ol/render/VectorContext' | |||||
import * as control from 'ol/control' | import * as control from 'ol/control' | ||||
import { styleList } from '@/utils/style.js' | import { styleList } from '@/utils/style.js' | ||||
import { getTrackList } from '@/api/task/index.js' | import { getTrackList } from '@/api/task/index.js' | ||||
const inspectionStore = useInspectionStore() | const inspectionStore = useInspectionStore() | ||||
const data = reactive({ | const data = reactive({ | ||||
map: null, | map: null, | ||||
view: null, | |||||
drawerShow: false, | drawerShow: false, | ||||
trackLayer: null, | trackLayer: null, | ||||
trackInfo: null, | trackInfo: null, | ||||
// 轨迹数据 | // 轨迹数据 | ||||
trackList: [] | |||||
trackList: [], | |||||
socket: null, | |||||
liveTrackLayer: null | |||||
}) | }) | ||||
console.log(inspectionStore.getList) | |||||
const getMapOptions = computed(() => { | const getMapOptions = computed(() => { | ||||
return { | return { | ||||
id: props.id | id: props.id | ||||
} | } | ||||
}) | }) | ||||
// watch(() => props.idd, (value) => { | |||||
// if (value) { | |||||
// console.log('hhh', value) | |||||
// } else { | |||||
// console.log('hhh', value) | |||||
// } | |||||
// }) | |||||
// if (props.ttest) { | |||||
// getTrackData(props.ttest.missionId).then(res => { | |||||
// if (res.trackList.length > 0) { | |||||
// initTrack(formatTradeList(res.trackList), 'route') | |||||
// } | |||||
// }) | |||||
// } | |||||
/** | /** | ||||
* 初始化地图 | * 初始化地图 | ||||
*/ | */ | ||||
params: { LAYERS: 'jiangning:town' } | params: { LAYERS: 'jiangning:town' } | ||||
}) | }) | ||||
}) | }) | ||||
data.view = new View({ | |||||
center: transform([118.773136, 31.828065], 'EPSG:4326', 'EPSG:3857'), | |||||
zoom: 11, | |||||
maxZoom: 17 | |||||
}) | |||||
data.map = new Map({ | data.map = new Map({ | ||||
// 地图容器 | // 地图容器 | ||||
target: props.id, | target: props.id, | ||||
view: new View({ | |||||
center: transform([118.773136, 31.828065], 'EPSG:4326', 'EPSG:3857'), | |||||
zoom: 11, | |||||
maxZoom: 17 | |||||
}), | |||||
view: data.view, | |||||
layers: [tdtImgMap], | layers: [tdtImgMap], | ||||
controls: control.defaults({ | controls: control.defaults({ | ||||
attribution: false, | attribution: false, | ||||
} | } | ||||
/* 获取轨迹数据 */ | /* 获取轨迹数据 */ | ||||
const getTrackData = async function(id) { | |||||
const res = await getTrackList(id) | |||||
const getTrackData = async function() { | |||||
const res = await getTrackList(inspectionStore.getList.id) | |||||
const trackList = res.data | const trackList = res.data | ||||
data.trackList = trackList | data.trackList = trackList | ||||
// const trackList = data.trackList | |||||
return Promise.resolve({ | return Promise.resolve({ | ||||
trackList | trackList | ||||
}) | }) | ||||
} | } | ||||
/* 处理轨迹列表 */ | |||||
const formatTradeList = function(trackList) { | |||||
return trackList.map((item) => | |||||
fromLonLat([parseFloat(item.lng), parseFloat(item.lat)]) | |||||
) | |||||
if (inspectionStore.getList.id) { | |||||
if (!data.map) { | |||||
getTrackData().then(({ trackList }) => { | |||||
setLiveTrack(formatTradeList(trackList)) | |||||
}) | |||||
} | |||||
} | |||||
/* 加载实时轨迹 */ | |||||
const setLiveTrack = function(hisTrackList) { | |||||
data.animating = false | |||||
data.disdance = 0 | |||||
var changeTrack | |||||
if (hisTrackList.length > 1) { | |||||
data.trackInfo = initTrack(hisTrackList, 'liveTrackLayer', 'route') | |||||
if (data.trackInfo.vectorLayer) { | |||||
data.liveTrackLayer.on('change', changeTrack) | |||||
} | |||||
} | |||||
/* 轨迹改变 */ | |||||
changeTrack = (event) => { | |||||
try { | |||||
const vectorContext = getVectorContext(event) | |||||
vectorContext.setStyle(styleList['geoMarker']) | |||||
VectorContext.drawGeometry(data.trackInfo.position) | |||||
vectorContext.drawGeometry(data.trackInfo.route) | |||||
data.map.render() | |||||
} catch { | |||||
console.log(event) | |||||
} | |||||
} | |||||
// 两秒钟获取一次数据 | |||||
data.socket = setInterval(() => { | |||||
getTrackData().then(({ trackList }) => { | |||||
const len = trackList.length | |||||
const obj = trackList[len - 1] | |||||
const coordinate = fromLonLat([obj?.lng, obj?.lat]) | |||||
if (data.trackInfo) { | |||||
data.trackInfo.route.appendCoordinate(coordinate) | |||||
data.trackInfo.vectorLayer.getSource().changed() | |||||
data.trackInfo.position.setCoordinates(coordinate) | |||||
} | |||||
}) | |||||
}, 2000) | |||||
} | } | ||||
/* 初始化轨迹 */ | /* 初始化轨迹 */ | ||||
const initTrack = function(coordinate, routeType) { | |||||
const initTrack = function(coordinate, layer, routeType) { | |||||
let vectorLayer = null | let vectorLayer = null | ||||
// 路线 | |||||
const route = new LineString(coordinate) | const route = new LineString(coordinate) | ||||
const routeFeature = new Feature({ | const routeFeature = new Feature({ | ||||
type: routeType, | type: routeType, | ||||
geometry: route | geometry: route | ||||
}) | }) | ||||
const startMarker = new Feature({ | |||||
const endMarker = new Feature({ | |||||
type: 'icon', | type: 'icon', | ||||
geometry: new Point(route.getFirstCoordinate()) | |||||
geometry: new Point(route.getLastCoordinate()) | |||||
}) | }) | ||||
const position = startMarker.getGeometry().clone() | |||||
const geoMarker = new Feature({ | |||||
type: 'geoMarker', | |||||
geometry: position | |||||
}) | |||||
geoMarker.setProperties({ type: 'geoMarker' }, false) | |||||
if (data.trackLayer) { | |||||
data.trackLayer.setSource( | |||||
new VectorSource({ | |||||
features: [geoMarker, routeFeature] | |||||
}) | |||||
) | |||||
vectorLayer = data.trackLayer | |||||
} else { | |||||
vectorLayer = new VectorLayer({ | |||||
source: new VectorSource({ | |||||
features: [geoMarker, routeFeature] | |||||
}), | |||||
style: function(feature) { | |||||
return styleList[feature.get('type')] | |||||
} | |||||
const position = endMarker.getGeometry().clone() | |||||
if (routeType === 'route') { // 轨迹数据 | |||||
const geoMarker = new Feature({ | |||||
type: 'geoMarker', | |||||
geometry: position | |||||
}) | }) | ||||
geoMarker.setProperties({ type: 'geoMarker' }, false) | |||||
if (data.liveTrackLayer) { | |||||
data.liveTrackLayer.setSource( | |||||
new VectorSource({ | |||||
features: [geoMarker, routeFeature] | |||||
}) | |||||
) | |||||
vectorLayer = data.liveTrackLayer | |||||
} else { | |||||
vectorLayer = new VectorLayer({ | |||||
source: new VectorSource({ | |||||
features: [geoMarker, routeFeature] | |||||
}), | |||||
style: function(feature) { | |||||
return styleList[feature.get('type')] | |||||
} | |||||
}) | |||||
} | |||||
data[layer] = vectorLayer | |||||
data.map.addLayer(data[layer]) | |||||
data.view.fit(route, { padding: [50, 50, 50, 50] }) | |||||
return { | |||||
vectorLayer, | |||||
geoMarker, | |||||
route, | |||||
position | |||||
} | |||||
} | } | ||||
data.mapData.addLayer(vectorLayer) | |||||
data.mapView.fit(route, { padding: [50, 50, 50, 50] }) | |||||
vectorLayer.on('postrender', moveFeature.bind()) | |||||
function moveFeature(event) { | |||||
/* 计算飞行的百分比 */ | |||||
const percent = data.videoInfo.currentTime / data.videoInfo.duration | |||||
const currentCoordinate = route.getCoordinateAt(percent) | |||||
position.setCoordinates(currentCoordinate) | |||||
geoMarker.setGeometry(null) | |||||
const vectorContext = getVectorContext(event) | |||||
vectorContext.setStyle(styleList['geoMarker']) | |||||
vectorContext.drawGeometry(position) | |||||
data.mapData?.render() | |||||
} | |||||
} | |||||
/* 处理轨迹列表 */ | |||||
const formatTradeList = function(trackList) { | |||||
return trackList.map((item) => | |||||
fromLonLat([parseFloat(item.lng), parseFloat(item.lat)]) | |||||
) | |||||
} | |||||
// 结束直播飞行 | |||||
const endLiveFly = () => { | |||||
clearInterval(data.socket) | |||||
data.socket = null | |||||
} | } | ||||
onMounted(() => { | onMounted(() => { | ||||
}) | }) | ||||
onBeforeUnmount(() => { | onBeforeUnmount(() => { | ||||
clearInterval(data.socket) | |||||
data.socket = null | |||||
}) | }) | ||||
return { | return { | ||||
...toRefs(data), | ...toRefs(data), | ||||
getMapOptions | |||||
getMapOptions, | |||||
endLiveFly, | |||||
initTrack | |||||
} | } | ||||
} | } | ||||
} | } |
<template> | <template> | ||||
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse"> | <n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse"> | ||||
<n-drawer-content closable title="报告详情"> | |||||
<n-drawer-content closable title="机场调度"> | |||||
<div ref="containerRef" class="warning__container"> | <div ref="containerRef" class="warning__container"> | ||||
<div class="warning__side"> | <div class="warning__side"> | ||||
</div> | </div> | ||||
<div class="control__panel"> | <div class="control__panel"> | ||||
<SpeedChart :data="chartData" /> | |||||
<ControlPanel v-if="showControl" mode="camera" @start="startOrder" @reset="endOrder" /> | |||||
<ControlPanel v-if="showControl" mode="locus" @start="startOrder" @reset="endOrder" /> | |||||
<!-- <SpeedChart :data="chartData" /> --> | |||||
<ControlPanel v-if="showControl" mode="camera" @instruct="sendControl" /> | |||||
<ControlPanel v-if="showControl" mode="locus" @instruct="sendControl" /> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div ref="mapRef" class="warn__back"> | <div ref="mapRef" class="warn__back"> | ||||
<Underlay /> | |||||
<Underlay ref="baseMap" /> | |||||
</div> | </div> | ||||
<div ref="videoRef" class="inner"> | <div ref="videoRef" class="inner"> | ||||
111111111111 | |||||
<VideoPlayer | |||||
id="emergency-video" | |||||
ref="emergencyVideo" | |||||
style="position: absolute;left: 0;top: 0" | |||||
/> | |||||
</div> | </div> | ||||
</n-drawer-content> | </n-drawer-content> | ||||
</n-drawer> | </n-drawer> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import { defineComponent, ref, reactive, toRefs, computed, watch, nextTick } from 'vue' | |||||
import { defineComponent, ref, reactive, toRefs, computed, watch, nextTick, onBeforeUnmount } from 'vue' | |||||
import Underlay from './Underlay.vue' | import Underlay from './Underlay.vue' | ||||
import ControlPanel from './ControlPanel.vue' | import ControlPanel from './ControlPanel.vue' | ||||
import SpeedChart from './SpeedChart.vue' | import SpeedChart from './SpeedChart.vue' | ||||
import { useInspectionStore } from '@/store/modules/inspection.js' | import { useInspectionStore } from '@/store/modules/inspection.js' | ||||
import VideoPlayer from '@/components/VideoPlayer/index.vue' | |||||
import { controlAir, emergencyRecord, uavInfo } from '@/api/task/index.js' | |||||
export default defineComponent({ | export default defineComponent({ | ||||
name: 'WarningDrawer', | name: 'WarningDrawer', | ||||
components: { Underlay, ControlPanel, SpeedChart }, | |||||
components: { Underlay, ControlPanel, SpeedChart, VideoPlayer }, | |||||
props: { | props: { | ||||
/* 可见 */ | /* 可见 */ | ||||
visible: { | visible: { | ||||
data: { | data: { | ||||
type: Object, | type: Object, | ||||
default: () => {} | default: () => {} | ||||
}, | |||||
id: { | |||||
type: Number, | |||||
default: 0 | |||||
} | } | ||||
}, | }, | ||||
emits: { | emits: { | ||||
'update:visible': null | |||||
'update:visible': null, | |||||
'refresh:id': null | |||||
}, | }, | ||||
setup(props, { emit }) { | setup(props, { emit }) { | ||||
const inspectionStore = useInspectionStore() | const inspectionStore = useInspectionStore() | ||||
const containerRef = ref() | const containerRef = ref() | ||||
const sideRef = ref() | const sideRef = ref() | ||||
const mapRef = ref() | const mapRef = ref() | ||||
const baseMap = ref() | |||||
const videoRef = ref() | const videoRef = ref() | ||||
const emergencyVideo = ref() | |||||
const data = reactive({ | const data = reactive({ | ||||
hasChanged: false, | hasChanged: false, | ||||
showControl: false, | showControl: false, | ||||
/* 关闭抽屉 */ | /* 关闭抽屉 */ | ||||
function handleDrawerColse() { | function handleDrawerColse() { | ||||
emit('update:visible', false) | emit('update:visible', false) | ||||
inspectionStore.resetList() | |||||
getEmergencyRecord(inspectionStore.getList.id) | |||||
// inspectionStore.resetList() | |||||
// 结束直播飞行 | |||||
// baseMap.value?.endLiveFly() | |||||
// 关闭视频播放 | |||||
emergencyVideo.value?.disposeVideo() | |||||
// emit('refresh:id') | |||||
} | |||||
const getEmergencyRecord = async(id) => { | |||||
const res = await emergencyRecord({ | |||||
emergencyMissionId: id | |||||
}) | |||||
if (res.code === 0) { | |||||
console.log(res.data) | |||||
} | |||||
} | } | ||||
const handleChange = () => { | const handleChange = () => { | ||||
} | } | ||||
} | } | ||||
const startOrder = (params) => { | |||||
console.log(params) | |||||
} | |||||
const endOrder = (params) => { | |||||
const sendControl = (params) => { | |||||
console.log(params) | console.log(params) | ||||
} | } | ||||
} | } | ||||
}) | }) | ||||
watch(() => inspectionStore.getList, (value) => { | |||||
if (value?.aiplayUrl) { | |||||
setTimeout(() => { | |||||
initOriginPlayer(value) | |||||
}, 3000) | |||||
} | |||||
}) | |||||
/* 初始化播放器 */ | |||||
function initOriginPlayer(value) { | |||||
const origin = { | |||||
width: '100%', | |||||
height: '100%', | |||||
source: value.aiplayUrl, | |||||
isLive: true | |||||
} | |||||
emergencyVideo.value?.init(origin) | |||||
} | |||||
const handleOperate = () => { | const handleOperate = () => { | ||||
data.operate = data.operate === '悬停' ? '继续飞行' : '悬停' | data.operate = data.operate === '悬停' ? '继续飞行' : '悬停' | ||||
} | } | ||||
showIcon: false, | showIcon: false, | ||||
content: `是否返航`, | content: `是否返航`, | ||||
confirm: () => { | confirm: () => { | ||||
emit('update:visible', false) | |||||
airBack() | |||||
} | } | ||||
} | } | ||||
) | ) | ||||
} | } | ||||
/** | |||||
* 飞机返仓 zhilin03 | |||||
*/ | |||||
const airBack = async() => { | |||||
const res = await controlAir({ | |||||
value: { | |||||
'zhilin': '03', | |||||
'taskId': inspectionStore.getList?.id, | |||||
'airportId': inspectionStore.getList?.airportId, | |||||
'msg': '飞行任务' | |||||
} | |||||
}) | |||||
if (res.code === 0) { | |||||
console.log('返航成功') | |||||
} | |||||
} | |||||
onBeforeUnmount(() => { | |||||
emergencyVideo.value?.disposeVideo() | |||||
}) | |||||
return { | return { | ||||
containerRef, | containerRef, | ||||
sideRef, | sideRef, | ||||
handleControl, | handleControl, | ||||
handleBack, | handleBack, | ||||
fanhang, | fanhang, | ||||
startOrder, | |||||
endOrder | |||||
sendControl, | |||||
baseMap, | |||||
emergencyVideo | |||||
} | } | ||||
} | } | ||||
}) | }) | ||||
position: absolute; | position: absolute; | ||||
width: calc(100% - 40px); | width: calc(100% - 40px); | ||||
height: calc(100% - 20px); | height: calc(100% - 20px); | ||||
background: blue; | |||||
} | } | ||||
.container__back{ | .container__back{ |