用的是阿里的引擎  AntV L7 地理空间数据可视化引擎

1.在项目中引入 对应的包

pnpm install @antv/l7 @antv/l7-maps

2.在页面引入

import { GaodeMap } from '@antv/l7-maps'
import AllData from '@/assets/Map/All.json'

3.template 代码

<template>
  <Spin :spinning="loadingStatus">
    <div class="chartBox">
      <div class="head">
        <span>供应商地区分布情况</span>
        <FSelect
          :allow-clear="false"
          v-model:value="searchDataType"
          :options="countryList"
          placeholder="请选择"
          style="width: 80px"
          @change="handleSearch"
        ></FSelect>
      </div>
      <div class="map-wrapper">
        <!-- 地图容器(盒子) -->
        <div class="map-container">
          <div id="globalMap" class="map-content"></div>
        </div>
      </div>
    </div>
  </Spin>
</template>

4.script 代码

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import { Spin } from 'ant-design-vue'
import { LineLayer, PolygonLayer, Popup, Scene } from '@antv/l7'
import { GaodeMap } from '@antv/l7-maps'
import AllData from '@/assets/Map/All.json'
import { countryList } from '..'
// 中国
import ChinaData from '@/assets/Map/China.json'
// 泰国
import ThailandData from '@/assets/Map/Thailand.json'
// 越南
import VietnamData from '@/assets/Map/Vietnam.json'
//马来西亚
import MalaysiaData from '@/assets/Map/Malaysia.json'
//菲律宾
import PhilippinesData from '@/assets/Map/Philippines.json'
//日本
import JapanData from '@/assets/Map/Japan.json'
//美国
import AmericaData from '@/assets/Map/America.json'
// 印尼
import IndonesiaData from '@/assets/Map/Indonesia.json'
// 全球国家数据类型定义
interface CountryProperties {
  name: string // 国家名称
  [key: string]: any // 其他属性
}

// 中国省份数据类型定义
interface ProvinceProperties {
  name: string // 省份名称
  nameLength: number // 名称长度(动态计算)
  [key: string]: any // 其他属性
}

// GeoJSON通用Feature类型
interface GeoFeature<T> {
  type: 'Feature'
  id?: string
  properties: T
  geometry: {
    type: 'Polygon' | 'MultiPolygon'
    coordinates: number[][][] | number[][][][] // 地理坐标
  }
}

