AnalysisSystemForRadionucli.../src/views/stationOperation/components/MapMarker.vue

430 lines
11 KiB
Vue

<template>
<div ref="mapPopupRef" class="facility-info-popover">
<h2>{{ popupTitle }} Info</h2>
<a-spin :spinning="isGettingInfo">
<div class="facility-info-item" v-for="(item, index) in columns" :key="index">
<div class="facility-info-item-label">{{ item.label }}</div>
<div class="facility-info-item-content">{{ currStationInfo[item.key] || '--' }}</div>
</div>
</a-spin>
</div>
</template>
<script>
import Overlay from 'ol/Overlay'
import { MarkerIcon, MarkerType } from './markerEnum'
import { fromLonLat } from 'ol/proj'
import PopupColumns from './markerPopupColumns'
import { getAction } from '../../../api/manage'
import { debounce } from 'lodash'
import { decimalToDms } from '@/utils/map'
const POPUP_OVERLAY_ID = 'map_popup'
export default {
props: {
list: {
type: Array,
required: true,
},
currList: {
type: Array,
required: true,
},
orgList: {
type: Array,
required: true,
},
markerType: {
type: Number,
default: 1,
},
radius: {
type: Number,
},
},
data() {
return {
currStationInfo: {},
isGettingInfo: false,
columns: {},
popupTitle: '',
}
},
mounted() {
this.map = this.$parent.getMapInstance()
this.map.getView().on('change:resolution', () => {
// 分辨率发生变化时的处理逻辑
this.changeCircleRadius()
})
this.getStationInfo = debounce((stationInfo) => {
// 查询设施详情时去抖动
if (this.isHover) {
this._getStationInfo(stationInfo)
}
}, 0)
},
methods: {
initCircles() {
const circleRadius = this.getRadius() * 2
console.log('this.list', this.list)
this.list
.filter(
(stationInfo) =>
stationInfo.stationType !== MarkerType.NuclearFacility && stationInfo.stationType !== MarkerType.NRL
)
.forEach((stationInfo) => {
this.map.addOverlay(this.getCircle(stationInfo, circleRadius))
})
},
getCircle({ lon, lat, stationType, stationId }, circleRadius) {
const circleDiv = document.createElement('div')
circleDiv.className = 'custom-map-circle'
circleDiv.style.width = circleRadius + 'px'
circleDiv.style.height = circleRadius + 'px'
circleDiv.style.borderRadius = '50%'
circleDiv.style.backgroundColor = 'rgba(255, 0, 0, .4)'
return new Overlay({
position: fromLonLat([lon, lat]),
element: circleDiv,
id: `circle_${stationType}_${stationId}`,
positioning: 'center-center',
className: 'circle-overlay',
})
},
// 修改圆的半径
changeCircleRadius() {
const overlays = this.map.getOverlays().getArray()
const circleOverlays = overlays.filter((item) => item.id.indexOf('circle') == 0) // 根据id标识获取所有的圆
const circleRadius = this.getRadius() * 2
circleOverlays.forEach((circle) => {
const circleEle = circle.getElement()
circleEle.style.width = circleRadius + 'px'
circleEle.style.height = circleRadius + 'px'
})
},
// 半径计算
getRadius() {
const metersPerUnit = this.map.getView().getProjection().getMetersPerUnit()
const distance = (this.radius * 1000) / metersPerUnit
const resolution = this.map.getView().getResolution()
return distance / resolution
},
// 初始化marker
initMarkers() {
this.list.forEach((stationInfo) => {
this.map.addOverlay(this.getMarker(stationInfo))
})
},
// 获取marker图标
getMarker(stationInfo) {
const { lon, lat } = stationInfo
const img = document.createElement('img')
img.draggable = false
img.src = MarkerIcon[stationInfo.stationType]
img.addEventListener('click', () => {
this.$emit('markerClick', stationInfo)
})
img.addEventListener('mouseover', () => {
this.isHover = true
this.showMapPopup(stationInfo)
})
img.addEventListener('mouseout', () => {
this.isHover = false
this.closeMapPopup()
})
return new Overlay({
position: fromLonLat([lon, lat]),
element: img,
id: `marker_${stationInfo.stationType}_${stationInfo.stationId}`,
positioning: 'center-center',
})
},
// 初始化波纹
initRipples() {
this.currList
.filter((stationInfo) => stationInfo.status !== 'Unoperating')
// .filter(
// stationInfo =>
// stationInfo.stationType !== MarkerType.NuclearFacility && stationInfo.stationType !== MarkerType.NRL
// )
.forEach((stationInfo) => {
this.map.addOverlay(this.getRipple(stationInfo))
})
},
getRipple({ lon, lat, stationId, stationType, quality }) {
const rippleDiv = document.createElement('div')
rippleDiv.className = 'custom-ripple'
if (quality == 'excellent') {
rippleDiv.innerHTML = ` <div class="inner-ripple-excellent-1"></div>
<div class="inner-ripple-excellent-2"></div>
`
} else if (quality == 'good') {
rippleDiv.innerHTML = ` <div class="inner-ripple-good-1"></div>
<div class="inner-ripple-good-2"></div>
`
} else if (quality == 'bad') {
rippleDiv.innerHTML = ` <div class="inner-ripple-bad-1"></div>
<div class="inner-ripple-bad-2"></div>
`
}
return new Overlay({
position: fromLonLat([lon, lat]),
element: rippleDiv,
id: `ripple_${stationType}_${stationId}`,
positioning: 'center-center',
className: 'ripple-overlay',
})
},
// 初始化地图弹窗
initMapPopup() {
this.popupOverlay = new Overlay({
element: this.$refs.mapPopupRef,
positioning: 'top-center',
id: POPUP_OVERLAY_ID,
})
this.map.addOverlay(this.popupOverlay)
},
// 显示地图弹窗
async showMapPopup(stationInfo) {
this.popupOverlay.setPosition(fromLonLat([stationInfo.lon, stationInfo.lat]))
this.popupOverlay.setOffset([stationInfo.stationType == MarkerType.NuclearFacility ? 9 : 0, 35])
this.columns = PopupColumns[stationInfo.stationType]
this.popupTitle = stationInfo.stationType
this.isGettingInfo = true
this.getStationInfo(stationInfo)
},
// 获取站点详情
async _getStationInfo(stationInfo) {
try {
const { success, result, message } = await getAction('/jeecg-station-operation/stationOperation/findInfo', {
stationId: stationInfo.stationId,
type: stationInfo.stationType,
})
if (success) {
result.lon = decimalToDms(result.lon || result.longitude)
result.lat = decimalToDms(result.lat || result.latitude, false)
let params =
this.currList.find((item) => {
return item.stationId === result.stationId
}) || {}
this.currStationInfo = { ...result, ...params }
} else {
this.$message.error(message)
}
} catch (error) {
console.error(error)
} finally {
this.isGettingInfo = false
}
},
//关闭地图弹窗
closeMapPopup() {
this.popupOverlay.setPosition(null)
},
},
watch: {
list() {
this.map.getOverlays().clear()
this.initMapPopup()
this.initMarkers()
this.initRipples()
if (this.markerType == 2) {
this.initCircles()
}
},
currList: {
handler(newVal, oldVal) {
this.orgList.forEach((item) => {
var layer = this.map
.getOverlays()
.getArray()
.find((layer) => {
return layer.id === `ripple_${item.stationType}_${item.stationId}`
})
if (layer) {
this.map.removeOverlay(layer)
}
})
this.initRipples()
},
deep: true,
},
},
}
</script>
<style lang="less" scoped>
.facility-info {
&-popover {
position: relative;
width: 350px;
background-color: rgba(2, 26, 29, 0.9);
border-radius: 4px;
box-shadow: 0 0 5px rgba(2, 26, 29, 0.9);
padding: 5px;
&::before {
position: absolute;
top: -17px;
left: 50%;
content: '';
transform: translateX(-50%);
border: 6px solid transparent;
border-bottom-color: rgba(2, 26, 29, 0.9);
border-bottom-width: 12px;
}
h2 {
color: #6ebad0;
font-size: 16px;
text-align: center;
line-height: 30px;
border-bottom: 1px solid #0a544e;
margin-bottom: 0;
color: rgb(187, 138, 18);
}
}
@border: 1px solid #0a544e;
&-item {
display: flex;
border: @border;
border-top: 0;
line-height: 25px;
&-label,
&-content {
width: 50%;
padding: 0 3px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&-label {
border-right: @border;
color: #6ebad0;
}
}
}
</style>
<style lang="less">
.custom-ripple {
width: 85px;
height: 85px;
@duration: 1.8s;
@delay: 0.9s;
.inner-ripple-excellent-1 {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background-image: radial-gradient(circle, transparent 10%, rgba(15, 148, 28, 0.2) 30%, rgba(15, 148, 28, 0.5) 60%);
animation: rippleEffect @duration linear 0s infinite;
}
.inner-ripple-excellent-2 {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 50%;
transform: scale(0);
background-image: radial-gradient(circle, transparent 10%, rgba(15, 148, 28, 0.2) 30%, rgba(15, 148, 28, 0.5) 60%);
animation: rippleEffect @duration linear @delay infinite;
}
.inner-ripple-good-1 {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background-image: radial-gradient(
circle,
transparent 10%,
rgba(187, 138, 18, 0.2) 30%,
rgba(187, 138, 18, 0.5) 60%
);
animation: rippleEffect @duration linear 0s infinite;
}
.inner-ripple-good-2 {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 50%;
transform: scale(0);
background-image: radial-gradient(
circle,
transparent 10%,
rgba(187, 138, 18, 0.2) 30%,
rgba(187, 138, 18, 0.5) 60%
);
animation: rippleEffect @duration linear @delay infinite;
}
.inner-ripple-bad-1 {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background-image: radial-gradient(circle, transparent 10%, rgba(165, 58, 35, 0.2) 30%, rgba(165, 58, 35, 0.5) 60%);
animation: rippleEffect @duration linear 0s infinite;
}
.inner-ripple-bad-2 {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-radius: 50%;
transform: scale(0);
background-image: radial-gradient(circle, transparent 10%, rgba(165, 58, 35, 0.2) 30%, rgba(165, 58, 35, 0.5) 60%);
animation: rippleEffect @duration linear @delay infinite;
}
}
@keyframes rippleEffect {
0% {
transform: scale(0.6);
opacity: 1;
}
100% {
transform: scale(1.2);
opacity: 0;
}
}
.ripple-overlay,
.circle-overlay {
pointer-events: none !important;
}
</style>