430 lines
11 KiB
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>
|