first commit
This commit is contained in:
115
src/components/Map/window/flow.vue
Normal file
115
src/components/Map/window/flow.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<DialogComponent title="气象设备" width="395" :draggable="true" :modal="false" @close="$emit('close')">
|
||||
<div class="content-wrapper">
|
||||
|
||||
<ul class="list-wrapper">
|
||||
<li class="list-item" v-for="(item, index) in items" :key="index">
|
||||
<span class="label">{{ item.label }}:</span>
|
||||
<span class="value">{{ model[item.prop] }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</DialogComponent>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue'
|
||||
import DialogComponent from '@/components/Dialog/index.vue'
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: '深度观测站编号',
|
||||
prop: 'prop1'
|
||||
},
|
||||
{
|
||||
label: '雷达水位',
|
||||
prop: 'prop2'
|
||||
},
|
||||
{
|
||||
label: '压力式水位',
|
||||
prop: 'prop3'
|
||||
},
|
||||
{
|
||||
label: '温度',
|
||||
prop: 'prop4'
|
||||
},
|
||||
{
|
||||
label: '倾角',
|
||||
prop: 'prop5'
|
||||
},
|
||||
{
|
||||
label: '当前时间',
|
||||
prop: 'prop6'
|
||||
}
|
||||
]
|
||||
|
||||
const model = reactive({
|
||||
prop1: 'XD111',
|
||||
prop2: '30m',
|
||||
prop3: '30m',
|
||||
prop4: '30℃',
|
||||
prop5: '30°',
|
||||
prop6: '2025-09-17 10:43:05'
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
// height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.list-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 18px;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
width: 120px;
|
||||
font-size: 14px;
|
||||
color: #00C0FFFF;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #FFFFFFFF;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
background-color: #00C0FF33;
|
||||
color: #00C0FFFF;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-image: url('@/assets/images/common/icon-triangle.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
56
src/components/Map/window/index.vue
Normal file
56
src/components/Map/window/index.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<DialogComponent @mousedown="drag" :style="{ resize: 'both', overflow: 'auto' }" v-if="visible && option[type]" :title="option[type].title" :width="type==='_UAV' ? 1800:395" :draggable="true" :modal="false" @close="close">
|
||||
<component :is="option[type].component" ref="component"/>
|
||||
</DialogComponent>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import DialogComponent from '@/components/Dialog/screen.vue'
|
||||
import MonitorComponent from './monitor.vue'
|
||||
import UAVComponent from './uav.vue'
|
||||
import MeteorologyComponent from './meteorology.vue'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { dragEvent } from '@/utils/common'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
|
||||
const visible = computed(() => mapStore.windowInfo.visible)
|
||||
const type = computed(() => mapStore.windowInfo.type)
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
|
||||
const option = computed(() => ({
|
||||
_monitor: {
|
||||
title: '监控设备',
|
||||
component: MonitorComponent
|
||||
},
|
||||
_UAV: {
|
||||
title: '无人机设备',
|
||||
component: UAVComponent
|
||||
},
|
||||
_environmental: {
|
||||
title: data.value?.videoType || '环境监测设备',
|
||||
component: MeteorologyComponent
|
||||
}
|
||||
}))
|
||||
const component = ref(null)
|
||||
const close = () => {
|
||||
mapStore.updateWindowInfo(false)
|
||||
}
|
||||
/**
|
||||
* 拖拽事件
|
||||
* @param e
|
||||
*
|
||||
*/
|
||||
const drag = (e) => {
|
||||
dragEvent(e, 'el-dialog-title', () => {
|
||||
if(component.value.HikCCTV) {
|
||||
component.value.HikCCTV.initResize(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
218
src/components/Map/window/meteorology.vue
Normal file
218
src/components/Map/window/meteorology.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="content-wrapper">
|
||||
|
||||
<ul class="list-wrapper">
|
||||
<li class="list-item" v-for="(item, index) in items" :key="index">
|
||||
<span class="label">{{ item.label }}:</span>
|
||||
<span class="value">{{ model[item.prop] }}{{ item.unit }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
// import { getWaterwayVelocity, getWaterwayWeather } from '@/api/identification.js'
|
||||
import { dayjs } from 'element-plus'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
|
||||
const velocityItems = [
|
||||
{
|
||||
label: '深度观测站编号',
|
||||
prop: 'deepSiteCode'
|
||||
},
|
||||
{
|
||||
label: '雷达流速',
|
||||
prop: 'radarVelocity',
|
||||
unit: 'km/s'
|
||||
},
|
||||
{
|
||||
label: '雷达水位',
|
||||
prop: 'radarLevel',
|
||||
unit: 'm'
|
||||
},
|
||||
{
|
||||
label: '压力式水位',
|
||||
prop: 'pressureLevel',
|
||||
unit: 'm'
|
||||
},
|
||||
{
|
||||
label: '温度',
|
||||
prop: 'temperature',
|
||||
unit: '℃'
|
||||
},
|
||||
{
|
||||
label: '倾角',
|
||||
prop: 'angle',
|
||||
unit: '°'
|
||||
},
|
||||
{
|
||||
label: '当前时间',
|
||||
prop: 'createTime'
|
||||
}
|
||||
]
|
||||
const velocityModel = {
|
||||
deepSiteCode: 0,
|
||||
radarVelocity: 0,
|
||||
radarLevel: 0,
|
||||
pressureLevel: 0,
|
||||
temperature: 0,
|
||||
angle: 0,
|
||||
createTime: null
|
||||
}
|
||||
const weatherItems = [
|
||||
{
|
||||
label: '深度观测站编号',
|
||||
prop: 'deepSiteCode'
|
||||
},
|
||||
{
|
||||
label: '温度',
|
||||
prop: 'temperature',
|
||||
unit: '℃'
|
||||
},
|
||||
{
|
||||
label: '湿度',
|
||||
prop: 'humidity',
|
||||
unit: '%'
|
||||
},
|
||||
{
|
||||
label: '风速',
|
||||
prop: 'windSpeed',
|
||||
unit: 'm/s'
|
||||
},
|
||||
{
|
||||
label: '风向',
|
||||
prop: 'windDirect',
|
||||
unit: ''
|
||||
},
|
||||
{
|
||||
label: '大气压',
|
||||
prop: 'pressure',
|
||||
unit: 'hPa'
|
||||
},
|
||||
{
|
||||
label: '降水量',
|
||||
prop: 'rainFall',
|
||||
unit: 'mm'
|
||||
},
|
||||
{
|
||||
label: '光照度',
|
||||
prop: 'illuminance',
|
||||
unit: 'Lux'
|
||||
},
|
||||
{
|
||||
label: '当前时间',
|
||||
prop: 'createTime'
|
||||
}
|
||||
]
|
||||
const weatherModel = {
|
||||
deepSiteCode: 0,
|
||||
temperature: 0,
|
||||
humidity: 0,
|
||||
windSpeed: 0,
|
||||
windDirect: 0,
|
||||
pressure: 0,
|
||||
rainFall: 0,
|
||||
illuminance: 0,
|
||||
createTime: null
|
||||
}
|
||||
const items = ref([])
|
||||
|
||||
const model = reactive({})
|
||||
const init = () => {
|
||||
if(data.value.videoType === '航道流速设备') {
|
||||
items.value = velocityItems
|
||||
const params = {
|
||||
waterwayVelocity: data.value.id
|
||||
}
|
||||
// getWaterwayVelocity(params).then(res => {
|
||||
// Object.keys(velocityModel).forEach(key => {
|
||||
// if(key === 'createTime') {
|
||||
// model[key] = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
// }else{
|
||||
// model[key] = res.result[key]
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
}else if(data.value.videoType === '气象观测站设备') {
|
||||
items.value = weatherItems
|
||||
const params = {
|
||||
waterwayVelocity: data.value.id
|
||||
}
|
||||
// getWaterwayWeather(params).then(res => {
|
||||
// Object.keys(weatherModel).forEach(key => {
|
||||
// if(key === 'createTime') {
|
||||
// model[key] = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
// }else{
|
||||
// model[key] = res.result[key]
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
}
|
||||
}
|
||||
watch(() => data.value, () => {
|
||||
init()
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
// height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.list-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 18px;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
width: 120px;
|
||||
font-size: 14px;
|
||||
color: #00C0FFFF;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #FFFFFFFF;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
background-color: #00C0FF33;
|
||||
color: #00C0FFFF;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-image: url('@/assets/images/common/icon-triangle.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
175
src/components/Map/window/monitor.vue
Normal file
175
src/components/Map/window/monitor.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div class="content-wrapper">
|
||||
<div class="monitor-cctv video-windowcctv">
|
||||
<HikPlayerComponent ref="HikCCTV" v-if="visible" :cameraIndexCode="data.videoCode" id="cctv"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import HikPlayerComponent from '@/components/Player/HikPlayer.vue'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const visible = ref(false)
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
const HikCCTV = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
visible.value = true
|
||||
})
|
||||
})
|
||||
defineExpose({
|
||||
HikCCTV
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.list-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 18px;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
width: 100px;
|
||||
font-size: 14px;
|
||||
color: #00C0FFFF;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #FFFFFFFF;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.subtitle {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
background-color: #00C0FF33;
|
||||
color: #00C0FFFF;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-image: url('@/assets/images/common/icon-triangle.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
.title{
|
||||
position: relative;
|
||||
.button{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -32px;
|
||||
cursor: pointer;
|
||||
color: #00c0ff;
|
||||
padding: 6px 10px;
|
||||
background: rgba(22, 119, 255, 0.21);
|
||||
border-radius: 2px 2px 2px 2px;
|
||||
border: 1px solid #236ACE;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
margin-top: 10px;
|
||||
overflow: auto;
|
||||
.header{
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: linear-gradient( 0deg, rgba(10,167,255,0) 0%, rgba(22,115,255,0.5) 100%);
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
display: flex;
|
||||
|
||||
.header-label{
|
||||
font-size: 12px;
|
||||
color: #F4F9FECC;
|
||||
flex-shrink:0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
overflow: auto;
|
||||
.header{
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: linear-gradient( 0deg, rgba(10,167,255,0) 0%, rgba(22,115,255,0.5) 100%);
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
display: flex;
|
||||
|
||||
.header-label{
|
||||
font-size: 12px;
|
||||
color: #F4F9FECC;
|
||||
flex-shrink:0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.item{
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
|
||||
.row{
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
flex-shrink:0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&:nth-of-type(odd){
|
||||
background: #2a64bb2b;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
background-image: url('@/assets/images/common/row-background-active.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
.row.index::before{
|
||||
background: linear-gradient( 89deg, #00F0FF 0%, rgba(4,114,217,0) 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.monitor-cctv{
|
||||
height: 289px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>=>
|
||||
322
src/components/Map/window/trawler.vue
Normal file
322
src/components/Map/window/trawler.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<!-- 渔船信息 弹窗 -->
|
||||
<DialogComponent v-if="visible && info" :title="data.boatName" width="450" :draggable="true" :modal="false" :btn="true" @handle="handleTrack" @close="closeInfo">
|
||||
<div class="content-wrapper">
|
||||
|
||||
<ul class="list-wrapper">
|
||||
<li class="list-item" v-for="item in columns" :key="item.prop">
|
||||
<span class="label">{{item.label}}:</span>
|
||||
<span class="value">{{data[item.prop]}}{{ item.unit }}</span>
|
||||
</li>
|
||||
<div class="subtitle">监控列表:</div>
|
||||
|
||||
<div class="row" v-for="item in monitors" :key="item.videoName">
|
||||
<div class="left">
|
||||
<img src="@/assets/images/map/devices/icon-monitor.png" alt="">
|
||||
<span class="name" :title="item.videoName">{{ item.videoName }}</span>
|
||||
<span class="distance">({{item.distance}}km)</span>
|
||||
</div>
|
||||
<div class="button" @click="handleFollow(item)">
|
||||
<img src="@/assets/images/map/devices/icon-view.png" alt="">
|
||||
光电联动
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</DialogComponent>
|
||||
<!-- 光电随动视频 -->
|
||||
<DialogComponent class="monitor-follow-dialog" v-if="visible && monitor.visible" :title="monitor.data.videoName" width="450" :draggable="true" :modal="false" @close="closeMonitor" @mousedown="drag">
|
||||
<div class="monitor-follow video-windowfollow">
|
||||
<HikPlayerComponent ref="HikFollow" v-if="monitor.data&&monitor.data.url" :cameraIndexCode="monitor.data.url" id="follow"/>
|
||||
</div>
|
||||
</DialogComponent>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue'
|
||||
import DialogComponent from '@/components/Dialog/screen.vue'
|
||||
import HikPlayerComponent from '@/components/Player/HikPlayer.vue'
|
||||
import { findAISPointPositionByMmsi, getDevicesForServo, getDevicesIsCover, sitMoveByGps, servoByRadar, exitServoByRadar } from '@/api/map'
|
||||
import { dragEvent } from '@/utils/common'
|
||||
import GlobalMap from '@/components/map/js/GlobalMap'
|
||||
import ShipPathInMap from '@/components/map/js/ShipPathInMap'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const visible = computed(() => mapStore.windowInfo.visible)
|
||||
const type = computed(() => mapStore.windowInfo.type)
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
const info = ref(true)
|
||||
const monitor = reactive({
|
||||
visible: true,
|
||||
data: {}
|
||||
})
|
||||
const columns = [
|
||||
{
|
||||
label: 'AIS编号',
|
||||
prop: 'terminalCode'
|
||||
},
|
||||
{
|
||||
label: '船牌号',
|
||||
prop: 'boatName'
|
||||
},
|
||||
{
|
||||
label: '航速',
|
||||
prop: 'sog',
|
||||
unit: '节'
|
||||
},
|
||||
{
|
||||
label: '航向',
|
||||
prop: 'angle',
|
||||
unit: '度'
|
||||
}
|
||||
]
|
||||
const monitors = ref([])
|
||||
const HikFollow = ref(null)
|
||||
// 光电随动控制标识
|
||||
const currentServo = ref('')
|
||||
let globalMap = null
|
||||
|
||||
/**
|
||||
* 拖拽事件
|
||||
* @param e
|
||||
*
|
||||
*/
|
||||
const drag = (e) => {
|
||||
dragEvent(e, 'el-dialog', () => {
|
||||
HikFollow.value.initResize(false)
|
||||
})
|
||||
}
|
||||
const getMonitorList = () => {
|
||||
const params = {
|
||||
gpsX: data.value.longitude,
|
||||
gpsY: data.value.latitude
|
||||
}
|
||||
getDevicesForServo(params).then(res => {
|
||||
monitors.value = res.data
|
||||
if(res.data.length > 0) {
|
||||
// 光电联动 默认预览第一个监控视频
|
||||
handleFollow(res.data[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
const closeInfo = () => {
|
||||
info.value = false
|
||||
if(!monitor.visible) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
const closeMonitor = () => {
|
||||
monitor.visible = false
|
||||
monitor.data = {}
|
||||
if(!info.value) {
|
||||
close()
|
||||
}
|
||||
// 退出随动
|
||||
if(currentServo.value) {
|
||||
handleExitServo()
|
||||
}
|
||||
}
|
||||
const close = () => {
|
||||
mapStore.updateWindowInfo(false)
|
||||
}
|
||||
// 判断监控设备和将要联动的定位目标是否超出可视范围、是否处于视角遮挡区
|
||||
const handleFollow = (item) => {
|
||||
const params = {
|
||||
deviceCode: item.videoCode,
|
||||
gpsX: data.value.longitude,
|
||||
gpsY: data.value.latitude
|
||||
}
|
||||
getDevicesIsCover(params).then(res => {
|
||||
if (res.msg.includes('超出')) {
|
||||
ElMessage.warning(res.msg)
|
||||
}
|
||||
handleSitMoveByGps(item)
|
||||
})
|
||||
}
|
||||
// 联动控制
|
||||
const handleSitMoveByGps = (item) => {
|
||||
closeMonitor()
|
||||
const params = {
|
||||
deviceCode: item.videoCode,
|
||||
terminalCode: data.value.terminalCode,
|
||||
gpsX: data.value.longitude,
|
||||
gpsY: data.value.latitude
|
||||
}
|
||||
sitMoveByGps(params).then(res => {
|
||||
if (res.code === 200) {
|
||||
monitor.visible = true
|
||||
nextTick(() => {
|
||||
monitor.data.videoName = item.videoName
|
||||
monitor.data.url = item.videoCode
|
||||
})
|
||||
}
|
||||
// 光电随动控制
|
||||
handleServoByRadar(item)
|
||||
})
|
||||
}
|
||||
// 光电随动
|
||||
const handleServoByRadar = (item) => {
|
||||
const params = {
|
||||
deviceCode: item.videoCode,
|
||||
terminalCode: data.value.terminalCode,
|
||||
gpsX: data.value.longitude,
|
||||
gpsY: data.value.latitude
|
||||
}
|
||||
|
||||
servoByRadar(params).then(res => {
|
||||
if (res.code === 200) {
|
||||
currentServo.value = data.value.terminalCode
|
||||
}
|
||||
})
|
||||
}
|
||||
// 退出随动
|
||||
const handleExitServo = () => {
|
||||
const params = {
|
||||
terminalCode: currentServo.value
|
||||
}
|
||||
|
||||
exitServoByRadar(params).then(res => {
|
||||
if (res.code === 200) {
|
||||
currentServo.value = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 格式化轨迹数据
|
||||
* @param data 原始数据
|
||||
* @return {Array}
|
||||
*/
|
||||
const formatTrackData = (data) => {
|
||||
const list = []
|
||||
|
||||
const trackObj = data.map((_item) => {
|
||||
const info = { ..._item }
|
||||
info.longitude = Number(_item.longitude.toFixed(6))
|
||||
info.latitude = Number(_item.latitude.toFixed(6))
|
||||
info.time = new Date(_item.gpsTime).getTime()
|
||||
return info
|
||||
}).filter((point) => Math.abs(point.latitude) < 90 && Math.abs(point.longitude) < 180)
|
||||
list.push(trackObj)
|
||||
return list
|
||||
}
|
||||
// 查询轨迹
|
||||
const handleTrack = () => {
|
||||
globalMap = GlobalMap.instance
|
||||
findAISPointPositionByMmsi({ mmsi: data.value.terminalCode }).then(res => {
|
||||
if (res.success) {
|
||||
const arr = res.result[data.value.terminalCode]
|
||||
new ShipPathInMap(globalMap.map, globalMap.map.getLayer('track'), formatTrackData(arr))
|
||||
}
|
||||
}).finally(() => {
|
||||
mapStore.updateWindowInfo(false)
|
||||
})
|
||||
}
|
||||
watch(() => data.value, () => {
|
||||
if(type.value === '_trawler_dynamic') {
|
||||
info.value = true
|
||||
getMonitorList()
|
||||
}else{
|
||||
info.value = false
|
||||
monitor.visible = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.list-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 18px;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
width: 100px;
|
||||
font-size: 14px;
|
||||
color: #00C0FFFF;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #FFFFFFFF;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
background-color: #00C0FF33;
|
||||
color: #00C0FFFF;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-image: url('@/assets/images/common/icon-triangle.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
.row{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.name{
|
||||
color: rgba(0, 192, 255, 1);
|
||||
margin: 0 5px;
|
||||
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
.distance{
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
.left{
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
.button{
|
||||
color: rgba(0, 246, 255, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
img{
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.monitor-follow-dialog{
|
||||
margin-left: calc(50% + 225px) !important;
|
||||
.el-dialog__body{
|
||||
padding: 0 !important;
|
||||
}
|
||||
.monitor-follow{
|
||||
height: 289px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
219
src/components/Map/window/uav.vue
Normal file
219
src/components/Map/window/uav.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div class="content-wrapper">
|
||||
<div class="left-wrapper">
|
||||
<div class="uav-button" @click="handleAlgorithm">{{algorithmStatus ? '关闭' : '开启'}}算法</div>
|
||||
<div class="Form">
|
||||
<el-checkbox-group v-model="algorithmValue">
|
||||
<el-checkbox
|
||||
v-for="item in algorithms"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" />
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<div class="flv-container"><FlvPlayerComponent v-if="uavDialog.url" id="uav" :url="uavDialog.url"/></div>
|
||||
</div>
|
||||
<div class="resize-handle" @mousedown="startResize"></div>
|
||||
<div class="right-wrapper">
|
||||
<CockpitCom v-if="uavDialog.visible"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import FlvPlayerComponent from '@/components/FlvPlayer/index.vue'
|
||||
import CockpitCom from '@/views/business/drone/cockpit.vue'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
// 算法开启关闭状态
|
||||
const algorithmStatus = ref(false)
|
||||
const algorithms = [
|
||||
{ value: 120000, label: 'AIS挂牌/船牌识别' },
|
||||
{ value: 140000, label: '船型识别' },
|
||||
{ value: 139000, label: '未穿救生衣' },
|
||||
{ value: 140009, label: '未悬挂国旗' },
|
||||
{ value: 140008, label: '未封舱' }
|
||||
]
|
||||
const algorithmValue = ref([])
|
||||
const uavDialog = reactive({
|
||||
visible: false,
|
||||
url: ''
|
||||
})
|
||||
// 添加分割线相关的响应式数据
|
||||
const isResizing = ref(false)
|
||||
const leftWidth = ref('462px')
|
||||
const rightWidth = ref('1391px')
|
||||
const containerWidth = ref(0)
|
||||
let containerBoundsLeft = 0 // 容器左边界
|
||||
|
||||
// 开启/关闭算法
|
||||
const handleAlgorithm = () => {
|
||||
if(algorithmStatus.value) {
|
||||
closeAlgorithm()
|
||||
} else {
|
||||
let enable_orc = false
|
||||
const valueArray = [ ...algorithmValue.value ]
|
||||
const index = valueArray.indexOf(120000)
|
||||
if (index > -1) {
|
||||
valueArray.splice(index, 1)
|
||||
enable_orc = true
|
||||
}
|
||||
const params = {
|
||||
class_codes: valueArray.join(','),
|
||||
enable_orc,
|
||||
status: 'start',
|
||||
sn: data.value.droneSn
|
||||
}
|
||||
// doStartOrStopUavAlgorithm(params).then(res => {
|
||||
// if(res.success) {
|
||||
// algorithmStatus.value = true
|
||||
// ElMessage.success('算法已开启')
|
||||
// }else {
|
||||
// ElMessage.error(res.result)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
// 添加分割线控制相关方法
|
||||
const startResize = (e) => {
|
||||
isResizing.value = true
|
||||
const containerRect = e.target.parentElement.getBoundingClientRect()
|
||||
containerWidth.value = containerRect.width
|
||||
// 保存容器的左边界位置
|
||||
containerBoundsLeft = containerRect.left
|
||||
document.addEventListener('mousemove', resize)
|
||||
document.addEventListener('mouseup', stopResize)
|
||||
}
|
||||
const resize = (e) => {
|
||||
if (isResizing.value) {
|
||||
// 使用预先保存的容器左边界
|
||||
const offsetX = e.clientX - containerBoundsLeft
|
||||
|
||||
// 计算左侧宽度百分比
|
||||
let leftPercent = offsetX / containerWidth.value * 100
|
||||
// 限制左右两侧的最小宽度
|
||||
const minRightWidth = 1391
|
||||
const minLeftWidth = 200
|
||||
|
||||
// 计算右侧最小百分比
|
||||
const minRightPercent = minRightWidth / containerWidth.value * 100
|
||||
const minLeftPercent = minLeftWidth / containerWidth.value * 100
|
||||
|
||||
// 限制拖动范围
|
||||
if (leftPercent < minLeftPercent) {
|
||||
leftPercent = minLeftPercent
|
||||
} else if (leftPercent > 100 - minRightPercent) {
|
||||
leftPercent = 100 - minRightPercent
|
||||
}
|
||||
|
||||
leftWidth.value = `${leftPercent}%`
|
||||
rightWidth.value = `${100 - leftPercent}%`
|
||||
}
|
||||
}
|
||||
|
||||
const stopResize = () => {
|
||||
isResizing.value = false
|
||||
document.removeEventListener('mousemove', resize)
|
||||
document.removeEventListener('mouseup', stopResize)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 无人机弹窗
|
||||
.content-wrapper{
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
video{
|
||||
width: 100%;
|
||||
height: 830px;
|
||||
object-fit: fill;
|
||||
}
|
||||
// //播放按钮
|
||||
video::-webkit-media-controls-play-button{display: none;}
|
||||
//进度条
|
||||
video::-webkit-media-controls-timeline{display: none;}
|
||||
//观看的当前时间
|
||||
video::-webkit-media-controls-current-time-display{display: none;}
|
||||
//剩余时间
|
||||
video::-webkit-media-controls-time-remaining-display{display: none;}
|
||||
//音量按钮
|
||||
video::-webkit-media-controls-mute-button{display: none;}
|
||||
video::-webkit-media-controls-toggle-closed-captions-buttonf{display: none;}
|
||||
//1音量的控制条
|
||||
video::-webkit-media-controls-volume-slider{display: none;}
|
||||
video::-webkit-media-controls-enclosuret{display: none;}
|
||||
.uav-button{
|
||||
width: fit-content;
|
||||
cursor: pointer;
|
||||
color: #00c0ff;
|
||||
padding: 6px 10px;
|
||||
background: rgba(22, 119, 255, 0.21);
|
||||
border-radius: 2px 2px 2px 2px;
|
||||
border: 1px solid #236ACE;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
.left-wrapper{
|
||||
// width: 60%;
|
||||
width: v-bind(leftWidth);
|
||||
height: 830px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
.el-checkbox-group{
|
||||
gap: 4px !important;
|
||||
.el-checkbox{
|
||||
width: 150px !important;
|
||||
padding-left: 5px !important;
|
||||
}
|
||||
}
|
||||
.flv-container{
|
||||
flex: 1; // 占据剩余空间
|
||||
overflow: hidden; // 隐藏溢出内容
|
||||
}
|
||||
}
|
||||
.resize-handle {
|
||||
width: 8px;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
cursor: col-resize;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
z-index: 10;
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 2px;
|
||||
// height: 40px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
.right-wrapper{
|
||||
// width: 40%;
|
||||
width: v-bind(rightWidth);
|
||||
height: 830px;
|
||||
// position: absolute;
|
||||
// right: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.temporary{
|
||||
position: absolute;
|
||||
right: 0'';
|
||||
right: 0;
|
||||
top: 48px;
|
||||
z-index: 999;
|
||||
width: 1069px;
|
||||
height: 429px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user