@@ -47,3 +47,15 @@ export function airportTrack(id) { | |||
}) | |||
} | |||
/** | |||
* @description: 获取飞行轨迹 | |||
* @param {*} id 机场id | |||
* @return {*} | |||
*/ | |||
export function missionLive(id) { | |||
return request({ | |||
url: `/mission/live/${id}`, | |||
method: 'GET' | |||
}) | |||
} | |||
@@ -1,5 +1,5 @@ | |||
<template> | |||
<div v-if="isFailed"> | |||
<div v-if="isFailed && useEmpty"> | |||
<slot name="empty" /> | |||
</div> | |||
<div v-else :id="getPlayerId" /> | |||
@@ -14,6 +14,10 @@ export default defineComponent({ | |||
options: { | |||
type: Object, | |||
default: () => {} | |||
}, | |||
useEmpty: { | |||
type: Boolean, | |||
default: false | |||
} | |||
}, | |||
emits: ['timeUpdate', 'video-status'], | |||
@@ -41,8 +45,26 @@ export default defineComponent({ | |||
duration | |||
}) | |||
}, | |||
pause(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
const duration = player.getDuration() | |||
emit('video-status', { | |||
status: 'pause', | |||
currentTime, | |||
duration | |||
}) | |||
}, | |||
play(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
const duration = player.getDuration() | |||
emit('video-status', { | |||
status: 'play', | |||
currentTime, | |||
duration | |||
}) | |||
}, | |||
error(player, e) { | |||
disposeVideo() | |||
// disposeVideo() | |||
isFailed.value = true | |||
} | |||
}) |
@@ -17,7 +17,7 @@ | |||
</div> | |||
<div class="card__video"> | |||
<div class="video__item"> | |||
<VideoPlayer :options="getVideoOptions.inner"> | |||
<VideoPlayer :options="getVideoOptions.inner" :use-empty="true"> | |||
<template #empty> | |||
<div class="video__item--empty"> | |||
<img src="@/assets/images/lose-control.png"> | |||
@@ -111,7 +111,7 @@ export default { | |||
height: '100%', | |||
// source: row?.externalMonitorUrl, | |||
source: 'http://101.43.84.72:8080/live/34020000001320000001@34020000001320000001.flv', | |||
// source: row?.id === 2 ? live1 : video2, | |||
// source: 'https://live.play.t-aaron.com/live/THSAl_hd.m3u8', | |||
isLive: true | |||
} | |||
// outer: { |
@@ -13,7 +13,7 @@ | |||
<n-select v-model:value="videoForm.airportId" :options="airOptions" @update:value="handleAirportChange" /> | |||
</n-form-item> | |||
<n-form-item label="回放选择:" path="taskId"> | |||
<n-select v-model:value="videoForm.taskId" :options="taskOptions" @update:value="handleVideoChange" /> | |||
<n-select v-model:value="videoForm.taskId" :options="taskOptions" clearable @update:value="handleVideoChange" /> | |||
</n-form-item> | |||
</n-form> | |||
</p> | |||
@@ -39,8 +39,9 @@ | |||
import { dataToSelect } from '@/utils/handleData.js' | |||
import { airportList } from '@/api/dashboard/index.js' | |||
import { getTaskList } from '@/api/task/index.js' | |||
import { missionLive } from '@/api/dashboard/index.js' | |||
import VideoPlayer from '@/components/VideoPlayer/index.vue' | |||
import { reactive, computed, toRefs } from 'vue' | |||
import { reactive, computed, watch, toRefs, nextTick } from 'vue' | |||
export default { | |||
name: 'TaskCard', | |||
@@ -48,13 +49,22 @@ export default { | |||
setup() { | |||
const data = reactive({ | |||
videoForm: { | |||
airportId: 2, | |||
taskId: '' | |||
airportId: null, | |||
taskId: null | |||
}, | |||
airOptionsAll: [], | |||
airOptions: [], | |||
taskOptions: [], | |||
hasLive: false | |||
hasLive: false, | |||
airportIdBack: null, | |||
airportUrl: { | |||
origin: null, | |||
analyse: null | |||
}, | |||
liveUrl: { | |||
origin: null, | |||
analyse: null | |||
} | |||
}) | |||
/** | |||
@@ -65,12 +75,9 @@ export default { | |||
const loadAirport = (async function() { | |||
const res = await airportList({ page: 1, limit: 60 }) | |||
if (res.code === 0) { | |||
data.airOptionsAll = res.data | |||
data.airOptions = dataToSelect(res.data, { label: 'name', value: 'id' }) | |||
data.videoForm.airportId = res.data[0]?.id | |||
if (res.data[0]?.id) { | |||
loadTaskOption(res.data[0].id) | |||
} | |||
handleAirportChange(data.videoForm.airportId) | |||
} | |||
})() | |||
@@ -82,28 +89,54 @@ export default { | |||
} | |||
function handleAirportChange(value) { | |||
if (value === data.airportIdBack) return | |||
data.airportIdBack = value | |||
missionLive(value) | |||
.then(res => { | |||
if (res.code === 0) { | |||
data.airportUrl = { | |||
origin: res.data?.playUrl, | |||
analyse: res.data?.aiplayUrl | |||
} | |||
data.liveUrl = { ...data.airportUrl } | |||
} | |||
}) | |||
loadTaskOption(value) | |||
} | |||
function handleVideoChange(value) { | |||
// 1 | |||
if (!value) { | |||
data.liveUrl = { ...data.airportUrl } | |||
} else { | |||
data.liveUrl = { | |||
origin: 'http://101.43.84.72:8080/live/34020000001320000001@34020000001320000001.flv', | |||
analyse: 'http://101.43.84.72:8080/live/34020000001320000001@34020000001320000001.flv' | |||
} | |||
} | |||
} | |||
watch(() => data.liveUrl, | |||
(value) => { | |||
nextTick(() => { | |||
data.hasLive = (value.origin && value.analyse) || false | |||
}) | |||
}) | |||
const getVideoOptions = computed(() => { | |||
const row = data.airOptionsAll.find((item) => { return item.id === data.videoForm.airportId }) | |||
const { origin, analyse } = data.liveUrl | |||
return { | |||
origin: { | |||
id: 'video-origin', | |||
width: '100%', | |||
height: '100%', | |||
source: row?.internalMonitorUrl, | |||
source: origin, | |||
isLive: true | |||
}, | |||
analyse: { | |||
id: 'video-analyse', | |||
width: '100%', | |||
height: '100%', | |||
source: row?.externalMonitorUrl, | |||
source: analyse, | |||
isLive: true | |||
} | |||
} |
@@ -31,9 +31,8 @@ | |||
class="video_content" | |||
:class="videoShow == 'back' ? 'video_show' : 'video_hidden'" | |||
> | |||
<VideoPlayer :ref="videoRef" :options="getVideoOptions" @time-update="handleOriginTime" @video-status="handleOriginStatus" /> | |||
<VideoPlayer :ref="aiVideoRef" :options="getVideoAiOptions" @time-update="handleAiTime" @video-status="handleAiStatus" /> | |||
<VideoPlayer ref="videoRef" :options="getVideoOptions" @time-update="handleOriginTime" @video-status="handleOriginStatus" /> | |||
<VideoPlayer ref="aiVideoRef" :options="getVideoAiOptions" @time-update="handleAiTime" @video-status="handleAiStatus" /> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -42,7 +41,7 @@ | |||
</template> | |||
<script> | |||
import { defineComponent, computed, reactive, toRefs, onMounted, ref, watch } from 'vue' | |||
import { defineComponent, computed, reactive, toRefs, ref, watch } from 'vue' | |||
import { Map, View, Feature } from 'ol' | |||
import 'ol/ol.css' | |||
import { Tile, Vector as VectorLayer } from 'ol/layer' | |||
@@ -51,9 +50,6 @@ import Point from 'ol/geom/Point' | |||
import { XYZ, Vector as VectorSource } from 'ol/source' | |||
import { transform, fromLonLat } from 'ol/proj' | |||
import { getVectorContext } from 'ol/render' | |||
import VectorContext from 'ol/render/VectorContext' | |||
import { Draw } from 'ol/interaction' | |||
import { toRaw } from '@vue/reactivity' | |||
import * as control from 'ol/control' | |||
import { styleList } from '../tools/style.js' | |||
@@ -81,7 +77,7 @@ export default defineComponent({ | |||
const data = reactive({ | |||
show: props.visible, | |||
mapData: null, | |||
view: null, | |||
mapView: null, | |||
trackLayer: null, | |||
trackInfo: null, | |||
// 轨迹数据 | |||
@@ -112,7 +108,8 @@ export default defineComponent({ | |||
videoStatus: { | |||
status: 'init', | |||
aiStatus: 'init' | |||
} | |||
}, | |||
canSkip: true | |||
}) | |||
/* 获取抽屉的信息 */ | |||
@@ -147,13 +144,33 @@ export default defineComponent({ | |||
const aiVideoRef = ref(null) | |||
const handleOriginStatus = function(params) { | |||
data.videoStatus.status = params.status | |||
switch (params.status) { | |||
case 'pause': | |||
aiVideoRef.value?.pauseVideo() | |||
break | |||
case 'play': | |||
aiVideoRef.value.playVideo() | |||
break | |||
case 'ready': | |||
data.videoStatus.status = params.status | |||
break | |||
} | |||
data.videoInfo = { | |||
...params | |||
} | |||
} | |||
const handleAiStatus = function(params) { | |||
data.videoStatus.aiStatus = params.status | |||
switch (params.status) { | |||
case 'pause': | |||
videoRef.value?.pauseVideo() | |||
break | |||
case 'play': | |||
videoRef.value.playVideo() | |||
break | |||
case 'ready': | |||
data.videoStatus.aiStatus = params.status | |||
break | |||
} | |||
data.aiVideoInfo = { | |||
...params | |||
} | |||
@@ -163,8 +180,12 @@ export default defineComponent({ | |||
...params | |||
} | |||
const len = Math.abs(params.currentTime - data.aiVideoInfo.currentTime) | |||
if (params.status === 'skip' || len > 3) { | |||
aiVideoRef.value.seekTime(params.currentTime) | |||
if ((params.status === 'skip' || len > 3) && data.canSkip) { | |||
aiVideoRef.value.seekTime(params.currentTime + 0.1) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
}, 200) | |||
} | |||
} | |||
const handleAiTime = function(params) { | |||
@@ -172,8 +193,12 @@ export default defineComponent({ | |||
...params | |||
} | |||
const len = Math.abs(params.currentTime - data.videoInfo.currentTime) | |||
if (params.status === 'skip' || len > 3) { | |||
videoRef.value.seekTime(params.currentTime) | |||
if ((params.status === 'skip' || len > 3) && data.canSkip) { | |||
videoRef.value.seekTime(params.currentTime + 0.1) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
}, 200) | |||
} | |||
} | |||
/* 播放视频 */ | |||
@@ -182,18 +207,16 @@ export default defineComponent({ | |||
aiVideoRef.value.playVideo() | |||
} | |||
watch(() => data.videoStatus, (value) => { | |||
if (value.status === 'ready' && value.aiStatus === 'ready') { | |||
console.log(value, '===================') | |||
watch([() => props.visible, () => data.videoStatus], ([visible, status]) => { | |||
if (visible) { | |||
// initTrack(formatTradeList(data.trackList), 'route') | |||
} | |||
if (status.status === 'ready' && status.aiStatus === 'ready') { | |||
startAllVideo() | |||
initMap() | |||
initTrack(formatTradeList(data.trackList), 'route') | |||
} | |||
}) | |||
onMounted(() => { | |||
initMap() | |||
// 加载航线 | |||
initTrack(formatTradeList(data.trackList), 'route') | |||
}) | |||
}, { deep: true }) | |||
/* 初始化地图 */ | |||
const initMap = function() { | |||
@@ -206,7 +229,7 @@ export default defineComponent({ | |||
}) | |||
] | |||
data.view = new View({ | |||
data.mapView = new View({ | |||
maxZoom: 18, | |||
zoom: 5, | |||
center: transform([118.837886, 32.057175], 'EPSG:4326', 'EPSG:3857') | |||
@@ -214,7 +237,7 @@ export default defineComponent({ | |||
data.mapData = new Map({ | |||
layers: layers, | |||
target: 'history-map', | |||
view: data.view, | |||
view: data.mapView, | |||
controls: control.defaults({ | |||
attribution: false, | |||
rotate: false, | |||
@@ -267,7 +290,7 @@ export default defineComponent({ | |||
}) | |||
} | |||
data.mapData.addLayer(vectorLayer) | |||
data.view.fit(route, { padding: [50, 50, 50, 50] }) | |||
data.mapView.fit(route, { padding: [50, 50, 50, 50] }) | |||
vectorLayer.on('postrender', moveFeature.bind()) | |||
function moveFeature(event) { |
@@ -18,7 +18,7 @@ | |||
</template> | |||
<template #toolbar> | |||
<n-button @click="handleReported"> | |||
{{ reportStatus ? '修改并生成报告': '提交并生成报告' }} | |||
{{ isReported ? '修改并生成报告': '提交并生成报告' }} | |||
</n-button> | |||
</template> | |||
</DataTable> | |||
@@ -37,7 +37,7 @@ import HeadSearch from '@/components/Search/index.vue' | |||
import DataTable from '@/components/DataTable/index.vue' | |||
import ConfirmModal from './components/ConfirmModal.vue' | |||
import PositionDrawer from './components/PositionDrawer.vue' | |||
import { getQuestionList, generateReport } from '@/api/task/index.js' | |||
import { getQuestionList, generateReport, questionAnalyze } from '@/api/task/index.js' | |||
import { unref, reactive, toRefs, provide } from 'vue' | |||
export default { | |||
name: 'QuestionPage', | |||
@@ -133,7 +133,12 @@ export default { | |||
if (res.code === 0) { | |||
const { confirm, notreviewed } = res.data | |||
if (notreviewed === 0) { | |||
generateReport(props.data.id) | |||
await generateReport(props.data.id) | |||
.then(res => { | |||
if (res.code === 0) { | |||
data.isReported = true | |||
} | |||
}) | |||
} else { | |||
$dialog.confirm( | |||
{ | |||
@@ -141,7 +146,14 @@ export default { | |||
title: '提示', | |||
showIcon: false, | |||
content: `已核实${confirm}条,未核实${notreviewed}条,是否提交并生成报告?`, | |||
confirm: generateReport(props.data.id) | |||
confirm: () => { | |||
generateReport(props.data.id) | |||
.then(res => { | |||
if (res.code === 0) { | |||
data.isReported = true | |||
} | |||
}) | |||
} | |||
} | |||
) | |||
} |