1.技术选项:

vite+vue3+elementplus

2.实现思路:

1.按需加载如何实现?

    要实现按需加载就不能直接在项目的入口文件这种地方去通过script标签引入,这里封装了一个加载百度地图的js方法,实现动态的插入sript脚本

  根目录下创建Bmap.js文件

import _loadScript from 'load-script';

export function Map(url) {
    return new Promise((resolve, reject) => {
        _loadScript(url, (error, script) => {
            if (error) {
                return reject(error);
            }
            console.log('====================================');
            console.log(script);
            console.log('====================================');
            resolve(script);
        });
    });
}


export default function loadBMap() {
    return new Promise(function(resolve, reject) {
        if (typeof BMap !== 'undefined') {
            resolve(BMap);
            return true;
        }
        window.onBMapCallback = function() {
            resolve(BMap);
        };
        let script = document.createElement('script');
        script.type = 'text/javascript';
        script.src =
            'https://api.map.baidu.com/api?v=3.0&ak=自己的';
        script.onerror = reject;
        document.head.appendChild(script);
    });
}

2.初始化加载地图(注意事项)

    要初始化加载地图,我们需要确保的时候,在页面绘制完,或者弹框加载完成之后再去调用初始化的方法,否则就会报错。

报错示例:

Cannot read properties of undefined (reading  kc )

封装示例图

3.map.js脚本实现如下:

// 加载百度地图
export function LoadBaiduMapScript() {
  console.log("百度地图脚本初始化ing----------------");
  const BMap_URL =
    "https://api.map.baidu.com/api?v=3.0&ak=换成自己的&callback=onBMapCallback";
  return new Promise((resolve, _reject) => {
    // 如果已加载直接返回
    if (typeof BMap !== "undefined") {
      resolve(BMap);
      return true;
    }
    // 百度地图异步加载回调处理
    window.onBMapCallback = function () {
      console.log("百度地图脚本初始化成功...");
      resolve(BMap);
    };
    // 插入script脚本
    const scriptNode = document.createElement("script");
    scriptNode.setAttribute("type", "text/javascript");
    scriptNode.setAttribute("src", BMap_URL);
    document.body.appendChild(scriptNode);
  });
}

vue代码如下:

注意:该代码的ProModal是本人自己封装的,借鉴即可,原理一致

new BMap 可能会爆红 不用管,不影响使用

一定要在他外层的组件加载完之后 再调用initMap({})等方法,因为我这里使用的是弹框 只需要确定弹框打开之后,再调用就行了,之前使用的一直是onMounted生命周期,导致报错(自己区分场景即可)

<template>
  <div class="w-full h-full">
    <ProModal
      v-model:visible="visible"
      width="1200"
      @cancel="destroyMap"
      title="地图选择"
      @ok="handleOk"
      :use-type="2"
    >
      <ElInput
        placeholder="请输入地址"
        class="mb-2"
        style="width: 40%"
        v-model="searchValue"
        clearable
        :readonly="loading"
      >
        <template #append>
          <ElButton type="primary" @click="handleSearch">搜索</ElButton>
        </template>
      </ElInput>
      <div id="container" class="positionbox" v-loading="loading"></div>
    </ProModal>
    <ElButton type="primary" @click="handleClick">点击弹出地图</ElButton>
  </div>
</template>

<script lang="ts" setup>
import { ref, unref, watch } from "vue";
import { ProModal } from "@/components/ProComponents/index";
import { useMessage } from "@/hooks/useMessage";
import { ElButton, ElInput } from "element-plus";
import positionIcon from "@/assets/images/position.png"; //找一个标记地点的图标替代
import { LoadBaiduMapScript } from "./map";

const searchValue = ref(""); //搜索框

const emits = defineEmits(["selectAddress"]);
const visible = ref(false);
const loading = ref(false);
//下方的address lon lat 自己找一个地址的信息 写死就行
const address: any = ref("北京");
const lon: any = ref(116.40); //代表经度 (longitude)
const lat: any = ref(39.90); //代表纬度 (latitude)
const map: any = ref({});
const point: any = ref({});
const marker: any = ref({});
const current: any = ref({});
const message = useMessage();

const handleClick = () => {
  visible.value = true;
};

watch(visible, async (newVal) => {
  if (newVal) {
    await LoadBaiduMapScript();
    //必须在实例化之后才能获取到地图实例
    await initMap({});
    //定位
    await browserPosition();
  }
});

