Vue3实现地图选点组件的示例代码

 更新时间:2024年01月04日 11:51:26   作者:liyfn  
这篇文章主要为大家详细介绍了Vue3实现地图选点组件的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

Vue3地图选点组件

实现代码

<template>
  <div style="width: 100%; height: 500px">
    <div class="search-container">
      <el-autocomplete
        v-model="suggestionKeyWord"
        class="search-container__input"
        clearable
        :fetch-suggestions="searchSuggestions"
        placeholder="输入关键字搜索"
        @select="onSuggestionChoose"
      >
        <template #default="{ item }">
          <div class="value">{{ item.name }}</div>
          <span class="link">{{ item.address }}</span>
        </template>
      </el-autocomplete>
      <el-button type="primary" class="search-container__button" @click="doneMap"> 确定 </el-button>
    </div>
    <div class="map-body">
      <div id="container" class="map-body__left"></div>
      <img :class="iconClass" :src="markerSrc" alt="" />
      <!-- poi數據 -->
      <div class="map-body__right ele-map-picker-poi-list">
        <div
          v-for="(poi, index) in poiData"
          :key="index"
          :class="[
            'ele-map-picker-poi-item',
            { 'ele-map-picker-poi-item-active': index === chooseIndex },
          ]"
          @click="choose(index)"
        >
          <el-icon class="ele-map-picker-poi-item-icon el-icon-location-outline"
            ><Location
          /></el-icon>
          <!-- <icon-ep-location class="ele-map-picker-poi-item-icon el-icon-location-outline" /> -->
          <div class="ele-map-picker-poi-item-title">{{ poi.name }}</div>
          <div v-if="poi.address" class="ele-map-picker-poi-item-address">
            {{ poi.address }}
          </div>
          <el-icon v-if="index === chooseIndex" class="ele-map-picker-poi-item-check"
            ><Check
          /></el-icon>
          <!-- <icon-park-check-small
            v-if="index === chooseIndex"
            class="ele-map-picker-poi-item-check"
          /> -->
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
  import { onMounted } from 'vue';
  import AMapLoader from '@amap/amap-jsapi-loader';
  import markerSrc from '@/assets/images/location.png';
  import type { Poi } from './type';

  // const props = defineProps({});
  const emit = defineEmits(['done-map']);

  // 中心点位置
  let location: any = reactive([116.4074, 39.9042]);
  // 地图缩放比例
  const chooseZoom = 15;

  // 搜索关键字
  const suggestionKeyWord = ref('');
  // 搜索建议列表
  let suggestionData = reactive([]);
  // 地图实例
  let map: any;
  // 输入建议实例
  let autoComplete = reactive({});
  // 选中的建议
  let chooseSuggestion = reactive<any>({});
  // 地图中心标记点
  let centerMarker = reactive({});
  // poi检索实例
  let placeSearch = reactive({});
  // poi检索的数据
  const poiData = ref<Poi[]>([]);
  // 选中的数据
  const chooseIndex = ref<any>(null);
  // 是否是点击poi列表移动地图
  let isSelMove = false;
  // 图标是否显示跳动动画
  const showIconAnim = ref(false);

  const iconClass = computed(() => {
    return ['ele-map-picker-main-icon', { 'ele-map-picker-anim-bounce': showIconAnim.value }];
  });

  /**
   * @description: 初始化地图
   * @param {*} local
   * @return {*}
   */
  const initMap = (local: any) => {
    AMapLoader.load({
      key: 'xxxxxxxxxxxxx', // 申请好的Web端开发者Key,首次调用 load 时必填
      version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
      plugins: ['AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.AutoComplete'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
    }).then((AMap) => {
      map = new AMap.Map('container', {
        zoom: chooseZoom,
        center: location,
      });

      // 输入建议实例
      autoComplete = new AMap.AutoComplete({
        city: '全国',
      });

      // marker实例
      centerMarker = new AMap.Marker({
        icon: new AMap.Icon({
          image: markerSrc,
          size: new AMap.Size(26, 36.5),
          imageSize: new AMap.Size(26, 36.5),
        }),
        offset: new AMap.Pixel(-13, -36.5),
      });
      addMarker(location[0], location[1]);

      // 获取poi检索实例
      placeSearch = new AMap.PlaceSearch({
        type: '', // poi检索兴趣点类别
        pageSize: 30, // poi检索每页数量
        pageIndex: 1,
        extensions: 'all',
      });

      // 地图加载完成事件
      map.on('complete', () => {
        chooseIndex.value = null;
        const center = map.getCenter();
        searchNearBy(center.lat, center.lng, true);
      });

      // 地图移动结束事件
      map.on('moveend', () => {
        const center = map.getCenter();
        addMarker(center.lng, center.lat);
        if (isSelMove) {
          // poi列表点击的移动
          isSelMove = false;
        } else {
          // 拖动或搜索建议的移动
          showIconAnim.value = false;
          nextTick(() => {
            setTimeout(() => {
              showIconAnim.value = true;
            }, 0);
          });
          searchNearBy(center.lat, center.lng);
        }
      });
    });
  };
  /**
   * @description: poi检索
   * @param {*} lat
   * @param {*} lng
   * @param {*} force
   * @return {*}
   */
  const searchNearBy = (lat: any, lng: any) => {
    if (!placeSearch) {
      return;
    }
    // this.poiLoading = true;
    placeSearch.searchNearBy('', [lng, lat], 1000, (status: any, result: any) => {
      // this.poiLoading = false;
      if (status === 'complete') {
        const data = result.poiList.pois.filter((p: any) => p.location !== undefined);
        if (chooseSuggestion) {
          // 如果选中的搜索建议不在poi列表中则添加
          if (data.length === 0 || data[0].name !== chooseSuggestion.name) {
            data.unshift({ ...chooseSuggestion });
          }
          chooseSuggestion = null;
        } else {
          chooseIndex.value = null;
        }

        poiData.value = data;
        // v3.17 标准地址库-地址拼接省市区
        poiData.value.forEach((item) => {
          item.pname = item.pname || '';
          item.cityname = item.cityname || '';
          item.adname = item.adname || '';
          item.address = item.address || '';
          item.address = `${item.pname}${item.cityname}${item.adname}${item.address}`;
        });
      }
    });
  };

  /**
   * @description: poi列表选中
   * @param {*} index
   * @return {*}
   */
  const choose = (index: number) => {
    chooseIndex.value = index;
    isSelMove = true;
    // this.showIconAnim = false;
    // nextTick(() => {
    //     setTimeout(() => {
    //         this.showIconAnim = true;
    //     }, 0);
    // });
    const point = poiData.value[index].location;
    map.setZoomAndCenter(chooseZoom, [point.lng, point.lat]);
  };

  /**
   * @description: 添加marker
   * @param {*} lng
   * @param {*} lat
   * @return {*}
   */
  const addMarker = (lng: string, lat: string) => {
    // centerMarker.setMap(map);
    centerMarker.setPosition([lng, lat]);
    map.add(centerMarker);
  };

  /**
   * @description: 获取搜索数据
   * @param {*} keywords
   * @param {*} callback
   * @return {*}
   */
  const searchSuggestions = (keywords: string, callback: any) => {
    if (!keywords) {
      return callback(suggestionData);
    }
    autoComplete.search(keywords, (status: any, result: any) => {
      if (status === 'complete') {
        suggestionData = result.tips.filter((item) => item.location);

        suggestionData.forEach((item: any) => {
          item.address = item.address || '';
          item.district = item.district || '';
          item.address = `${item.district}${item.address}`;
        });
        callback(suggestionData);
      }
    });
  };

  /**
   * @description: 点击选择
   * @param {*} item
   * @return {*}
   */
  const onSuggestionChoose = (item: any) => {
    suggestionKeyWord.value = item.name;
    chooseSuggestion = item;
    chooseIndex.value = 0;

    const point = item.location;
    if (point) {
      map.setZoomAndCenter(chooseZoom, [point.lng, point.lat]);
      addMarker(point.lng, point.lat);
    }
  };

  /**
   * @description: 确定
   * @return {*}
   */
  const doneMap = () => {
    // 地图中心点
    // const center = { ...map.getCenter() };
    // getByLatLng({ lat: center.lat, lng: center.lng }).then((res) => {
    //   // console.log('接口获取的值', res);
    //   if (res.result) {
    //     location = {
    //       country: res.result?.country?.i18nName,
    //       province: res.result?.province?.i18nName || '',
    //       city: res.result?.city?.i18nName,
    //       district: res.result?.district?.i18nName,
    //       address: res.result.raw?.formattedAddress,
    //       lat: center.lat,
    //       lng: center.lng,
    //     };
    //   }
    //   // 选中则取高德地图返回的address
    //   if (chooseIndex.value || chooseIndex.value === 0) {
    //     location.address = poiData.value[chooseIndex.value].address || '';
    //   }
    //   suggestionKeyWord.value = '';
    //   emit('done-map', location);
    // });

    // TODO 由于数据规范性,需获取经纬度后重新请求三级地址
    if (chooseIndex.value || chooseIndex.value === 0) {
      location.address = poiData.value[chooseIndex.value].address || '';
    }
    console.log('选中的地址', location);
    suggestionKeyWord.value = '';
    emit('done-map', location);
  };

  onMounted(() => {
    setTimeout(() => {
      initMap(location);
    }, 200);
  });
</script>

<style scoped lang="scss">
  #container {
    margin: 0;
    padding: 0;
    width: 100%;
    height: calc(100% - 50px);
  }
  .search-container {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
    :deep(.el-autocomplete) {
      width: 80%;
    }
  }
  .map-body {
    display: flex;
    height: 450px;
    &__left {
      width: 70% !important;
      height: 100% !important;
    }
    &__right {
      flex: 1;
    }
  }

  /* 地图图标跳动动画 */
  .ele-map-picker-anim-bounce {
    animation: elePickerAnimBounce 500ms;
    animation-direction: alternate;
  }

  @keyframes elePickerAnimBounce {
    0%,
    60%,
    75%,
    90%,
    to {
      transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    }
    0%,
    to {
      transform: translate3d(0, 0, 0);
    }
    25% {
      transform: translate3d(0, -10px, 0);
    }
    50% {
      transform: translate3d(0, -20px, 0);
    }
    75% {
      transform: translate3d(0, -10px, 0);
    }
  }

  .ele-map-picker-main-icon {
    width: 26px;
    position: absolute;
    left: 50%;
    bottom: 50%;
    margin-left: -13px;
  }

  /* poi列表 */
  .ele-map-picker-poi-list {
    overflow: auto;
    width: 300px;
  }
  .ele-map-picker-poi-item {
    position: relative;
    padding: 8px 30px 8px 44px;
    border-bottom: 1px solid hsl(0deg 0% 60% / 15%);
    cursor: pointer;
  }
  .ele-map-picker-poi-item:hover {
    background-color: hsl(0deg 0% 60% / 5%);
  }
  .ele-map-picker-poi-item-icon {
    position: absolute;
    top: 50%;
    left: 14px;
    transform: translateY(-50%);
    font-size: 20px;
    opacity: 0.4;
  }
  .ele-map-picker-poi-item-title {
    font-size: 14px;
  }
  .ele-map-picker-poi-item-address {
    margin-top: 2px;
    font-size: 12px;
    opacity: 0.6;
  }
  .ele-map-picker-poi-item .ele-map-picker-poi-item-check {
    position: absolute;
    top: 50%;
    right: 7px;
    display: none;
    font-size: 16px;
    color: #3b74ff;
    transform: translateY(-50%);
  }
  .ele-map-picker-poi-item-active .ele-map-picker-poi-item-check {
    display: block;
  }
