项目需求
实现地图有鼠标放大、缩小功能,并且固定显示地图弹框,地图上标记点有默认图标,初试默认弹框同时出现,点击某一弹框隐藏其他弹框,只显示点击的地图弹框,最终实现效果
实现方法
采用echart地图,结合经纬度起点和终点确认弹框出现位置。
代码实现
<template>
<div id="china_map_box">
<el-button class="all-btn" @click="allEvent">{{ $t('i18n.general.nationwide') }}</el-button>
<div id="china_map" v-loading="loading" />
</div>
</template>
<script>
import '@/utils/china'
import i18nMap from '../i18nMap'
export default {
name: 'MapChar',
props: {
mapData: {
type: Array,
default: () => []
}
},
data () {
return {
i18nMap,
labelData: this.mapData,
options: null,
loading: false,
barIcon: require('@/assets/images/barIcon.png'),
maptkBac: require('@/assets/images/maptkBac.png'),
zjIcon: require('@/assets/images/zjIcon.png'),
reopImg: require('@/assets/images/barTit.png'),
saleRate: this.$t('i18n.aquaculture.saleRates'),
mapList: [],
}
},
watch: {
mapData: {
handler (newVal) {
this.$nextTick(() => {
this.labelData = []
this.mapList = []
if (newVal?.length) {
newVal.forEach(item => {
let params = {
code: item.companyCode,
name: item.companyName,
areaName: item.areaName,
coords: [[item.lon, item.lat], [item.rlon, item.rlat]],
value: [item.lon, item.lat],
rateList: item.rateList
}
this.labelData.push(params)
this.mapList.push(
{
name: item.areaName,
value: 1
}
)
})
this.initEchartMap()
}
})
},
deep: true
}
},
mounted () {
this.$nextTick(() => {
this.initEchartMap()
})
window.addEventListener('resize', this.initEchartMap)
},
beforeRouteLeave (to, from, next) {
window.removeEventListener('resize', this.initEchartMap)
next()
},
destroyed() {
window.removeEventListener('resize')
},
methods: {
// 初始化中国地图
initEchartMap () {
const mapDiv = document.getElementById('china_map')
const myChart = this.$echarts.init(mapDiv)
this.labelData = [
{
"lon": 108.67749,
"lat": 18.81228,
"areaName": "海南",
"companyCode": "0623",
"companyName": "卜蜂水产(东方)有限公司",
"rateList": [
{
"code": "NP",
"name": "NP",
"value": 0.7468
},
{
"code": "PL12",
"name": "PL12",
"value": 3.9687
},
{
"code": "PL4",
"name": "PL4",
"value": 1.3059
}
],
"rlon": 101.709704,
"rlat": 18.812109
},
{
"lon": 109.70227,
"lat": 20.88826,
"areaName": "广东",
"companyCode": "0830",
"companyName": "卜蜂水产(湛江)有限公司",
"rateList": [
{
"code": "NP",
"name": "NP",
"value": null
},
{
"code": "PL12",
"name": "PL12",
"value": 0
},
{
"code": "PL4",
"name": "PL4",
"value": 0
}
],
"rlon": 95.366176,
"rlat": 29.634224
},
{
"lon": 117.88766,
"lat": 24.10597,
"areaName": "福建",
"companyCode": "0868",
"companyName": "漳州卜蜂正大水产有限公司",
"rateList": [
{
"code": "PL12",
"name": "PL12",
"value": 0
}
],
"rlon": 92.701258,
"rlat": 39.990153
},
{
"lon": 109.70253,
"lat": 20.88765,
"areaName": "广东",
"companyCode": "0836",
"companyName": "广东湛江正大水产有限公司",
"rateList": [
{
"code": "PL12",
"name": "PL12",
"value": 0.9737
}
],
"rlon": 114.716403,
"rlat": 17.828566
},
{
"lon": 121.20484,
"lat": 32.45375,
"areaName": "江苏",
"companyCode": "1417",
"companyName": "卜蜂水产(江苏)有限公司",
"rateList": [
{
"code": "PL12",
"name": "PL12",
"value": 0
}
],
"rlon": 123.470157,
"rlat": 35.124961
}
]
this.mapList = [
{
"name": "海南",
"value": 1
},
{
"name": "广东",
"value": 1
},
{
"name": "江苏",
"value": 1
}
]
myChart.clear()
myChart.resize()
this.setEchartOption();
myChart.setOption(this.options)
myChart.off('click')
// 弹框点击事件
myChart.on('click', (params) => {
if (params.seriesType == 'lines') {
this.mapList = [{ name: params.data.areaName, value: 1 }]
this.labelData = [params.data]
this.setEchartOption();
myChart.setOption(this.options)
this.$emit('switch-company', params)
}
})
},
fontSize (res) {
const clientWidth = document.documentElement.clientWidth
if (!clientWidth) return
const fontSize = clientWidth / 1920
return res * fontSize
},
transformBfb (num) {
if (num)
return num * 100 + '%'
},
setEchartOption () {
const imgTest = require('@/icons/svg/dott.png') // 地图标记点icon
const symbolImg = 'image://' + imgTest
let img = document.createElement('img')
img.src = require('@/icons/svg/mapBac.png') // 地图弹框背景图片
const mapTk = require('@/assets/images/zjIcon.png')
const zjIcon = 'image://' + mapTk
let map = 'china'
this.options = {
backgroundColor: 'transparent',
visualMap: {
show: false,
min: 0,
max: 200,
left: '10%',
top: 'bottom',
calculable: true,
seriesIndex: [0],
inRange: {
color: ['rgba(0, 107, 255, 0.3)'] // 蓝绿
}
},
geo: [
{
map: map, // 主图
aspectScale: 0.85,
layoutCenter: ["50%", "50%"], // 地图位置
layoutSize: '100%',
zlevel: 1,
zoom: 1.02, // 当前视角的缩放比例
roam: true, // 是否开启平游或缩放
scaleLimit: { // 滚轮缩放的极限控制
min: 1,
max: 3
},
center: undefined,
show: true,
itemStyle: {
normal: {
shadowOffsetX: 0,
shadowOffsetY: 15,
shadowColor: '#0a347e',
shadowBlur: 2,
borderColor: '#3496B8',
borderWidth: 1,
areaColor: 'rgba(19, 125, 255, 0.4)'
},
emphasis: {
areaColor: 'rgba(9, 199, 246, 0.6)',
shadowColor: 'rgba(20, 113, 255, 1)',
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowBlur: 10
}
},
label: {
normal: {
show: true,
color: '#fff'
},
emphasis: {
show: true,
color: '#FFFFFF'
}
},
regions: [{
name: '',
itemStyle: {
areaColor: 'rgba(0, 10, 52, 1)',
borderColor: 'rgba(0, 10, 52, 1)',
normal: {
opacity: 0.5,
label: {
show: false,
}
}
}
}],
select: { // 地图选中区域样式
label: { // 选中区域的label(文字)样式
color: '#fff',
areaColor: '#0075FF'
},
itemStyle: { // 选中区域的默认样式
areaColor: '#0075FF',
color: '#fff',
shadowColor: 'rgba(20, 113, 255, 1)',
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowBlur: 10
}
},
data: this.mapList,
}
],
series: [
{
name: 'mapSer',
type: 'map',
map: 'china',
zoom: 1,
zlevel: 6,
roam: false,
geoIndex: 0,
data: this.mapList,
animation: true,
label: {
normal: {
show: false,
color: '#FFFFFF'
},
emphasis: {
show: false,
}
}
}, { // 区域散点图
type: 'scatter',
coordinateSystem: 'geo',
zlevel: 7,
symbol: symbolImg,
symbolSize: [47, 47],
label: {
normal: {
show: false,
}
},
data: this.labelData,
itemStyle: { // 坐标点颜色
normal: {
show: true,
},
emphasis: {
show: false
}
},
}, {
type: 'lines',
zlevel: 3,
symbol: zjIcon,
symbolSize: [10, 10],
color: '#ff8003',
opacity: 1,
label: {
show: true,
borderWidth: 0,
className: 'tooltipImg',
formatter: function (params) { // 弹框显示内容
let rates = params.data.rateList
const arr = [
`{titImg|}{company|${params.name}}`,
`{reopImg|}{title|${i18nMap['销售额达成率']}}`,
]
if (rates) {
rates.forEach((item, index) => {
if (index % 2 == 0) {
let twoPro = ''
if (rates.length > index + 1 && rates.length > 1) {
const num2 = rates[index + 1].value
const bfb2 = num2 ? (num2 * 100).toFixed(2) + '%' : ' -'
twoPro = `{proName|${rates[index + 1].name}:}{proVal|${bfb2}`
}
const num1 = rates[index].value
const bfb1 = num1 ? (num1 * 100).toFixed(2) + '%' : ' -'
let rateArr = `{proName|${rates[index].name}:}{proVal|${bfb1}} ${twoPro}}`
arr.push(rateArr)
}
})
}
return arr.join('\n')
},
backgroundColor: {
image: this.maptkBac
},
padding: [5, 0, 24, 20], // 内边距属性
width: this.fontSize(258), // 宽属性
height: this.fontSize(126),// 高属性
rich: {
titImg: {
backgroundColor: {
image: this.barIcon,
},
align: 'left',
lineHeight: this.fontSize(50),
height: this.fontSize(26),
width: this.fontSize(26),
},
reopImg: {
align: 'left',
verticalAlign: 'bottom',
lineHeight: this.fontSize(20),
width: this.fontSize(20),
height: this.fontSize(20),
backgroundColor: {
image: this.reopImg
},
},
company: {
color: '#fff',
align: 'left',
height: this.fontSize(50),
lineHeight: this.fontSize(50),
fontWeight: 600,
fontSize: this.fontSize(16),
padding: [0, 0, 0, this.fontSize(5)]
},
title: {
color: '#fff',
align: 'left',
fontWeight: 'bold',
lineHeight: this.fontSize(30),
fontSize: this.fontSize(16),
padding: [this.fontSize(15), 0, 0, this.fontSize(5)],
},
subTit: {
color: '#fff',
align: 'left',
fontWeight: 400,
lineHeight: this.fontSize(25),
fontSize: this.fontSize(16),
padding: [this.fontSize(15), 0, 0, 0],
},
proName: {
color: '#fff',
align: 'left',
fontWeight: 'normal',
lineHeight: this.fontSize(25),
fontSize: this.fontSize(16),
padding: [this.fontSize(15), 0, 0, 0],
width: this.fontSize(45),
},
proVal: {
color: '#fff',
align: 'left',
fontWeight: 'normal',
lineHeight: this.fontSize(25),
fontSize: this.fontSize(16),
padding: [this.fontSize(15), 0, 0, 0],
width: this.fontSize(70),
},
}
},
lineStyle: {
type: 'dotted',
color: '#fff',
width: 2,
opacity: 1,
},
data: this.labelData,
},
]
}
},
allEvent() {
this.$emit('all')
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/mixin.scss";
#china_map_box {
width: 100%; position: relative;
@include wh(100%, 946px);
@include commonBack('~@/icons/svg/zs2.png');
#china_map {
z-index: 1;
width: 100%;
::v-deep .tooltipImg {
@include commonBack('~@/assets/images/maptkBac.png');
}
}
.imgCss {
position: absolute;
@include commonBack('~@/icons/svg/mapBackground.png');
@include wh(1065px, 775px);
top: 60px;
left: 8px;
z-index: 0;
}
.all-btn {
position: absolute; right: 20px; top: 20px; z-index: 98;
background-color: #1d377d; color: #ffffff; border: solid 1px #1d377d;
}
}
</style>