// GeoJSON FeatureCollection类型
interface GeoJSONFeatureCollection<T> {
  type: 'FeatureCollection'
  features: GeoFeature<T>[]
}
const mapListData: any = {
  1: ChinaData,
  2: ThailandData,
  3: VietnamData,
  4: MalaysiaData,
  5: PhilippinesData,
  6: AmericaData,
  7: JapanData,
  8: IndonesiaData,
}
// 对应国家中心经纬度
const mapCoordinateData: any = {
  1: [105.0, 37.8],
  2: [100.992541, 12.8],
  3: [108.277199, 14.058324],
  4: [101.98, 4.21],
  5: [121.774017, 12.879721],
  6: [-95.0, 37.0],
  7: [138.25, 39.2],
  8: [113.92, -0.79],
}
// 每个国家对应的缩放不同
const mapMultiple: any = {
  1: 2.2,
  2: 3.8,
  3: 3.5,
  4: 3.8,
  5: 3.8,
  6: 3,
  7: 3.5,
  8: 3.2,
}
// 地图实例引用
const sceneRef = ref<Scene | null>(null)
const loadingStatus = ref(false)
const searchDataType = ref(1)
const handleSearch = () => {
  if (loadingStatus.value) return
  loadingStatus.value = true
  if (sceneRef.value) {
    sceneRef.value.destroy()
    sceneRef.value = null
  }
  initMap()
  setTimeout(() => {
    loadingStatus.value = false
  }, 1000)
}
// 初始化地图
const initMap = () => {
  // 创建地图场景(全球视角)
  const scene = new Scene({
    id: 'globalMap',
    map: new GaodeMap({
      style: 'light', // 浅色底图
      center: mapCoordinateData[searchDataType.value], // 初始中心(中国视野)
      zoom: mapMultiple[searchDataType.value], // 初始缩放级别
      maxZoom: 12, // 最大缩放
      minZoom: 1, // 最小缩放
    }),
  })
  sceneRef.value = scene

  // 地图加载完成后加载数据
  scene.on('loaded', async () => {
    try {
      const [countryRes, provinceRes] = [
        // 全球国家GeoJSON数据
        AllData,
        // 中国省份GeoJSON数据
        mapListData[searchDataType.value],
      ]
      // 解析数据
      const countryData: GeoJSONFeatureCollection<CountryProperties> = countryRes as any
      const provinceRawData: GeoJSONFeatureCollection<{ name: string }> = provinceRes as any
      // 2. 处理中国省份数据(计算名称长度)
      const provinceData: GeoJSONFeatureCollection<ProvinceProperties> = {
        ...provinceRawData,
        features: provinceRawData.features.map(feature => ({
          ...feature,
          properties: {
            ...feature.properties,
            nameLength: feature.properties.name.length, // 计算名称长度
          },
        })),
      }
      console.log(provinceData, 'provinceData')

      // 3. 创建全球国家填充图层(基础显示)
      const countryLayer = new PolygonLayer({
        zIndex: 1, // 底层显示
      })
        .source(countryData)
        .color('#f0f0f0') // 浅灰色填充
        .shape('fill')
        .style({
          opacity: 0.9, // 半透明
        })

      // 4. 创建全球国家边界图层
      const countryBorderLayer = new LineLayer({
        zIndex: 2,
      })
        .source(countryData)
        .color('#cccccc') // 灰色边界
        .size(0.6)
        .style({
          lineType: 'solid',
        })

      // 5. 创建中国省份高亮图层
      const chinaProvinceLayer = new PolygonLayer({
        zIndex: 3, // 覆盖在国家图层之上
      })
        .source(provinceData)
        .scale('nameLength', {
          type: 'linear',
          domain: [2, 6], // 省份名称长度范围(2-6字)
        })
        .color('nameLength', ['#e6f7ff', '#bae7ff', '#91d5ff', '#69c0ff', '#40a9ff', '#1890ff']) // 蓝色系渐变
        .shape('fill')
        .active(true) // 鼠标悬停高亮

      // 6. 创建中国省份边界图层
      const chinaBorderLayer = new LineLayer({
        zIndex: 4,
      })
        .source(provinceData)
        .color('#ffffff') // 白色边界
        .size(1.2)
        .style({
          lineType: 'solid',
        })

      // 添加所有图层到地图
      scene.addLayer(countryLayer)
      scene.addLayer(countryBorderLayer)
      scene.addLayer(chinaProvinceLayer)
      scene.addLayer(chinaBorderLayer)

      // 7. 中国省份鼠标交互(显示弹窗)
      chinaProvinceLayer.on('mousemove', (e: { lngLat: [number, number]; feature: GeoFeature<ProvinceProperties> }) => {
        const { name, nameLength } = e.feature.properties
        const popup = new Popup({
          offsets: [0, -10],
          closeButton: false,
        }).setLnglat(e.lngLat).setHTML(`
            <div style="padding: 8px 12px; background: rgba(255,255,255,0.9); border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.15)">
              <p style="margin: 0; font-weight: 500">${name}</p>
              <p style="margin: 4px 0 0; font-size: 12px; color: #666">名称长度: ${nameLength}</p>
            </div>
          `)
        scene.addPopup(popup)
      })

      // 8. 全球国家鼠标交互(显示弹窗)
      countryLayer.on('mousemove', (e: { lngLat: [number, number]; feature: GeoFeature<CountryProperties> }) => {
        const { name } = e.feature.properties
        const popup = new Popup({
          offsets: [0, -10],
          closeButton: false,
        }).setLnglat(e.lngLat).setHTML(`
            <div style="padding: 6px 10px; background: rgba(255,255,255,0.9); border-radius: 4px; box-shadow: 0 2px 6px rgba(0,0,0,0.1)">
              <p style="margin: 0; font-size: 13px">${name}</p>
            </div>
          `)
        scene.addPopup(popup)
      })
    } catch (error) {
      console.error('地图初始化失败:', error)
    }
  })
}

// 组件挂载时初始化地图
onMounted(() => {
  initMap()
})

// 组件卸载时销毁地图
onUnmounted(() => {
  if (sceneRef.value) {
    sceneRef.value.destroy()
    sceneRef.value = null
  }
})
// 处理窗口大小变化
const handleResize = () => {
  handleSearch()
}

window.addEventListener('resize', handleResize)
</script>

5.CSS代码

<style scoped lang="scss">
.chartBox {
  width: 100%;
  height: 454px;
  background-color: #ffffff;
  padding: 24px;
  margin-bottom: 16px;
  overflow: hidden;
  z-index: 1000;
  .head {
    font-weight: 500;
    font-size: 16px;
    color: #333333;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
  }
}

.map-wrapper {
  /* 外层容器,可控制整体大小 */
  width: 100%;
  height: 39vh;
  margin: 0 auto; /* 居中显示 */
  box-sizing: border-box;
}

.map-container {
  /* 核心:地图盒子容器 */
  width: 100%;
  height: 100%;
  position: relative; /* 作为地图的定位基准 */
  overflow: hidden;
  border: 1px solid #eee;
  border-radius: 8px;
  box-sizing: border-box;
}

.map-content {
  /* 地图元素 */
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0; /* 填满容器 */
}
:deep(.l7-control-logo-link) {
  display: none;
}
</style>

对应国家的JSON文件可以看上一篇文章~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