<script lang="ts" setup name="Map"> import type { Ref } from 'vue' import { getCurrentInstance, ref } from 'vue' import dayjs from 'dayjs' import L from 'leaflet' import devJson from 'public/config/dev.json' import type { PointListInfo } from '../device/dev-interface' // import aMap from '@/components/aMap/aMap.vue' import { getDataHisList, getDevInfo, getDevMap } from '@/api/ptz/dev' import useWebsocketStore from '@/store/modules/websocket' import type { lineDataI } from '@/components/Echart/echart-interface' import type { DateReturn, TypeReturn } from '@/views/statistics/statistics-interface' import 'leaflet/dist/leaflet.css' import HKVideo from '@/components/HKVideo/index.vue' import 'leaflet.label' import { getIsOffLine } from '@/utils/auth' const { proxy } = getCurrentInstance() const websocket = useWebsocketStore() const video: any = ref() const toParentsMap = ref({ lat: '', lng: '', }) const map = ref(null) const baseLayer = ref([]) const keyword = ref('') const refreshMarker = ref(false) const isRightShow = ref(false) const pointList: Ref<PointListInfo[]> = ref([]) const currentName = ref('') const src = ref('') const monitorId = ref('') const deviceIp = ref('') const device = ref({}) const devCH4Loading = ref(false) const devCH4XData: Ref<string[]> = ref([]) const devCH4Data: Ref<lineDataI[]> = ref([]) const devCH4YDataMax = ref() const webRtcServer = ref(null) const amap = ref() // 组件 function search() { refreshMarker.value = false map.value.eachLayer((layer) => { if (getIsOffLine() === 'true') { if (layer !== baseLayer.value[0]) { layer.remove() } } else if (getIsOffLine() === 'false') { if (layer !== baseLayer.value[0] && layer !== baseLayer.value[1]) { layer.remove() } } }) getDevMap(keyword.value).then((res) => { res.data.forEach((item: any) => { let icon let color switch (item.deviceStatus) { case '0': icon = './marker/gray.png' color = 'gray' break case '1': icon = './marker/green.png' color = 'green' break case '2': icon = './marker/red.png' color = 'red' break } const Icon = L.icon({ iconUrl: icon, iconSize: [40, 40], }) const message = `<div style="padding: 10px;width: 230px;font-size: 16px;"> <div style="font-weight: bold;margin-bottom: 10px">${item.monitorName || ''}</div> <div><span>所在场站:</span>${item.stationName || ''}</div> <div style="color: ` + `${color}` + `"><span style="color: black">设备状态:</span>${item.deviceStatusName || ''}</div> <div style="margin-bottom: 10px; word-break: break-all; "><span>详细位置:</span>${item.location || ''}</div> <div class="btnDetail" style="color: #3776ff;margin-left: 170px;cursor: pointer;font-size: 15px" id="btnDetail_${item.monitorId || ''}">详情</div> </div>` const popup = L.popup().setContent(message) // const marker = L.marker([item.latitude, item.longitude], { icon: Icon, slug: item }).addTo(map.value).bindPopup(popup) const divIcon = L.divIcon({ html: `<div style="display: flex"> <image src="${icon}" width="35" height="35"> <span style="background: white;border-radius: 2px;height: 21px;padding: 0 2px;border: solid 1px #2121d7;width: max-content;position: absolute;top: 8px;left: 36px;">${item.monitorName || ''}</span> </div>`, className: 'my-div-icon', iconSize: [50, 50], }) const marker = L.marker([item.latitude, item.longitude], { icon: divIcon }).addTo(map.value).bindPopup(popup) marker.on('click', (e) => { document.getElementById(`btnDetail_${item.monitorId}`).onclick = function () { clickRecall(item) } }) }) refreshMarker.value = true // 定位 if (res.data.length > 0) { map.value.setView({ lat: res.data[0].latitude, lng: res.data[0].longitude }) } }) } onBeforeMount(() => { toParentsMap.value.lat = proxy.$position.lat toParentsMap.value.lng = proxy.$position.lng }) onMounted(() => { map.value = L.map('map', { minZoom: Number(proxy.$zoom.minZoom), maxZoom: Number(proxy.$zoom.maxZoom), center: [toParentsMap.value.lat, toParentsMap.value.lng], zoom: Number(proxy.$zoom.zoom), zoomControl: false, attributionControl: false, crs: L.CRS.EPSG3857, }) const str = getIsOffLine() === 'true' ? '/static/tiles/{z}/{x}/{y}.png' : 'https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8' baseLayer.value.push(L.tileLayer(str).addTo(map.value)) if (getIsOffLine() === 'false') { baseLayer.value.push(L.tileLayer('https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8').addTo(map.value)) } search() }) // 右侧弹窗 function clickRecall(item: any) { webRtcServer.value = null isRightShow.value = true currentName.value = item.monitorName monitorId.value = item.monitorId deviceIp.value = item.deviceIp nextTick(() => { monitorId.value && getDevInfo(monitorId.value).then((res) => { device.value = res.data src.value = `http://192.168.2.4:8091/rtp/11010820001110020000_${devJson[res.data.monitorName]}.live.mp4` // res.data.playUrl video.value.play() // const devInfo = res.data // webRtcServer.value = new WebRtcStreamer('video', proxy.rtspServer) // webRtcServer.value.connect(`rtsp://${devInfo.deviceUser}:${devInfo.devicePassword}@${devInfo.deviceIp}`) // console.log(`rtsp://${devInfo.deviceUser}:${devInfo.devicePassword}@${devInfo.deviceIp}`) }) devCH4Loading.value = true const param = { monitorId: item.monitorId, startTime: dayjs(Date.now() - 1 * 60 * 1000).format('YYYY-MM-DD HH:mm:ss'), endTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), } getDataHisList(param).then((res) => { const yValue = res.data.reverse().map((item: DateReturn) => { if (Object.keys(item).toString().includes('num')) { Number(item.num) } else { Number(item.concentration) } }) devCH4YDataMax.value = Math.max(yValue) > 10 ? Math.max(yValue) : 10 devCH4XData.value = res.data.map((item: TypeReturn) => item.logTime ? item.logTime.split(' ')[1] : '') devCH4Data.value = [{ name: 'CH4', data: yValue }] devCH4Loading.value = false }) }) } // watch监听websocket变化 const unwatch = watch(websocket, (newVal) => { if (newVal.refreshMap) { search() } if (newVal.lineData && Object.keys(newVal.lineData).length >= 1 && isRightShow.value) { // console.log('监测到新的数据,并且右侧面板开启!') // 匹配id if (newVal.lineData.deviceIp === deviceIp.value) { devCH4Loading.value = true // 判断数据长度 if (devCH4XData.value.length > 300) { devCH4XData.value.splice(0, 100) devCH4Data.value[0].data.splice(0, 100) } devCH4XData.value = [...devCH4XData.value, dayjs(newVal.lineData.time).format('HH:mm:ss')] devCH4Data.value = [{ name: 'CH4', data: devCH4Data.value[0].data.concat(newVal.lineData.gasData) }] devCH4Loading.value = false } } }) onBeforeUnmount(() => { if (webRtcServer.value && typeof webRtcServer.value.disconnect === 'function') { webRtcServer.value.disconnect() webRtcServer.value = null } unwatch() }) </script> <template> <div id="map" style="height: calc(100vh - 110px);" /> <div class="input"> <el-input v-model="keyword" type="text" placeholder="场站/设备名称关键字" clearable style="width: 200px;" /> <el-button type="primary" style="margin-left: 5px;" @click="search"> 查询 </el-button> </div> <div class="cover"> <div class="green" /> <div class="gray" /> <div class="red" /> <div class="item"> 正常 </div> <div class="item"> 离线 </div> <div class="item"> 报警 </div> </div> <div v-if="isRightShow" class="right"> <svg-icon name="icon-close" class="close" @click="isRightShow = false" /> <div class="devName"> {{ currentName }} </div> <bench-col id="home" icon="icon-video" title="视频监控" height="250px" style="background: #faefe0;padding: 0;"> <!-- <h-k-video style="width: 100%;height: 100%;background:currentColor;" id="map" :device="device"/> --> <video id="video" ref="video" :src="src" autoPlay controls style="width: 100%;height: 100%;background: currentcolor;" /> </bench-col> <bench-col v-loading="devCH4Loading" icon="icon-line" title="浓度监控(ppm·m)" height="250px" style="background: #faefe0;padding: 0;"> <line-chart :x-axis-data="devCH4XData" width="340px" :data="devCH4Data" unit="" :grid="{ top: 10, left: 10, right: 30, bottom: 20, containLabel: true }" /> </bench-col> </div> </template> <style lang="scss" scoped> .map { width: 100%; height: 100%; } .right { z-index: 1000 !important; position: absolute; right: 20px; top: 10px; width: 350px; height: calc(100vh - var(--g-topbar-height) - var(--g-header-height) - 20px); background: #faefe0; border-radius: 10px; padding: 20px 0; .devName { padding-left: 10px; margin-bottom: 10px; font-size: 19px; font-weight: bold; } .title { font-size: 17px; font-weight: bold; margin: 10px 0; color: #464646; } .close { width: 20px; height: 20px; position: absolute; right: 10px; top: 20px; } } .input { position: absolute; top: 20px; left: 20px; width: 280px; height: 34px; display: flex; } .cover { position: absolute; bottom: 25px; left: 5px; width: 120px; height: 170px; border-radius: 10px; background: #faefe0; z-index: 1111; display: flex; flex-flow: column wrap; justify-content: space-evenly; align-items: center; .item { width: 50px; height: 45px; padding-top: 10px; font-weight: bold; color: #5f5f5f; } .green { background: url("../../assets/images/marker/green.png") no-repeat center / cover; width: 45px; height: 45px; } .red { background: url("../../assets/images/marker/red.png") no-repeat center / cover; width: 45px; height: 45px; } .gray { background: url("../../assets/images/marker/gray.png") no-repeat center / cover; width: 45px; height: 45px; } } </style> <style> .my-div-icon { color: black; font-size: 14px; text-align: center; pointer-events: none; /* Prevent mouse events from firing */ } </style>