开发一个 Vue3 的地图位置选择组件

效果预览

image-20210915220508075

源码

<template>
  <div id="amap-container" class="h-96 w-full"></div>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";
import { useGlobalSetting } from "@/hooks/setting";
import _ from "lodash";

// 页面 map 实例,在 AMapLoader.load 之后加载
var map = null;
// 全局 AMap 实例,在 AMapLoader.load 之后加载
var AMap = null;
// 内部经纬度保存对象
var innerValRef = ref<LngLat>(null);
// 位置指示标记
var marker = null;
// 初始位置。一开始就初始化,之所以不保存到 innerValRef 是因为此时 innerValRef 的监听中的操作尚未可用。
var initPos: LngLat;

export interface LngLat {
  lng: Number;
  lat: Number;
}

async function loadAmap() {
  try {
    await AMapLoader.load({
      key: useGlobalSetting ().amapKey, // 申请好的 Web 端开发者 Key,首次调用 load 时必填
      version: "1.4.15", 
      plugins: [], 
      AMapUI: {
        version: "1.1", 
        plugins: [],
      },
      Loca: {
        version: "1.3.2", 
      },
    });
    AMap = globalThis.AMap;
    map = new AMap.Map("amap-container", {
      zoom: 11, // 级别
      center: [initPos.lng, initPos.lat], // 中心点坐标
      viewMode: "3D", // 使用 3D 视图
    });
    map.on("click", handleClick);
    innerValRef.value = initPos;
  } catch (error) {
    console.log(error);
  }
}

var handleClick = (e) => {
  const pos: LngLat = { lng: e.lnglat.getLng(), lat: e.lnglat.getLat() };
  innerValRef.value = pos;
};

var setMarker = (pos: LngLat) => {
  if (marker != null) {
    map.remove(marker);
  }
  marker = new AMap.Marker({
    position: new AMap.LngLat(pos.lng, pos.lat),
    title: "位置",
  });
  map.add(marker);
  console.log("setMarker");
};


export default defineComponent({
  props: {
    lng: Number,
    lat: Number,
  },
  mounted() {
    loadAmap();
  },
  setup(props, { attrs, slots, emit }) {
    initPos = { lng: props.lng, lat: props.lat };
    watch(
      () => innerValRef.value,
      (cur, prev) => {
        setMarker(cur);
        emit("update:lng", cur.lng);
        emit("update:lat", cur.lat);
      }
    );

    watch(
      () => props.lng,
      (cur: any, prev) => {
        innerValRef.value.lng = cur;
        setMarker(innerValRef.value);
      }
    );

    watch(
      () => props.lat,
      (cur: any, prev) => {
        innerValRef.value.lat = cur;
        setMarker(innerValRef.value);
      }
    );

    return {};
  },
});
</script>

使用方法

      <n-form-item path="location" label="所在位置">
        <n-input-number
          :step="0.01"
          v-model:value="model.location.lng"
          placeholder="ModuleName"
          class="mr-2"
        />
        <n-input-number :step="0.01" v-model:value="model.location.lat" placeholder="ModuleName" />
      </n-form-item>
      <amap-location-selector
        v-model:lng="model.location.lng"
        v-model:lat="model.location.lat"
        class="mb-2"
      ></amap-location-selector>

原理

v-model:propName="xx" 相当于

  :propName="xx"
  @update:propName="xx = $event"

据此可进行双向绑定。