@@ -7,14 +7,16 @@ | |||
<script> | |||
import { defineComponent, ref, computed, watch, onMounted, nextTick } from 'vue' | |||
import { defineComponent, reactive, ref, computed, toRefs, nextTick } from 'vue' | |||
export default defineComponent({ | |||
name: 'VideoPlayer', | |||
props: { | |||
options: { | |||
type: Object, | |||
default: () => {} | |||
/* 播放器id */ | |||
id: { | |||
type: String, | |||
default: () => 'palyer' | |||
}, | |||
/* 是有填充空白 */ | |||
useEmpty: { | |||
type: Boolean, | |||
default: false | |||
@@ -22,120 +24,115 @@ export default defineComponent({ | |||
}, | |||
emits: ['timeUpdate', 'video-status'], | |||
setup(props, { emit }) { | |||
const videoPlayer = ref(null) | |||
const hasInit = ref(false) | |||
const isSeek = ref(false) | |||
const isFailed = ref(false) | |||
const videoPlayer = Symbol.for(props.id) | |||
const data = reactive({ | |||
[videoPlayer]: null, | |||
seekTime: null, | |||
canSeek: true, | |||
isSeek: false, | |||
isFailed: true | |||
}) | |||
/* 获取播放器的id */ | |||
const getPlayerId = computed(() => { | |||
return props.id | |||
}) | |||
/* 播放器组件 */ | |||
const toolComponent = Aliplayer.Component({ | |||
/* 时间更新事件 */ | |||
timeupdate(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
const duration = player.getDuration() | |||
emit('timeUpdate', { | |||
status: isSeek.value || Number.isInteger(currentTime) ? 'skip' : 'palying', | |||
currentTime, | |||
duration | |||
}) | |||
data.seekTime = player.getCurrentTime() | |||
emit('video-status', data.isSeek ? 'skip' : 'playing') | |||
}, | |||
ready(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
const duration = player.getDuration() | |||
emit('video-status', { | |||
status: 'ready', | |||
currentTime, | |||
duration | |||
}) | |||
emit('video-status', 'ready') | |||
}, | |||
pause(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
const duration = player.getDuration() | |||
emit('video-status', { | |||
status: 'pause', | |||
currentTime, | |||
duration | |||
}) | |||
emit('video-status', 'pause') | |||
}, | |||
play(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
const duration = player.getDuration() | |||
emit('video-status', { | |||
status: 'play', | |||
currentTime, | |||
duration | |||
}) | |||
emit('video-status', 'play') | |||
}, | |||
ended(player, e) { | |||
emit('video-status', 'ended') | |||
}, | |||
error(player, e) { | |||
// disposeVideo() | |||
isFailed.value = true | |||
data.isFailed = true | |||
emit('video-status', 'error') | |||
} | |||
}) | |||
const getPlayerId = computed(() => { | |||
return props.options?.id || 'player' | |||
}) | |||
function init() { | |||
const player = new Aliplayer({ | |||
id: 'player', | |||
width: '500px', | |||
height: '260px', | |||
autoplay: true, | |||
...props.options, | |||
components: [ | |||
toolComponent | |||
] | |||
}, function(player) { | |||
if (player.getOptions().autoplay) { | |||
player.mute() | |||
} | |||
}) | |||
videoPlayer.value = player | |||
player.on('startSeek', () => { | |||
isSeek.value = true | |||
}) | |||
player.on('completeSeek', () => { | |||
isSeek.value = false | |||
/* 初始化播放器 */ | |||
function init(options) { | |||
data.isFailed = false | |||
nextTick(() => { | |||
/* 实例化ali播放器 */ | |||
const player = new Aliplayer( | |||
{ | |||
id: props.id, | |||
width: '500px', | |||
height: '260px', | |||
autoplay: false, | |||
...options, | |||
components: [toolComponent] | |||
}, | |||
function(player) { player.mute() } | |||
) | |||
/* 监听开始拖拽事件 */ | |||
player.on('startSeek', ({ paramData }) => { | |||
/* 仅变更标识 */ | |||
data.isSeek = true | |||
}) | |||
/* 监听完成拖拽事件 */ | |||
player.on('completeSeek', ({ paramData }) => { | |||
/* 仅变更标识 */ | |||
data.isSeek = false | |||
data.seekTime = paramData | |||
/* 是否通知跳转 */ | |||
if (data.canSeek) { | |||
emit('video-status', 'skip') | |||
} | |||
}) | |||
data[videoPlayer] = player | |||
}) | |||
hasInit.value = true | |||
} | |||
/* 获取当前播放器的时间 */ | |||
function getTime() { | |||
return videoPlayer.value.getCurrentTime() | |||
const currentTime = data[videoPlayer]?.getCurrentTime() || 0 | |||
const duration = data[videoPlayer]?.getDuration() || 0 | |||
const seekTime = data.seekTime | |||
return { | |||
currentTime, | |||
duration, | |||
seekTime | |||
} | |||
} | |||
/* 设定播放器播放时间 */ | |||
function seekTime(time) { | |||
videoPlayer.value.seek(time) | |||
data.canSeek = false | |||
data[videoPlayer]?.seek(time) | |||
setTimeout(() => { | |||
data.canSeek = true | |||
}, 100) | |||
} | |||
/* 设定播放器开始 */ | |||
function playVideo() { | |||
videoPlayer.value.play() | |||
data[videoPlayer]?.play() | |||
} | |||
/* 设定播放器暂停 */ | |||
function pauseVideo() { | |||
videoPlayer.value.pause() | |||
data[videoPlayer]?.pause() | |||
} | |||
/* 销毁播放器 */ | |||
function disposeVideo() { | |||
hasInit.value = false | |||
videoPlayer.value.dispose() | |||
data[videoPlayer]?.dispose() | |||
} | |||
watch( | |||
() => props.options, | |||
(val) => { | |||
isFailed.value = false | |||
if (props.options.source && !hasInit.value) { | |||
nextTick(() => { | |||
init() | |||
}) | |||
} | |||
if (hasInit.value) { | |||
disposeVideo() | |||
nextTick(() => { | |||
init() | |||
}) | |||
} | |||
} | |||
) | |||
onMounted(() => { | |||
if (props.options.source && !hasInit.value) { | |||
init() | |||
} | |||
}) | |||
return { | |||
isFailed, | |||
...toRefs(data), | |||
getPlayerId, | |||
init, | |||
getTime, | |||
seekTime, | |||
playVideo, | |||
@@ -153,7 +150,10 @@ export default defineComponent({ | |||
.prism-player .prism-ErrorMessage .prism-error-operation a.prism-button.prism-button-refresh{ | |||
display: none; | |||
} | |||
.prism-player .prism-ErrorMessage .prism-detect-info.prism-center{ | |||
.prism-player .prism-ErrorMessage .prism-error-operation a.prism-button.prism-button-orange{ | |||
display: none; | |||
} | |||
/* .prism-player .prism-ErrorMessage .prism-detect-info.prism-center{ | |||
display: none; | |||
} */ | |||
</style> |
@@ -16,10 +16,10 @@ export const TASK_TYPE = [ | |||
] | |||
export const QUESTION_TYPE = [ | |||
{ label: '林场问题图斑', value: 1 }, | |||
{ label: '病虫树', value: 2 }, | |||
{ label: '人员活动', value: 3 }, | |||
{ label: '火灾隐患', value: 4 } | |||
{ label: '林场问题图斑', value: 'LC001' }, | |||
{ label: '病虫树', value: 'LC002' }, | |||
{ label: '人员活动', value: 'LC003' }, | |||
{ label: '火灾隐患', value: 'LC004' } | |||
] | |||
export const QUESTION_STATUS = [ |
@@ -17,7 +17,7 @@ | |||
</div> | |||
<div class="card__video"> | |||
<div class="video__item"> | |||
<VideoPlayer :options="getVideoOptions.inner" :use-empty="true"> | |||
<VideoPlayer id="video-inner" ref="videoRef" :use-empty="true"> | |||
<template #empty> | |||
<div class="video__item--empty"> | |||
<img src="@/assets/images/lose-control.png"> | |||
@@ -32,7 +32,6 @@ | |||
<li v-for="(item,index) in weatherList" :key="index"> {{ item.label }}: {{ item.value }} </li> | |||
</ul> | |||
</div> | |||
<!-- <VideoPlayer :options="getVideoOptions.outer" /> --> | |||
<BaseMap ref="mapRef" :coordinate="coordinate" /> | |||
</div> | |||
</div> | |||
@@ -41,16 +40,17 @@ | |||
<script> | |||
import { dataToSelect } from '@/utils/handleData.js' | |||
import { airportList, airportWeather, airportTrack } from '@/api/dashboard/index.js' | |||
import { airportList, airportWeather } from '@/api/dashboard/index.js' | |||
import VideoPlayer from '@/components/VideoPlayer/index.vue' | |||
import BaseMap from '@/components/BaseMap/BaseMap.vue' | |||
import { ref, reactive, computed, toRefs } from 'vue' | |||
import { ref, reactive, toRefs, nextTick } from 'vue' | |||
export default { | |||
name: 'TaskCard', | |||
components: { VideoPlayer, BaseMap }, | |||
setup() { | |||
const mapRef = ref() | |||
const videoRef = ref() | |||
const data = reactive({ | |||
videoForm: { | |||
airportId: null | |||
@@ -90,6 +90,10 @@ export default { | |||
{ label: '风度', value: parm.wspd + 'm/s' }, | |||
{ label: '风向', value: parm.wdir } | |||
] : [] | |||
videoRef.value.disposeVideo() | |||
nextTick(() => { | |||
initPlayer() | |||
}) | |||
} | |||
} | |||
@@ -102,34 +106,23 @@ export default { | |||
} | |||
} | |||
const getVideoOptions = computed(() => { | |||
function initPlayer() { | |||
const row = data.airOptionsAll.find((item) => { return item.id === data.videoForm.airportId }) | |||
return { | |||
inner: { | |||
id: 'video-inner', | |||
width: '100%', | |||
height: '100%', | |||
source: row?.externalMonitorUrl, | |||
// source: 'http://101.43.84.72:8080/live/34020000001320000001@34020000001320000001.flv', | |||
// source: 'https://live.play.t-aaron.com/live/THSAl_hd.m3u8', | |||
isLive: true | |||
} | |||
// outer: { | |||
// id: 'video-outer', | |||
// width: '100%', | |||
// height: '100%', | |||
// source: row?.externalMonitorUrl, | |||
// isLive: true | |||
// } | |||
const options = { | |||
width: '100%', | |||
height: '100%', | |||
source: row?.externalMonitorUrl, | |||
isLive: true | |||
} | |||
}) | |||
videoRef.value?.init(options) | |||
} | |||
return { | |||
...toRefs(data), | |||
mapRef, | |||
videoRef, | |||
loadAirport, | |||
getAirportInfo, | |||
getVideoOptions | |||
getAirportInfo | |||
} | |||
} | |||
} |
@@ -26,10 +26,10 @@ | |||
</div> | |||
<div v-else class="card__video"> | |||
<div class="video__item"> | |||
<VideoPlayer :options="getVideoOptions.origin" /> | |||
<VideoPlayer id="dashboard-video-origin" ref="originRef" @video-status="handleOriginStatus" /> | |||
</div> | |||
<div class="video__item"> | |||
<VideoPlayer :options="getVideoOptions.analyse" /> | |||
<VideoPlayer id="dashboard-video-analyse" ref="analyseRef" @video-status="handleAnalyseStatus" /> | |||
</div> | |||
</div> | |||
</n-card> | |||
@@ -41,12 +41,14 @@ 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, watch, toRefs, nextTick } from 'vue' | |||
import { ref, reactive, computed, watch, toRefs, nextTick } from 'vue' | |||
export default { | |||
name: 'TaskCard', | |||
components: { VideoPlayer }, | |||
setup() { | |||
const originRef = ref() | |||
const analyseRef = ref() | |||
const data = reactive({ | |||
videoForm: { | |||
airportId: null, | |||
@@ -63,7 +65,23 @@ export default { | |||
liveUrl: { | |||
origin: null, | |||
analyse: null | |||
} | |||
}, | |||
videoInfo: { | |||
origin: { | |||
currentTime: 0, | |||
duration: 100, | |||
status: 'init' | |||
}, | |||
analyse: { | |||
currentTime: 0, | |||
duration: 100, | |||
status: 'init' | |||
} | |||
}, | |||
canSkip: true, | |||
originDebounce: null, | |||
analyseDebounce: null | |||
}) | |||
@@ -122,35 +140,186 @@ export default { | |||
(value) => { | |||
nextTick(() => { | |||
data.hasPlayer = (value.origin && value.analyse) || false | |||
if (data.hasPlayer) { | |||
originRef.value?.disposeVideo() | |||
analyseRef.value?.disposeVideo() | |||
nextTick(() => { | |||
initOriginPlayer() | |||
initAnalysePlayer() | |||
}) | |||
} | |||
}) | |||
}) | |||
const getVideoOptions = computed(() => { | |||
const { origin, analyse, isLive } = data.liveUrl | |||
return { | |||
origin: { | |||
id: 'video-origin', | |||
width: '100%', | |||
height: '100%', | |||
source: origin, | |||
isLive: isLive | |||
}, | |||
analyse: { | |||
id: 'video-analyse', | |||
width: '100%', | |||
height: '100%', | |||
source: analyse, | |||
isLive: isLive | |||
/* 初始化播放器 */ | |||
function initOriginPlayer() { | |||
data.videoInfo.origin.status = 'init' | |||
const origin = { | |||
width: '100%', | |||
height: '100%', | |||
source: data.liveUrl.origin, | |||
isLive: data.liveUrl.isLive | |||
} | |||
originRef.value?.init(origin) | |||
setTimeout(() => { | |||
if (data.videoInfo.origin.status === 'init') { | |||
originRef.value?.disposeVideo() | |||
initOriginPlayer() | |||
} | |||
}, 30000) | |||
} | |||
function initAnalysePlayer() { | |||
data.videoInfo.analyse.status = 'init' | |||
const analyse = { | |||
width: '100%', | |||
height: '100%', | |||
source: data.liveUrl.analyse, | |||
isLive: data.liveUrl.isLive | |||
} | |||
}) | |||
analyseRef.value?.init(analyse) | |||
setTimeout(() => { | |||
if (data.videoInfo.analyse.status === 'init') { | |||
analyseRef.value?.disposeVideo() | |||
initAnalysePlayer() | |||
} | |||
}, 30000) | |||
} | |||
/* 监 听原视频 状态 */ | |||
const handleOriginStatus = function(status) { | |||
clearTimeout(data.originDebounce) | |||
if (!(['ready', 'pause', 'ended'].includes(status))) { | |||
data.originDebounce = setTimeout(() => { | |||
originRef.value?.disposeVideo() | |||
initOriginPlayer() | |||
}, 30000) | |||
} | |||
if (!analyseRef.value) return | |||
if (analyseRef.value.isFailed) return | |||
const analyseTime = analyseRef.value?.getTime().currentTime || 0 | |||
const { currentTime, duration, seekTime } = originRef.value?.getTime() | |||
const len = Math.abs(analyseTime - currentTime) | |||
data.videoInfo.origin = { | |||
currentTime: currentTime, | |||
duration: duration, | |||
status: status | |||
} | |||
switch (status) { | |||
case 'ready': | |||
break | |||
case 'play': | |||
if (!data.liveUrl.isLive) { | |||
analyseRef.value?.playVideo() | |||
} | |||
break | |||
case 'pause': | |||
if (!data.liveUrl.isLive) { | |||
analyseRef.value?.pauseVideo() | |||
} | |||
break | |||
case 'skip': | |||
if (data.canSkip && !data.liveUrl.isLive) { | |||
analyseRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
case 'playing': | |||
/* 播放中时延迟超过三秒则跳转 */ | |||
if (len > 3 && data.canSkip && !data.liveUrl.isLive) { | |||
analyseRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
} | |||
} | |||
/* 监听分析视频状态 */ | |||
const handleAnalyseStatus = function(status) { | |||
clearTimeout(data.analyseDebounce) | |||
if (!(['ready', 'pause', 'ended'].includes(status))) { | |||
data.analyseDebounce = setTimeout(() => { | |||
analyseRef.value?.disposeVideo() | |||
initAnalysePlayer() | |||
}, 30000) | |||
} | |||
if (!originRef.value) return | |||
if (originRef.value.isFailed) return | |||
const originTime = originRef.value?.getTime().currentTime || 0 | |||
const { currentTime, duration, seekTime } = analyseRef.value?.getTime() | |||
const len = Math.abs(originTime - currentTime) | |||
data.videoInfo.analyse = { | |||
currentTime: currentTime, | |||
duration: duration, | |||
status: status | |||
} | |||
switch (status) { | |||
case 'ready': | |||
break | |||
case 'play': | |||
if (!data.liveUrl.isLive) { | |||
originRef.value?.playVideo() | |||
} | |||
break | |||
case 'pause': | |||
if (!data.liveUrl.isLive) { | |||
originRef.value?.pauseVideo() | |||
} | |||
break | |||
case 'skip': | |||
if (data.canSkip && !data.liveUrl.isLive) { | |||
originRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
case 'playing': | |||
/* 播放中时延迟超过三秒则跳转 */ | |||
if (len > 3 && data.canSkip && !data.liveUrl.isLive) { | |||
originRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
} | |||
} | |||
watch(() => [data.videoInfo.origin.status, data.videoInfo.analyse.status], | |||
([origin, analyse]) => { | |||
if (!data.liveUrl.isLive) { | |||
if (origin === 'ready' && analyse === 'ready') { | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
} | |||
} | |||
}, { deep: true }) | |||
return { | |||
...toRefs(data), | |||
originRef, | |||
analyseRef, | |||
loadAirport, | |||
handleAirportChange, | |||
handleVideoChange, | |||
getVideoOptions | |||
handleOriginStatus, | |||
handleAnalyseStatus | |||
} | |||
} | |||
} |
@@ -31,8 +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 id="demand-video-origin" ref="originRef" @video-status="handleOriginStatus" /> | |||
<VideoPlayer id="demand-video-analyse" ref="analyseRef" @video-status="handleAnalyseStatus" /> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -75,6 +75,9 @@ export default defineComponent({ | |||
'update:visible': null | |||
}, | |||
setup(props, { emit }) { | |||
const originRef = ref(null) | |||
const analyseRef = ref(null) | |||
const data = reactive({ | |||
show: props.visible, | |||
mapData: null, | |||
@@ -102,18 +105,20 @@ export default defineComponent({ | |||
}, | |||
videoShow: 'back', // 视频展示开关 | |||
videoInfo: { | |||
currentTime: 0, | |||
duration: 100 | |||
}, | |||
aiVideoInfo: { | |||
currentTime: 0, | |||
duration: 100 | |||
}, | |||
videoStatus: { | |||
status: 'init', | |||
aiStatus: 'init' | |||
origin: { | |||
currentTime: 0, | |||
duration: 100, | |||
status: 'init' | |||
}, | |||
analyse: { | |||
currentTime: 0, | |||
duration: 100, | |||
status: 'init' | |||
} | |||
}, | |||
canSkip: true | |||
canSkip: true, | |||
originDebounce: null, | |||
analyseDebounce: null | |||
}) | |||
/* 获取抽屉的信息 */ | |||
@@ -124,92 +129,139 @@ export default defineComponent({ | |||
placement: 'right' | |||
} | |||
}) | |||
/* 获取原视频地址 */ | |||
const getVideoOptions = computed(() => { | |||
return { | |||
id: 'palyer-on', | |||
function initOriginPlayer(islive) { | |||
data.videoInfo.origin.status = 'init' | |||
const origin = { | |||
width: '400px', | |||
height: '324px', | |||
autoplay: false, | |||
source: props.data.videoUrl | |||
source: props.data.videoUrl, | |||
autoplay: false | |||
} | |||
}) | |||
/* 获取AI视频地址 */ | |||
const getVideoAiOptions = computed(() => { | |||
return { | |||
id: 'palyer-ai-on', | |||
originRef.value?.init(origin) | |||
setTimeout(() => { | |||
if (data.videoInfo.origin.status === 'init') { | |||
originRef.value?.disposeVideo() | |||
initOriginPlayer() | |||
} | |||
}, 30000) | |||
} | |||
function initAnalysePlayer() { | |||
data.videoInfo.analyse.status = 'init' | |||
const analyse = { | |||
width: '400px', | |||
height: '324px', | |||
autoplay: false, | |||
source: props.data.aiVideoUrl | |||
source: props.data.aiVideoUrl, | |||
autoplay: false | |||
} | |||
}) | |||
const videoRef = ref(null) | |||
const aiVideoRef = ref(null) | |||
analyseRef.value?.init(analyse) | |||
setTimeout(() => { | |||
if (data.videoInfo.analyse.status === 'init') { | |||
analyseRef.value?.disposeVideo() | |||
initAnalysePlayer() | |||
} | |||
}, 30000) | |||
} | |||
const handleOriginStatus = function(params) { | |||
switch (params.status) { | |||
case 'pause': | |||
aiVideoRef.value?.pauseVideo() | |||
break | |||
case 'play': | |||
aiVideoRef.value.playVideo() | |||
break | |||
case 'ready': | |||
data.videoStatus.status = params.status | |||
break | |||
const handleOriginStatus = function(status) { | |||
clearTimeout(data.originDebounce) | |||
if (!(['ready', 'pause', 'ended'].includes(status))) { | |||
data.originDebounce = setTimeout(() => { | |||
originRef.value?.disposeVideo() | |||
initOriginPlayer() | |||
}, 30000) | |||
} | |||
data.videoInfo = { | |||
...params | |||
if (analyseRef.value.isFailed) return | |||
const analyseTime = analyseRef.value?.getTime().currentTime || 0 | |||
const { currentTime, duration, seekTime } = originRef.value?.getTime() | |||
const len = Math.abs(analyseTime - currentTime) | |||
data.videoInfo.origin = { | |||
currentTime: currentTime, | |||
duration: duration, | |||
status: status | |||
} | |||
} | |||
const handleAiStatus = function(params) { | |||
switch (params.status) { | |||
switch (status) { | |||
case 'play': | |||
analyseRef.value?.playVideo() | |||
break | |||
case 'pause': | |||
videoRef.value?.pauseVideo() | |||
analyseRef.value?.pauseVideo() | |||
break | |||
case 'play': | |||
videoRef.value.playVideo() | |||
case 'skip': | |||
if (data.canSkip) { | |||
analyseRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
case 'ready': | |||
data.videoStatus.aiStatus = params.status | |||
case 'playing': | |||
/* 播放中时延迟超过三秒则跳转 */ | |||
if (len > 3 && data.canSkip) { | |||
analyseRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
} | |||
data.aiVideoInfo = { | |||
...params | |||
} | |||
} | |||
const handleOriginTime = function(params) { | |||
data.videoInfo = { | |||
...params | |||
/* 监听分析视频状态 */ | |||
const handleAnalyseStatus = function(status) { | |||
clearTimeout(data.analyseDebounce) | |||
if (!(['ready', 'pause', 'ended'].includes(status))) { | |||
data.analyseDebounce = setTimeout(() => { | |||
analyseRef.value?.disposeVideo() | |||
initAnalysePlayer() | |||
}, 30000) | |||
} | |||
const len = Math.abs(params.currentTime - data.aiVideoInfo.currentTime) | |||
if ((params.status === 'skip' || len > 3) && data.canSkip) { | |||
aiVideoRef.value?.seekTime(params.currentTime + 0.1) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
}, 200) | |||
if (originRef.value.isFailed) return | |||
const originTime = originRef.value?.getTime().currentTime || 0 | |||
const { currentTime, duration, seekTime } = analyseRef.value?.getTime() | |||
const len = Math.abs(originTime - currentTime) | |||
data.videoInfo.analyse = { | |||
currentTime: currentTime, | |||
duration: duration, | |||
status: status | |||
} | |||
} | |||
const handleAiTime = function(params) { | |||
data.aiVideoInfo = { | |||
...params | |||
} | |||
const len = Math.abs(params.currentTime - data.videoInfo.currentTime) | |||
if ((params.status === 'skip' || len > 3) && data.canSkip) { | |||
videoRef.value?.seekTime(params.currentTime + 0.1) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
}, 200) | |||
switch (status) { | |||
case 'play': | |||
originRef.value?.playVideo() | |||
break | |||
case 'pause': | |||
originRef.value?.pauseVideo() | |||
break | |||
case 'skip': | |||
if (data.canSkip) { | |||
originRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
case 'playing': | |||
/* 播放中时延迟超过三秒则跳转 */ | |||
if (len > 3 && data.canSkip) { | |||
originRef.value?.seekTime(seekTime) | |||
data.canSkip = false | |||
setTimeout(() => { | |||
data.canSkip = true | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
}, 200) | |||
} | |||
break | |||
} | |||
} | |||
/* 播放视频 */ | |||
const startAllVideo = function() { | |||
videoRef.value.playVideo() | |||
aiVideoRef.value.playVideo() | |||
} | |||
/* 获取轨迹数据 */ | |||
const getTrackData = async function() { | |||
@@ -222,27 +274,40 @@ export default defineComponent({ | |||
}) | |||
} | |||
watch(() => data.videoStatus, (status) => { | |||
if (status.status === 'ready' && status.aiStatus === 'ready') { | |||
startAllVideo() | |||
} | |||
}, { deep: true }) | |||
watch(() => props.visible, (value) => { | |||
if (value) { | |||
if (document.getElementById('history-map')) { | |||
initMap() | |||
} else { | |||
setTimeout(() => { | |||
watch(() => props.visible, (visible) => { | |||
if (visible) { | |||
nextTick(() => { | |||
if (data.videoInfo.origin.status === 'init') { | |||
initOriginPlayer() | |||
} | |||
if (data.videoInfo.analyse.status === 'init') { | |||
initAnalysePlayer() | |||
} | |||
if (!data.mapData) { | |||
initMap() | |||
getTrackData().then(res => { | |||
if (res.trackList.length > 0) { | |||
initTrack(formatTradeList(res.trackList), 'route') | |||
} | |||
}) | |||
}, 500) | |||
} | |||
} | |||
}) | |||
} else { | |||
originRef.value?.disposeVideo() | |||
analyseRef.value?.disposeVideo() | |||
data.videoInfo.origin.status = 'init' | |||
data.videoInfo.analyse.status = 'init' | |||
data.mapData = null | |||
} | |||
}) | |||
}, { deep: true }) | |||
watch(() => [data.videoInfo.origin.status, data.videoInfo.analyse.status], | |||
([origin, analyse]) => { | |||
if (origin === 'ready' && analyse === 'ready') { | |||
originRef.value?.playVideo() | |||
analyseRef.value?.playVideo() | |||
} | |||
}, { deep: true }) | |||
/* 初始化地图 */ | |||
const initMap = function() { | |||
@@ -342,15 +407,11 @@ export default defineComponent({ | |||
return { | |||
...toRefs(data), | |||
videoRef, | |||
aiVideoRef, | |||
originRef, | |||
analyseRef, | |||
getDrawerOptions, | |||
handleOriginStatus, | |||
handleAiStatus, | |||
handleOriginTime, | |||
handleAiTime, | |||
getVideoOptions, | |||
getVideoAiOptions, | |||
handleAnalyseStatus, | |||
handleDrawerColse, | |||
showVideo | |||
} | |||
@@ -374,7 +435,7 @@ export default defineComponent({ | |||
} | |||
/* 视频 */ | |||
.videobox { | |||
width: 800px; | |||
width: 810px; | |||
display: flex; | |||
flex-direction: column; | |||
justify-content: flex-start; | |||
@@ -384,7 +445,7 @@ export default defineComponent({ | |||
right: 10px; | |||
} | |||
.flagbox { | |||
width: 800px; | |||
width: 810px; | |||
height: 27px; | |||
background: #fff; | |||
padding: 0 10px; | |||
@@ -393,7 +454,7 @@ export default defineComponent({ | |||
align-items: center; | |||
} | |||
.flagbox_open { | |||
width: 800px; | |||
width: 810px; | |||
animation: openbox 1s; | |||
} | |||
.flaxbox_back { | |||
@@ -402,7 +463,7 @@ export default defineComponent({ | |||
} | |||
@keyframes backbox { | |||
from { | |||
width: 800px; | |||
width: 810px; | |||
} | |||
to { | |||
width: 116px; | |||
@@ -413,12 +474,13 @@ export default defineComponent({ | |||
width: 116px; | |||
} | |||
to { | |||
width: 800px; | |||
width: 810px; | |||
} | |||
} | |||
.video_content { | |||
width: 800px; | |||
width: 810px; | |||
display: flex; | |||
justify-content: space-between; | |||
} | |||
.video_show { | |||
opacity: 1; |
@@ -32,8 +32,8 @@ | |||
class="video_content" | |||
:class="videoShow == 'back' ? 'video_show' : 'video_hidden'" | |||
> | |||
<VideoPlayer :options="getVideoOptions.origin" /> | |||
<VideoPlayer :options="getVideoOptions.analyse" /> | |||
<VideoPlayer id="live-origin" ref="originRef" /> | |||
<VideoPlayer id="live-analyse" ref="analyseRef" /> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -42,7 +42,7 @@ | |||
</template> | |||
<script> | |||
import { defineComponent, computed, reactive, toRefs, watch } from 'vue' | |||
import { defineComponent, computed, ref, reactive, toRefs, watch, nextTick } from 'vue' | |||
import { Map, View, Feature } from 'ol' | |||
import 'ol/ol.css' | |||
import { Tile, Vector as VectorLayer } from 'ol/layer' | |||
@@ -77,6 +77,8 @@ export default defineComponent({ | |||
'update:visible': null | |||
}, | |||
setup(props, { emit }) { | |||
const originRef = ref() | |||
const analyseRef = ref() | |||
const data = reactive({ | |||
mapData: null, | |||
view: null, | |||
@@ -112,8 +114,16 @@ export default defineComponent({ | |||
disdance: null, | |||
animating: null, | |||
detailData: {}, | |||
videoShow: 'back' // 视频展示开关 | |||
videoShow: 'back', // 视频展示开关 | |||
videoInfo: { | |||
origin: { | |||
status: 'init' | |||
}, | |||
analyse: { | |||
status: 'init' | |||
} | |||
} | |||
}) | |||
/* 获取抽屉的信息 */ | |||
const getDrawerOptions = computed(() => { | |||
@@ -123,25 +133,40 @@ export default defineComponent({ | |||
placement: 'right' | |||
} | |||
}) | |||
/* 获取原视频地址 */ | |||
const getVideoOptions = computed(() => { | |||
return { | |||
origin: { | |||
id: 'video-live', | |||
width: '800px', | |||
height: '212px', | |||
source: props.data.playUrl, | |||
isLive: true | |||
}, | |||
analyse: { | |||
id: 'video-live-analyse', | |||
width: '800px', | |||
height: '212px', | |||
source: props.data.aiplayUrl, | |||
isLive: true | |||
/* 初始化播放器 */ | |||
function initOriginPlayer(islive) { | |||
data.videoInfo.origin.status = 'init' | |||
const origin = { | |||
width: '400px', | |||
height: '324px', | |||
source: props.data.playUrl, | |||
isLive: true | |||
} | |||
originRef.value?.init(origin) | |||
setTimeout(() => { | |||
if (data.videoInfo.origin.status === 'init') { | |||
originRef.value?.disposeVideo() | |||
initOriginPlayer() | |||
} | |||
}, 30000) | |||
} | |||
function initAnalysePlayer() { | |||
data.videoInfo.analyse.status = 'init' | |||
const analyse = { | |||
width: '400px', | |||
height: '324px', | |||
source: props.data.aiplayUrl, | |||
isLive: true | |||
} | |||
}) | |||
analyseRef.value?.init(analyse) | |||
setTimeout(() => { | |||
if (data.videoInfo.analyse.status === 'init') { | |||
analyseRef.value?.disposeVideo() | |||
initAnalysePlayer() | |||
} | |||
}, 30000) | |||
} | |||
function handleDrawerColse() { | |||
emit('update:visible', false) | |||
@@ -169,8 +194,14 @@ export default defineComponent({ | |||
watch(() => props.visible, (value) => { | |||
if (value) { | |||
if (!data.mapData) { | |||
if (document.getElementById('live-map')) { | |||
nextTick(() => { | |||
if (data.videoInfo.origin.status === 'init') { | |||
initOriginPlayer() | |||
} | |||
if (data.videoInfo.analyse.status === 'init') { | |||
initAnalysePlayer() | |||
} | |||
if (!data.mapData) { | |||
initMap() | |||
// 加载机场航线 | |||
getLineData().then(({ trackList }) => { | |||
@@ -180,29 +211,16 @@ export default defineComponent({ | |||
}) | |||
// 加载历史轨迹和实时轨迹 | |||
getTrackData().then(({ trackList }) => { | |||
if (trackList.length > 0) { | |||
setLiveTrack(formatTradeList(trackList)) | |||
} | |||
setLiveTrack(formatTradeList(trackList)) | |||
}) | |||
} else { | |||
setTimeout(() => { | |||
initMap() | |||
// 加载机场航线 | |||
getLineData().then(({ trackList }) => { | |||
if (trackList.length > 0) { | |||
initTrack(formatTradeList(trackList), 'lineTrackLayer', 'lineRoute') | |||
} | |||
}) | |||
// 加载历史轨迹和实时轨迹 | |||
getTrackData().then(({ trackList }) => { | |||
if (trackList.length > 0) { | |||
setLiveTrack(formatTradeList(trackList)) | |||
} | |||
}) | |||
}, 500) | |||
} | |||
} | |||
}) | |||
} else { | |||
originRef.value?.disposeVideo() | |||
analyseRef.value?.disposeVideo() | |||
data.videoInfo.origin.status = 'init' | |||
data.videoInfo.analyse.status = 'init' | |||
data.mapData = null | |||
clearInterval(data.socket) | |||
data.socket = null | |||
} | |||
@@ -333,7 +351,7 @@ export default defineComponent({ | |||
data.socket = setInterval(() => { | |||
getTrackData().then(({ trackList }) => { | |||
const obj = trackList[0] | |||
const coordinate = fromLonLat([obj.lng, obj.lat]) | |||
const coordinate = fromLonLat([obj?.lng, obj?.lat]) | |||
if (data.trackInfo) { | |||
data.trackInfo.route.appendCoordinate(coordinate) | |||
data.trackInfo.vectorLayer.getSource().changed() | |||
@@ -380,9 +398,10 @@ export default defineComponent({ | |||
return { | |||
...toRefs(data), | |||
originRef, | |||
analyseRef, | |||
getDrawerOptions, | |||
handleDrawerColse, | |||
getVideoOptions, | |||
showVideo | |||
} | |||
} | |||
@@ -405,7 +424,7 @@ export default defineComponent({ | |||
} | |||
/* 视频 */ | |||
.videobox { | |||
width: 800px; | |||
width: 810px; | |||
display: flex; | |||
flex-direction: column; | |||
justify-content: flex-start; | |||
@@ -415,7 +434,7 @@ export default defineComponent({ | |||
right: 10px; | |||
} | |||
.flagbox { | |||
width: 800px; | |||
width: 810px; | |||
height: 27px; | |||
background: #fff; | |||
padding: 0 10px; | |||
@@ -424,7 +443,7 @@ export default defineComponent({ | |||
align-items: center; | |||
} | |||
.flagbox_open { | |||
width: 800px; | |||
width: 810px; | |||
animation: openbox 1s; | |||
} | |||
.flaxbox_back { | |||
@@ -433,7 +452,7 @@ export default defineComponent({ | |||
} | |||
@keyframes backbox { | |||
from { | |||
width: 800px; | |||
width: 810px; | |||
} | |||
to { | |||
width: 116px; | |||
@@ -444,14 +463,15 @@ export default defineComponent({ | |||
width: 116px; | |||
} | |||
to { | |||
width: 800px; | |||
width: 810px; | |||
} | |||
} | |||
.video_content { | |||
width: 800px; | |||
width: 810px; | |||
height: 220px; | |||
position: relative; | |||
display: flex; | |||
justify-content: space-between; | |||
} | |||
.video_show { |
@@ -55,9 +55,21 @@ export default defineComponent({ | |||
}, | |||
setup(props, { emit }) { | |||
getAirOptions() | |||
const formItem = ref([]) | |||
if (props?.data?.id) { | |||
getLineOptions(props.data.droneId) | |||
if (props.data.status !== 1) { | |||
formItem.value = [ | |||
{ type: 'input', key: 'droneName', label: '无人机' }, | |||
{ type: 'date', key: 'flyTime', label: '飞行时间', props: { type: 'datetime', valueFormat: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss' }} | |||
] | |||
} else { | |||
formItem.value = [] | |||
} | |||
} else { | |||
formItem.value = [] | |||
} | |||
const MODAL_TYPE = { | |||
'create': '新建任务', | |||
'preview': '任务详情', | |||
@@ -88,7 +100,7 @@ export default defineComponent({ | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
...form.formItem.concat(formItem.value) | |||
} | |||
}) | |||