Vue3地图选点组件

发布时间:2024年01月03日

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>


文章来源:https://blog.csdn.net/qq_42825643/article/details/135369325
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。