vue2+echart地图实现默认弹框同时出现

63 阅读2分钟

项目需求

实现地图有鼠标放大、缩小功能,并且固定显示地图弹框,地图上标记点有默认图标,初试默认弹框同时出现,点击某一弹框隐藏其他弹框,只显示点击的地图弹框,最终实现效果

cab9b04adf5f413b15a8df7495472dd.png

实现方法

采用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>