Files
erqi-web/src/components/Map/window/trawler.vue

320 lines
8.0 KiB
Vue
Raw Normal View History

2025-12-24 18:19:05 +08:00
<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 => {
2025-12-25 18:33:15 +08:00
const arr = res.data[data.value.terminalCode]
2025-12-24 18:19:05 +08:00
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>