// 初始百度地图 record可为传参 去修改上次选择的地址
const initMap = (record: any) => {
  address.value = record.address ? record.address : "郑州市";
  // 1.创建地图实例
  map.value = new BMap.Map("container");
  lon.value = record?.coordinates?.lon ?? lon.value ?? 116.40;
  lat.value = record?.coordinates?.lat ?? lat.value ?? 39.90;
  // 2.设置中心点
  point.value = new BMap.Point(116.40, 39.90);
  console.log("🚀 ~ initMap ~ point.value:", point.value, unref(map));
  // // // 3.设置级别
  unref(map).centerAndZoom(point.value, 15);
  // // 4.开启鼠标滚轮缩放功能。仅对PC上有效
  unref(map).enableScrollWheelZoom();
  const myIcon = new BMap.Icon(positionIcon, new BMap.Size(23, 25));
  marker.value = new BMap.Marker(point.value, myIcon);
  unref(map).addOverlay(marker.value); //添加一个标注
  // 打开信息窗口
  upInfoWindow();
  // 点击地图事件
  unref(map).addEventListener("click", function (e: any) {
    lon.value = e.point.lng;
    lat.value = e.point.lat;
    point.value = new BMap.Point(lon.value, lat.value);
    unref(map).centerAndZoom(point.value, 15);
    upInfoWindow();
    const gc = new BMap.Geocoder();
    gc.getLocation(point.value, (rs: any) => {
      if (rs && rs.address) {
        address.value = rs.address;
      }
    });
  });
};

// 打开信息窗口
const upInfoWindow = () => {
  const opts = {
    width: 250, // 信息窗口宽度
    height: 120, // 信息窗口高度
    title: "经纬度", // 信息窗口标题
  };
  console.log(unref(address));
  const word = `<div>地址:${unref(address)}</div>
                  <div>经度:${unref(lon)}</div>
                  <div>纬度:${unref(lat)}</div>`;
  const infoWindow = new BMap.InfoWindow(word, opts); // 创建信息窗口对象
  unref(map).openInfoWindow(infoWindow, point.value); // 打开信息窗口
  // 使信息窗口多次打开
  unref(marker).addEventListener("click", function () {
    unref(map).openInfoWindow(infoWindow, point.value);
  });
};

// 对搜索框中的文字进行逆解析
function geocoderLocation() {
  point.value = new BMap.Point(lon.value, lat.value);
  unref(map).centerAndZoom(unref(point), 15);
  unref(map).addOverlay(new BMap.Marker(unref(point)));
  const gc = new BMap.Geocoder();
  gc.getLocation(unref(point), (rs: any) => {
    console.log(rs);
    if (rs && rs.address) {
      address.value = rs.address;
      upInfoWindow();
    }
  });
}

// 搜索框改变事件
const handleSearch = () => {
  if (searchValue) {
    const value = searchValue.value.trim();
    //创建地址解析器实例
    const myGeo = new BMap.Geocoder();
    // 将地址解析结果显示在地图上,并调整地图视野
    myGeo.getPoint(
      value,
      async (point: any) => {
        if (point) {
          lon.value = point.lng;
          lat.value = point.lat;
          await geocoderLocation();
        } else {
          message({ type: "warning", message: "您选择的地址没有解析到结果!" });
        }
      },
      value
    );
  } else {
    message({ type: "warning", message: "搜索框不能为空" });
  }
};

// 浏览器定位
function browserPosition() {
  loading.value = true;
  const geolocation = new BMap.Geolocation();
  geolocation.getCurrentPosition((r: any) => {
    if (r) {
      (lon.value = r.point.lng), (lat.value = r.point.lat), geocoderLocation();
    } else {
      message({ type: "error", message: "定位失败,请手动输入经纬度" });
    }
    loading.value = false;
  });
}

//销毁地图实例
const destroyMap = () => {
  if (map.value) {
    // 清除地图上的所有标注、图层等
    map.value.clearOverlays();
    // 将 map 设为 null
    map.value = null;
    console.log("地图实例已销毁");
  }
};

//确认回调
const handleOk = () => {
  destroyMap();
  emits("selectAddress", {
    address: address.value,
    lon: lon.value,
    lat: lat.value,
  });
};
</script>

<style scoped lang="less">
.positionbox {
  width: 77vw;
  height: 64vh;
  margin-top: 20px;
}
</style>

组件压缩包链接如下:

链接: https://pan.baidu.com/s/1ZNuE8nJ0L93lKYeny0-NPQ?pwd=9527 提取码: 9527 复制这段内容后打开百度网盘手机App,操作更方便哦

实现效果如下

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