vue-baidu-map-3x 使用记录

在 Vue3 + TypeScript 项目中,为了采用 标签组件 的方式,使用百度地图组件,冲浪发现了一个开源库 ovo,很方便!喜欢的朋友记得帮 原作者 点下 star ~

vue-baidu-map-3xbaidu-map的vue3/vue2版本(支持v2.0、v3.0和webGl api)我全都有。同时也是vue2-baidu-map的文档https://map.heifahaizei.com/doc/index.html

目录

快速上手

全局注册

局部注册

注意事项

错误示例

正确示例

输入框搜索点位,并定位至该点位

实现效果

引入地图组件

[为什么不采用 BmAutoComplete?](#为什么不采用 BmAutoComplete?)

[使用 BmControl 实现自定义控件](#使用 BmControl 实现自定义控件)

定义响应式变量

关于初始化变量的踩坑

[添加地图初始化方法 ready](#添加地图初始化方法 ready)

监听外部组件传入地址的监变化

[ready 方法逻辑](#ready 方法逻辑)

获取地址搜索结果列表

[使用百度地图 API 检索跨域](#使用百度地图 API 检索跨域)

获取搜索结果列表方法

执行定位

[使用 nextTick 修改数据](#使用 nextTick 修改数据)

点位弹窗信息

展示搜索点位的周边点位弹窗

实现效果

引入地图组件

辐射圆、海量点组件

控制弹框在地图范围内显示

接收弹框组件的周边点位数据,并显示

【一个我很迷惑的报错】多个页面引用地图组件时,路由报错


快速上手

全局注册

一次性引入 百度地图组件库 的所有组件

TypeScript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import BaiduMap from 'vue-baidu-map-3x'

const app = createApp(App);

app.use(BaiduMap, {
  // ak 是在百度地图开发者平台申请的密钥 详见 http://lbsyun.baidu.com/apiconsole/key */
  ak: '百度地图ak',
  // v:'2.0',  // 默认使用3.0
  // type: 'WebGL' // ||API 默认API  (使用此模式 BMap=BMapGL)
});
app.mount('#app');

局部注册

TypeScript 复制代码
<template>
  <baidu-map class="map" ak="BaiduMapAK" v="3.0" type="API" :center="{lng: 116.404, lat: 39.915}" :zoom="15">
  </baidu-map>
</template>

<script setup>
import { BaiduMap } from 'vue-baidu-map-3x'
</script>

<style>
.map {
  width: 100%;
  height: 300px;
}
</style>

注意事项

  • 百度地图容器 必须 定义高度:BaiduMap 组件容器本身是一个空的块级元素,如果容器不定义高度,百度地图将渲染在一个高度为 0 不可见的容器内
  • 百度地图容器 必须 定义 center、zoom:没有设置 center 和 zoom 属性的地图组件是不进行地图渲染的。当 center 属性为合法地名字符串时例外,因为百度地图会根据地名自动调整 zoom 的值
  • 在 ready 中执行地图 API 加载后的代码,不能在 onMounted 中执行:由于百度地图 JS API 只有 JSONP 一种加载方式,因此 BaiduMap 组件及其所有子组件的渲染只能是异步的。因此,请使用在组件的 ready 事件来执行地图 API 加载完毕后才能执行的代码,不要试图在 vue 自身的生命周期中调用 BMap 类,更不要在这些时机修改 model 层

错误示例

TypeScript 复制代码
<script setup>
import {ref,onMounted} from 'vue';

const center = ref({lng: 0, lat: 0});
const zoom = ref(3);

onMounted(() => {
  center.value.lng = 116.404;
  center.value.lat = 39.915;
  zoom.value = 15;
});
</script>

正确示例

TypeScript 复制代码
<script setup>
import {ref,onMounted} from 'vue';

const center = ref({lng: 0, lat: 0});
const zoom = ref(3);

const handler = ({BMap, map}) => {
  console.log(BMap, map);
  center.value.lng = 116.404;
  center.value.lat = 39.915;
  zoom.value = 15;
}
</script>

输入框搜索点位,并定位至该点位

实现效果

二次封装地图组件:

  • 该组件可以被 编辑、查看、其他页面 进行引入
  • 该组件可以接收指定的值(地点名称、经纬度等等)
  • 点击 确认按钮 后,搜索显示结果列表
  • 点击 结果列表的某项 后,定位到该点,并让该点居中展示

接收搜索字段,显示搜索结果列表:

点击结果列表,执行定位,展示点位信息:

引入地图组件

先来看看完整代码

TypeScript 复制代码
    <baidu-map
      class="ths-map"
      :zoom="mapZoom"
      :center="mapCenter"
      @ready="ready"
    >
      <!-- 比例尺控件 -->
      <bm-scale anchor="BMAP_ANCHOR_TOP_RIGHT"></bm-scale>
      <!-- 缩放控件 -->
      <bm-navigation anchor="BMAP_ANCHOR_TOP_RIGHT"></bm-navigation>
      <!-- 定位控件 -->
      <bm-geolocation anchor="BMAP_ANCHOR_BOTTOM_RIGHT" :show-address-bar="true" :auto-location="true"></bm-geolocation>

      <!-- 自定义控件 -->
      <bm-control class="map-search-bm-control">
        <!-- 手写输入框 -->
        <div class="flex map-search-container">
          <el-input v-model="keyword" class="map-search-input" @input="getSearchList"></el-input>
          <el-button type="primary" class="map-search-btn" @click="getSearchList">
            搜索
          </el-button>
        </div>
        <!-- 搜索列表 -->
        <div v-if="searchListVisible" class="map-search-list">
          <div
            v-for="(elem, eIndex) of searchList"
            :key="eIndex"
            class="map-search-item"
            @click="performPositioning(elem)"
          >
            <p class="map-search-item-title">
              {{ elem.name }}
            </p>
            <p class="map-search-item-addr">
              {{ elem.address }}
            </p>
          </div>
        </div>
      </bm-control>

      <!-- 点 https://map.heifahaizei.com/doc/overlay/marker.html -->
      <bm-marker
        v-if="isShowSearchPoint"
        :position="{ lng: searchPoint.location.lng, lat: searchPoint.location.lat }"
        @click="spInfoWindow.show = true"
      >
      </bm-marker>

      <!-- 搜索点位弹窗 https://map.heifahaizei.com/doc/overlay/info-window.html#%E5%B1%9E%E6%80%A7 -->
      <bm-info-window
        :show="spInfoWindow.show"
        :title="spInfoWindow.name"
        :offset="{ width: 0, height: -24 }"
        :position="{ lng: spInfoWindow.location.lng, lat: spInfoWindow.location.lat }"
        :width="0"
        :max-width="300"
        @close="spInfoWindow.show = false"
        @open="spInfoWindow.show = true"
      >
        <!-- {{ spInfoWindow }} -->
        <!-- 搜索点位地址 -->
        <p class="sp-info-window-addr">
          {{ spInfoWindow.spName }}:<br />
          {{ spInfoWindow.spAddress }}
        </p>
        <div class="sp-info-window-header">
          <!-- 搜索点位名称 -->
          <!-- <span class="sp-info-window-name">{{ spInfoWindow.spName }}</span> -->
          <span class="sp-info-window-around" @click="infoWinGoAround"> 搜周边 </span>
        </div>
      </bm-info-window>
    </baidu-map>

为什么不采用 BmAutoComplete?

这个需求使用 官网组件 bm-auto-complete 能实现,不采用是基于以下几个原因:

  • UI 定制效果太差了,无法 深度定制 输入框样式(使用 el-input 组件会出现 bug,貌似只能采用原生 input 组件)
  • 控制 请求发送时机 不方便(文字发生变化直接就请求了,有的时候希望点了确认按钮再请求)
  • 无法自定义请求结果列表

使用 BmControl 实现自定义控件

为了解决上一个问题,采用了 BmControl

定义响应式变量

关于初始化变量的踩坑

这些变量 尽量 一开始就写好数据结构:

  • 比如 mapCenter 里,一开始就应该定义 lng、lat,后面具体的值可以是 0 state.mapCenter = { lng: 0, lat: 0}
  • 如果直接初始化成 state.mapCenter = {},会导致发生不可预知的错误

当然这并不是绝对的:

  • 我最开始没初始化 单个点位 searchPoint 的对象结构时,没报错
  • 我最开始没初始化 点位信息弹窗 spInfoWindow 的对象结构时,就报错找不到 lng 属性了
TypeScript 复制代码
    const state = reactive({
      // 地图缩放级别
      mapZoom: 11,
      // 地图中心点
      mapCenter: {
        lng: 113.88402,
        lat: 22.555259,
      },
      // 是否展示搜索点位
      isShowSearchPoint: false,
      // 搜索点位信息
      searchPoint: {
        lng: 0,
        lat: 0,
      } as any,
      // 搜索点位弹窗信息
      spInfoWindow: {
        show: false,
        spName: '',
        spAddress: '',
        location: {
          lng: 0,
          lat: 0,
        },
      } as any,
      // 搜索关键字
      keyword: '',
      // 搜索列表
      searchList: [] as any[],
      // 搜索列表是否可见
      searchListVisible: false,
    });

初始化方法可以在 ready 函数中执行,不能在 onMounted 中执行

TypeScript 复制代码
    /**
     * 页面初始化
     */
    const initPage = () => {
      // 是否展示搜索点位
      state.isShowSearchPoint = false;
      // 搜索点位信息
      state.searchPoint = {} as any;
      // 搜索点位弹窗信息
      state.spInfoWindow = {
        show: false,
        spName: '',
        spAddress: '',
        location: {
          lng: 0,
          lat: 0,
        },
      };
      // 搜索列表
      state.searchList = [];
      // 搜索列表是否可见
      state.searchListVisible = false;
    };

添加地图初始化方法 ready

监听外部组件传入地址的监变化

外部传入的地址,会被赋值给 state 中的变量,防止 props 被修改

TypeScript 复制代码
    watch(
      () => props.complaintsAddr,
      () => {
        state.keyword = props.complaintsAddr;
        // 关闭周边搜索弹框
        state.aroundDialogVisible = false;
      },
    );

ready 方法逻辑

props.complaintsAddr 是供其他页面传入 地址 的字段:

  • 如果接收到了这个字段,则直接调用百度地图 API 接口,获取搜索结果列表,执行定位
  • 如果没接收到这个字段,则执行初始化,调整 state 中的变量,隐藏不该出现的内容
TypeScript 复制代码
    const ready = async (e: any) => {
      if (props.complaintsAddr) {
        await getSearchList();
        for (const elem of state.searchList) {
          if (elem.address === props.complaintsAddr || elem.name === props.complaintsAddr) {
            performPositioning(elem);
            break;
          }
        }
      } else {
        // 页面初始化
        await initPage();
      }
    };

    onMounted(async () => {
      /*
       *  页面初始化
       * await initPage();
       */
    });

获取地址搜索结果列表

使用百度地图 API 检索跨域

地点检索 | 百度地图API SDK

本地调用百度接口,会出现跨域问题,这是正常的,使用 nginx 代理本地发送的请求

TypeScript 复制代码
        location /mapapi/ {
            proxy_pass   http://api.map.baidu.com/;
           add_header Access-Control-Allow-Origin *;
        }

获取搜索结果列表方法

TypeScript 复制代码
    /**
     * 获取搜索列表
     */
    const getSearchList = async () => {
      state.searchList = [];
      try {
        const res = await getInputList(state.keyword, '宝安区');
        if (res.data.message === 'ok') {
          state.searchList = res.data?.results;
          // 展示搜索列表
          state.searchListVisible = true;
        }
      } catch (err) {
        console.error('地图 搜索列表 接口请求失败', err);
        state.searchList = [];
        state.searchListVisible = true;
      }
    };

执行定位

使用 nextTick 修改数据

单个点 BmMaker 组件,使用 v-if 进行控制,否则一开始没点搜索结果的时候,就会直接渲染

++使用 nextTick,修改点位坐标数据、中心点数据、点位弹框信息++,原因如下:

  • 如果不使用 nextTick,会导致 点位弹框信息 报错 ------ 获取不到 img 这个 DOM 元素
  • 也就是说,点位弹框信息 必须在 点位存在 的时候,才能渲染

选择结果列表后,应该把选中的结果信息发出去,告诉外面使用地图的组件

TypeScript 复制代码
    /**
     * 执行定位
     * @param elem 点位信息,必传
     * @param isCloseSearchList 是否关闭搜索列表,菲必传
     */
    const performPositioning = async (elem: any) => {
      // console.log('执行定位 ---', elem);

      // 隐藏列表
      state.searchListVisible = false;

      // 修改关键字信息
      state.keyword = elem.address ? elem.address : elem.name;
      // 更新外部组件的关键字信息
      emit('search-point-info', elem);

      // 渲染点位
      nextTick(() => {
        // 修改中心点坐标,把当前搜索点,设置为中心点
        state.mapCenter.lng = elem.location.lng;
        state.mapCenter.lat = elem.location.lat;

        // 修改搜索点位坐标
        state.searchPoint = elem;
        // 展示搜索点位
        state.isShowSearchPoint = true;
        console.log('搜索点位的经纬度 ---', state.searchPoint);

        // 设置点位弹窗信息
        showInfoWindow(elem);
      });
    };

点位弹窗信息

TypeScript 复制代码
    /**
     * 展示点位弹窗
     * @param elem 点位信息
     */
    const showInfoWindow = (elem: any) => {
      // 设置点位弹窗信息
      state.spInfoWindow.spName = elem.name; // 标题
      state.spInfoWindow.spAddress = elem.address; // 地址
      state.spInfoWindow.location = elem.location; // 弹框坐标

      // 展示点位信息弹框
      state.spInfoWindow.show = true;

      // 修改搜索点位坐标
      state.searchPoint = elem;

      state.mapCenter = elem.location;

      console.log('展示点位弹窗 ---', state.spInfoWindow);
    };

展示搜索点位的周边点位弹窗

实现效果

  • 点击搜索点位的去周边后,出现弹框
  • 弹窗只能在地图中显示,不能在整个屏幕中显示
  • 搜索到周边点位后,地图绘制周边距离(辐射圆),并把所有点位进行打点

引入地图组件

辐射圆、海量点组件

TypeScript 复制代码
      <!-- 添加-多个点 https://map.heifahaizei.com/doc/overlay/point-collection.html -->
      <bm-point-collection
        :points="points"
        shape="BMAP_POINT_SHAPE_STAR"
        color="red"
        size="BMAP_POINT_SIZE_SMALL"
      >
      </bm-point-collection>

      <!-- 圆 https://map.heifahaizei.com/doc/overlay/circle.html -->
      <bm-circle
        :center="circlePath.center"
        :radius="circlePath.radius"
        stroke-color="blue"
        :stroke-opacity="0.5"
        :stroke-weight="2"
        :editing="false"
        :mass-clear="true"
      ></bm-circle>

控制弹框在地图范围内显示

el-dialog 只能在全局内显示,所以:手写 div 当弹框,让弹框跟地图点位同级,通过绝对定位实现

接收弹框组件的周边点位数据,并显示

注意:辐射圆单位是 m,画太小了,页面就显示不出辐射圆了(......)

TypeScript 复制代码
    /**
     * 地图多个点集合
     */
    const addPoints = (elems: any) => {
      const pointAll = [];
      for (const workSite of elems.workSiteResult) {
        const position = { lng: workSite.longitude, lat: workSite.latitude };
        pointAll.push(position);
      }

      state.points = pointAll;
      // 填充辐射圆半径
      state.circlePath.radius = elems.kilometerDistance * 1000;
      // 填充辐射圆中心坐标
      state.circlePath.center = elems.location;
    };

【一个我很迷惑的报错】多个页面引用地图组件时,路由报错

我在查看、编辑页都引用了地图组件,就会出现下面的报错

后来发现,只要我把地图、地图组件内部的组件,再 cv 一份出来,分别引入到对应的页面,才能解决报错

如下所示,3 中的 edit、view 会引用地图组件,如果 3 中的 edit、view 同时引用了 1 或者同时引用了 2,那就会报错

3 中的 edit 引用 1,view 引用 2,就不会报错

相关推荐
baiduopenmap13 天前
LBS 开发微课堂|通过openGL ES轻松实现建筑物渲染及动画
前端·百度地图
baiduopenmap1 个月前
百度世界2024精选公开课:基于地图智能体的导航出行AI应用创新实践
前端·人工智能·百度地图
程序喵D4 个月前
百度地图SDK Android版开发 7 覆盖物示例1
android·百度地图
程序喵D4 个月前
百度地图SDK Android版开发 6 显示覆盖物
android·百度地图
进阶的巨人0018 个月前
百度地图实现3d发光建筑群
前端·3d·百度地图·建筑群
java_强哥8 个月前
html接入百度地图
前端·html·百度地图
小果子^_^9 个月前
vue集成百度地图,实现关键字搜索并自定义覆盖物,保存成静态图片
前端·vue.js·百度地图
Hi202402179 个月前
通过更新路书当前坐标下marker的icon来展示沿途的风景
百度地图·路书
秩沅9 个月前
【unity接入SDK案例】从0到1 如何接入百度地图SDK到unity中【二】
android·unity·游戏引擎·百度地图
爱敲代码的小旗9 个月前
html 如何引入 百度地图
vue·百度地图