Files
erqi-web/src/components/Map/index.vue
2025-12-26 01:14:12 +08:00

532 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="map"></div>
<div class="map-mask"></div>
<!-- 四周边框 -->
<div class="map-border"></div>
<img class="map-bottom" src="@/assets/images/common/icon-bottom.png" alt="">
<img class="AI" src="@/assets/images/common/icon-AI.png" alt="" @click="toModel">
<div class="AI AI-text" @click="toModel"></div>
<!-- 信息弹窗 -->
<InfoWindowComponent/>
<!-- 渔船信息弹窗 -->
<TrawlerInfoWindowComponent/>
</template>
<script setup>
import { computed, onMounted, onUnmounted, watch } from 'vue'
import * as maptalks from 'maptalks'
import GlobalMap from './js/GlobalMap'
import useMapStore from '@/store/modules/map'
import { getAssetsFile } from '@/utils/common'
import * as BoatUtil from './lbtbox/boatTerminal'
import * as $configs from './map-config.js'
import { ElMessage } from 'element-plus'
import { monitors, uavs, stations, environmentals, fences, detailFences } from './js/mock.js'
import InfoWindowComponent from '@/components/Map/window/index.vue'
import TrawlerInfoWindowComponent from '@/components/Map/window/trawler.vue'
import { dsVideoList, findUavPage, findEnvPage } from '@/api/device.js'
const mapStore = useMapStore()
const UAV = computed(() => mapStore.legend.UAV)
const monitor = computed(() => mapStore.legend.monitor)
const origin_monitor = computed(() => mapStore.legend.origin_monitor)
const ais_station = computed(() => mapStore.legend.ais_station)
const environmental = computed(() => mapStore.legend.environmental)
const fence = computed(() => mapStore.legend.fence)
const sector = computed(() => mapStore.sector)
let globalMap = null
let vector = {}
const geography = {
monitor: [], // 监控数据
origin_monitor: [], // 原始监控数据
UAV: [], // 无人机
ais_station: [], // ais基站
environmental: [], // 环境监测
fence: [] // 电子围栏
}
let sectorLayer = null
const initMap = () => {
const mapDom = document.getElementById('map')
globalMap = new GlobalMap(mapDom)//, { seamlessZoom: false })
}
const initLayerToMap = (type) => { // 地理图层注册
if (vector[type]) {
vector[type].remove()
vector[type] = null
}
vector[type] = new maptalks.VectorLayer(type).addTo(globalMap.map)
}
// 叠加电子围栏数据
const addFenceToMap = (type) => {
initLayerToMap(type)
geography[type].forEach((e) => {
if (e.positionInfo) {
const params = []
const symbol = [
{
lineColor: e.lineColor,
lineWidth: e.lineWidth - 0,
lineDasharray: [ 4, 4 ],
polygonFill: e.fillColor,
polygonOpacity: e.diaphaneity - 0,
textFill: 'black',
textHaloFill: 'white',
textHaloRadius: 2,
textName: e.warnAreaName,
textSize: {
stops: [
[ 10, 0 ],
[ 11, 12 ]
]
}
}
]
// 点位数据处理
const arr = e.positionInfo.split(';').map((pair) => {
const [ x, y ] = pair.split(',').map(Number)
return { x, y }
})
params.push(arr)
params.push({ symbol, zIndex: 1 })
const geometry = new maptalks.Polygon(...params)
vector[type].addGeometry(geometry)
}
})
}
/**
* 叠加监控数据
* @param type
* @param prop 监控类型
*/
const addMonitorToMap = () => {
initLayerToMap('monitor')
initLayerToMap('sectors_monitor')
geography.monitor.forEach(item => {
if (item.longitude && item.latitude) {
const marker = new maptalks.Marker(
{
x: item.longitude,
y: item.latitude
},
{
id: item.id,
symbol: $configs.getDevicePointSymbol('_monitor', { ...item, name: item.videoName }),
properties: item,
zIndex: 2
}
)
marker.addTo(vector.monitor)
drawSector('_monitor', [ item.longitude, item.latitude ], 5 * 1000, item.id, 30)
marker.on('click', (evt) => {
// 先隐藏所有扇形
changeSectorsInLayer('sectors_monitor', false)
// 确保扇形图层是可见的
if (!vector.sectors_monitor.isVisible()) {
vector.sectors_monitor.show()
}
// 显示当前点击项的所有相关扇形(圆、椭圆、线)
const baseId = `sector__monitor${item.id}`
const circleGeometry = vector.sectors_monitor.getGeometryById(baseId + '_circle')
const ellipseGeometry = vector.sectors_monitor.getGeometryById(baseId + '_ellipse')
const lineGeometry = vector.sectors_monitor.getGeometryById(baseId + '_line')
if (circleGeometry) {
circleGeometry.show()
}
if (ellipseGeometry) {
ellipseGeometry.show()
}
if (lineGeometry) {
lineGeometry.show()
}
mapStore.updateWindowInfo({ visible: true, type: '_monitor', data: { ...item } })
})
}
})
vector.monitor.show()
}
/**
* 叠加无人机数据
* @param type
* @param prop 无人机类型
*/
const addUAVToMap = () => {
initLayerToMap('UAV')
initLayerToMap('sectors_UAV')
geography.UAV.forEach(item => {
if (item.longitude && item.latitude) {
const marker = new maptalks.Marker(
{
x: item.longitude,
y: item.latitude
},
{
id: item.id,
symbol: $configs.getDevicePointSymbol('_UAV', { ...item, name: item.videoName }),
properties: item,
zIndex: 2
}
)
marker.addTo(vector.UAV)
drawSector('_UAV', [ item.longitude, item.latitude ], 5 * 1000, item.id)
marker.on('click', (evt) => {
// 先隐藏所有扇形
changeSectorsInLayer('sectors_UAV', false)
// 确保扇形图层是可见的
if (!vector.sectors_UAV.isVisible()) {
vector.sectors_UAV.show()
}
// 显示当前点击项的所有相关扇形(圆、椭圆、线)
const baseId = `sector__UAV${item.id}`
const circleGeometry = vector.sectors_UAV.getGeometryById(baseId + '_circle')
if (circleGeometry) {
circleGeometry.show()
}
if(item.sourceType) {
mapStore.updateWindowInfo({ visible: true, type: '_UAV', data: { ...item } })
}
})
}
})
vector.UAV.show()
}
// 添加辅助函数来隐藏所有扇形
const changeSectorsInLayer = (layerName, show) => {
if (vector[layerName]) {
const allGeometries = vector[layerName].getGeometries()
allGeometries.forEach(geometry => {
if(show) {
geometry.show()
}else{
geometry.hide()
}
})
}
}
// 需要监控的起始角度和结束角度,修改视野范围可以传递经纬度坐标
const drawSector = (type, center, radius, id, angle) => {
const sectorId = `sector_${type}${id}`
// 如果已存在同ID的扇形则先移除
const existingSector = vector['sectors' + type].getGeometryById(sectorId)
if (existingSector) {
vector['sectors' + type].removeGeometry(existingSector)
}
// 如果已有该监控点的扇形图层,则先移除
if (globalMap.map.getLayer(sectorId)) {
globalMap.map.getLayer(sectorId).remove()
}
let circle = new maptalks.Circle(center, radius, {
id: sectorId + '_circle',
symbol: {
lineColor: '#1CA8FF',
lineWidth: 1,
lineOpacity: 1,
polygonFill: '#1ca8ff',
polygonOpacity: 0.16
}
})
if(angle) {
let ellipse = new maptalks.Sector(center, radius - 1 * 1000, angle - 30, angle + 30, {
id: sectorId + '_ellipse',
symbol: {
lineColor: '#FF8D1C',
polygonFill: '#ff8d1c29'
}
})
let line = new maptalks.Sector(center, radius + 1 * 1000, angle, angle, {
id: sectorId + '_line',
symbol: {
lineColor: '#FF8D1C',
polygonFill: '#ff8d1c29',
lineDasharray: [ 5, 10 ]
}
})
vector['sectors' + type].addGeometry([ circle, ellipse, line ])
}else{
vector['sectors' + type].addGeometry(circle)
}
// 添加到可视域图层
changeSectorsInLayer('sectors' + type, false)
// vector['sectors' + type].hide()
}
/**
* 叠加ais基站数据
* @param type
* @param prop ais基站类型
*/
const addAisStationToMap = () => {
initLayerToMap('ais_station')
geography.ais_station.forEach(item => {
if (item.longitude && item.latitude) {
const marker = new maptalks.Marker(
{
x: item.longitude,
y: item.latitude
},
{
id: item.id,
symbol: $configs.getDevicePointSymbol('_ais_station', { ...item }),
properties: item,
zIndex: 2
}
)
marker.addTo(vector.ais_station)
marker.on('click', (evt) => {
})
}
})
vector.ais_station.show()
}
/**
* 叠加环境监测数据
* @param type
* @param prop 环境监测类型
*/
const addEnvironmentalToMap = () => {
initLayerToMap('environmental')
geography.environmental.forEach(item => {
if (item.longitude && item.latitude) {
const marker = new maptalks.Marker(
{
x: item.longitude,
y: item.latitude
},
{
id: item.id,
symbol: $configs.getDevicePointSymbol('_environmental', { ...item }),
properties: item,
zIndex: 2
}
)
marker.addTo(vector.environmental)
marker.on('click', (evt) => {
})
}
})
vector.environmental.show()
}
const initUAV = () => {
const params = {
pageNo: 1,
pageSize: 9999
}
findUavPage(params).then(res => {
if (res.success) {
geography.UAV = res.result.records
addUAVToMap()
}
})
}
const initMonitor = () => {
const params = {
pageNo: 1,
pageSize: 9999
}
dsVideoList(params).then(res => {
if (res.success) {
geography.monitor = res.result.records
addMonitorToMap()
}
})
}
const initAisStation = () => {
geography.ais_station = stations
addAisStationToMap()
}
const initEnvironmental = () => {
geography.environmental = environmentals
addEnvironmentalToMap()
}
const initFence = () => {
geography.fence = fences
geography.detailFence = detailFences
addFenceToMap('fence')
addFenceToMap('detailFence')
}
const toModel = () => {
mapStore.updateLargeModel(true)
}
onMounted(() => {
initMap()
initFence()
initUAV()
initMonitor()
initAisStation()
initEnvironmental()
// 渔船链接
BoatUtil.init(mapStore)
BoatUtil.getShip()
// 轨迹图层
initLayerToMap('track')
})
watch(() => UAV.value, () => {
if(UAV.value) {
initUAV()
}else{
vector.UAV?.hide()
}
})
watch(() => monitor.value, () => {
if(monitor.value) {
initMonitor()
}else{
vector.monitor?.hide()
}
})
watch(() => ais_station.value, () => {
if(ais_station.value) {
initAisStation()
}else{
vector.ais_station?.hide()
}
})
watch(() => environmental.value, () => {
if(environmental.value) {
initEnvironmental()
}else{
vector.environmental?.hide()
}
})
watch(() => fence.value, () => {
if(fence.value) {
initFence()
}else{
vector.fence?.hide()
vector.detailFence?.hide()
}
})
watch(() => sector.value.monitor, (newVal) => {
if(vector.sectors_monitor) {
if(newVal) {
// vector.sectors_monitor.show()
changeSectorsInLayer('sectors_monitor', true)
} else {
// vector.sectors_monitor.hide()
changeSectorsInLayer('sectors_monitor', false)
}
}
})
watch(() => sector.value.UAV, (newVal) => {
if(vector.sectors_UAV) {
if(newVal) {
// vector.sectors_UAV.show()
changeSectorsInLayer('sectors_UAV', true)
} else {
// vector.sectors_UAV.hide()
changeSectorsInLayer('sectors_UAV', false)
}
}
})
onUnmounted(() => {
globalMap.destroy()
globalMap = null
BoatUtil.destroyWebsocket()
})
</script>
<style lang="scss" scoped>
#map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.map-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('@/assets/images/common/map-mask.png');
background-size: 100% 100%;
background-repeat: no-repeat;
pointer-events: none;
}
.map-border {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
height: calc(100% - 18px);
background-image: url('@/assets/images/common/map-border.png');
background-size: 100% 100%;
background-repeat: no-repeat;
pointer-events: none;
}
.map-bottom {
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%);
}
.AI{
position: absolute;
bottom: 15px;
left: 50%;
transform: translate(-50%);
cursor: pointer;
z-index: 2;
}
.AI-text{
font-family: 'YouSheBiaoTiHei';
font-size: 22px;
-webkit-text-stroke: 1px rgba(4,176,244,0.4);
text-align: left;
font-style: normal;
text-transform: none;
background-image: linear-gradient(180deg, #FFFFFF 5%, #2DE5FF 100%);
-webkit-background-clip: text;
bottom: 33px;
-webkit-text-fill-color: transparent;
}
// 弹窗样式
:deep(.dialog-wrapper.el-dialog){
// height: 900px;
background: linear-gradient(90deg, rgba(12, 25, 41, 0.8) 0%, rgba(12, 25, 41, 0.6) 100%);
border: 1px solid #00C0FF;
border-radius: 0;
padding: 0;
.el-dialog__header{
display: flex;
justify-content: space-between;
padding: 0 16px;
background: linear-gradient(0deg, rgba(10, 169, 255, 0) 0%, rgba(10, 169, 255, 0.5) 100%);
.title{
display: flex;
justify-content: space-between;
align-items: center;
font-size: 20px;
color: #FFFFFF;
text-align: left;
height: 56px;
.icon-group img {
cursor: pointer;
&:active {
opacity: .8;
}
}
}
}
.el-dialog__body {
padding: 16px;
box-sizing: border-box;
}
}
</style>