</style>
<style lang="scss">
  .map-body {
    .amap-icon {
      display: none;
    }
  }
</style>

到此这篇关于Vue3实现地图选点组件的示例代码的文章就介绍到这了,更多相关Vue3地图选点组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue 实现拖动滑块验证功能(只有css+js没有后台验证步骤)

    Vue 实现拖动滑块验证功能(只有css+js没有后台验证步骤)

    这篇文章给大家介绍了基于vue实现拖动滑块验证功能,代码引用css与js都是线上的,将代码全部复制到一个html中可以直接打开,超级简单,感兴趣的朋友跟随脚本之家小编一起看看吧
    2018-08-08
  • vue+element ui表格添加多个搜索条件筛选功能(前端查询)

    vue+element ui表格添加多个搜索条件筛选功能(前端查询)

    这篇文章主要给大家介绍了关于vue+element ui表格添加多个搜索条件筛选功能的相关资料,最近在使用element-ui的表格组件时,遇到了搜索框功能的实现问题,需要的朋友可以参考下
    2023-08-08
  • Vue中 Vue.prototype使用详解

    Vue中 Vue.prototype使用详解

    本文将结合实例代码,介绍Vue中 Vue.prototype使用,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • vue项目打包:修改dist文件名方式

    vue项目打包:修改dist文件名方式

    这篇文章主要介绍了vue项目打包:修改dist文件名方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • element如何初始化组件功能详解

    element如何初始化组件功能详解

    Element UI是一套基于Vue的桌面端组件库,封装好了很多常用的UI组件,下面这篇文章主要给大家介绍了关于element如何初始化组件功能的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • Vue动画事件详解及过渡动画实例

    Vue动画事件详解及过渡动画实例

    通过 Vue.js 的过渡系统,可以在元素从 DOM 中插入或移除时自动应用过渡效果。Vue.js 会在适当的时机为你触发 CSS 过渡或动画,你也可以提供相应的 JavaScript 钩子函数在过渡过程中执行自定义的 DOM 操作
    2019-02-02
  • Vue如何用this.$set改变数组里的某个值

    Vue如何用this.$set改变数组里的某个值

    这篇文章主要介绍了Vue用this.$set改变数组里的某个值,文中通过示例代码介绍了vue中this.$set()的用法----更新数组和对象的值,需要的朋友可以参考下
    2022-12-12
  • vue-router 源码实现前端路由的两种方式

    vue-router 源码实现前端路由的两种方式

    这篇文章主要介绍了vue-router 源码实现前端路由的两种方式,主要介绍Hash 路由和History 路由,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • 在Vant的基础上实现添加表单验证框架的方法示例

    在Vant的基础上实现添加表单验证框架的方法示例

    这篇文章主要介绍了在Vant的基础上实现添加验证框架的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 解决antd日期选择组件,添加value就无法点击下一年和下一月问题

    解决antd日期选择组件,添加value就无法点击下一年和下一月问题

    这篇文章主要介绍了解决antd日期选择组件,添加value就无法点击下一年和下一月问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10

最新评论