- 安装依赖
pnpm install @amap/amap-jsapi-loader --save
- 实现组件
ReAMap, 由useAMap.ts和index.vue构成
功能:通过传输经纬度,渲染展示地图。 代码如下
① useAMap.ts
import { shallowRef } from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";
export function useAMap() {
const map = shallowRef(null);
const initMap = async (container: string | HTMLElement, options?: object) => {
try {
const AMap = await AMapLoader.load({
key: "xxxx", // 生产环境建议将 Key 存储在配置文件中
version: "2.0",
plugins: []
});
map.value = new AMap.Map(container, {
zoom: 11,
center: [116.39, 39.9],
...options
});
} catch (error) {
console.error("地图加载失败:", error);
}
};
const destroyMap = () => {
if (map.value) {
map.value.destroy();
map.value = null;
}
};
return { map, initMap, destroyMap };
}
②index.vue
<template>
<div ref="mapContainer" class="re-amap-container" />
</template>
<script setup lang="ts">
import {
ref,
onMounted,
onUnmounted,
defineProps,
withDefaults,
defineExpose,
watch
} from "vue";
import { useAMap } from "./useAMap";
// 定义 emits
const emit = defineEmits(["map-click"]);
// 定义 props
interface Props {
options?: object;
markerOptions?: object;
infoWindow?: object;
}
const props = withDefaults(defineProps<Props>(), {
options: () => ({}),
markerOptions: null,
infoWindow: null
});
const mapContainer = ref<HTMLElement | null>(null);
const { map, initMap, destroyMap } = useAMap();
// 监听地图实例创建
watch(map, newMap => {
if (newMap) {
// 绑定地图点击事件
newMap.on("click", (e: any) => {
emit("map-click", e.lnglat);
});
// 处理标记点和信息窗
if (props.markerOptions) {
const AMap = (window as any).AMap;
const marker = new AMap.Marker(props.markerOptions);
newMap.add(marker);
if (props.infoWindow) {
const infoWindow = new AMap.InfoWindow(props.infoWindow);
marker.on("click", () => {
infoWindow.open(newMap, marker.getPosition());
});
}
}
}
});
onMounted(() => {
if (mapContainer.value) {
initMap(mapContainer.value, props.options);
}
});
onUnmounted(() => {
destroyMap();
});
// 将 map 实例暴露给父组件
defineExpose({
map
});
</script>
<style scoped>
.re-amap-container {
width: 100%;
height: 100%;
}
</style>
- 使用组件
省略了一部分代码,只贴主要的关键代码,对地图组件有什么操作,通过ref去控制即可
<script lang="ts" setup>
import { ref } from "vue";
const amapRef = ref(null);
const mapOptions = {
center: [116.39, 39.9],
zoom: 16
};
const markerOptions = {
position: [116.39, 39.9],
title: "xxx"
};
const infoWindowOptions = {
isCustom: false, // 使用高德默认样式
autoMove: true, // 点击标记点后自动平移地图,使信息窗完整显示
offset: [11, -60], // 设置信息窗的偏移量
content:
'<div style="color: #333; font-size: 14px; padding: 10px;">地址:xxxx</div>'
};
const handleMapClick = lnglat => {
console.log("地图点击位置:", lnglat);
};
</script>
<template>
<div class="h-[173px] w-[526px]">
<ReAMap
ref="amapRef"
:options="mapOptions"
:marker-options="markerOptions"
:info-window="infoWindowOptions"
@map-click="handleMapClick"
/>
</div>
</template>
实现效果可参考下图
背景介绍
铛铛~ 今天有一版优化,之前是通过传输经纬度,进行渲染展示地图。
今天的需求是,只给地址,根据地址去获取地理编码。
除了pnpm install @amap/amap-jsapi-loader --save 外,还要用到安全密钥去使用AMap.Geocoder,(新版高德 JS API 都强制要求)。
AMap.Geocoder地理编码与逆地理编码服务,用于地址描述与经纬度坐标之间的转换,可以通过回调函数获取查询结果。
正向地理编码: 比如在搜索页面,用户输入的是某个地址描述信息,然后调用正向地理编码接口,将地址描述信息转换成地理坐标(经纬度)
逆向地理编码:比如在某个地图页面,用户可以通过点选地图选择点,然后调用头逆地理编码接口,将地理坐标(经纬度)转换成地址描述信息
new AMap.Geocoder(opts: GeocoderOptions)
GeocoderOptions参数介绍 参数名 默认值 描述 city "全国" 城市,地理编码时,设置地址描述所在城市可选值:城市名(中文或中文全拼)、citycode、adcode radius 1000 逆地理编码时,以给定坐标为中心点,单位:米,取值范围:0 - 3000 lang "zh_cn" 设置语言类型 可选值:zh_cn(中文)、en(英文) batch - 是否批量查询,batch 设置为 false 时,只返回第一条记录 extensions "base" 逆地理编码时,返回信息的详略 base:返回基本地址信息,all:返回地址信息及附近poi、道路、道路交叉口等信息
GeocoderOptions参数介绍
| 参数名 | 默认值 | 描述 |
|---|---|---|
| city | "全国" | 城市,地理编码时,设置地址描述所在城市,可选值:城市名(中文或中文全拼)、citycode、adcode |
| radius | 1000 | 逆地理编码时,以给定坐标为中心点,单位:米,取值范围:0 - 3000 |
| lang | "zh_cn" | 设置语言类型,可选值:zh_cn(中文)、en(英文) |
| batch | - | 是否批量查询,batch 设置为 false 时,只返回第一条记录 |
| extensions | "base" | 逆地理编码时,返回信息的详略:base 返回基本地址信息;all 返回地址信息及附近 poi、道路、道路交叉口等信息 |
方法介绍
| 方法名 | 说明 | 参数值描述 |
|---|---|---|
| getLocation(keyword, cbk) | 将地址信息转化为高德经纬度坐标信息 | keyword (String):关键字;cbk (GeocoderCallback):回调函数 |
| getAddress(location, cbk) | 将高德经纬度坐标信息转化为结构化的地址信息 | location (LngLat | Array):给定坐标;cbk (ReGeocoderCallback):回调函数 |
安全密钥在高德控制台复制,和key是配套的,见下图。
引入安全密钥
一定要在使用import AMapLoader from "@amap/amap-jsapi-loader";之前引入
① 可以在main.ts (src/main.ts)中引入
// 高德地图安全密钥配置 (重要:必须在 AMapLoader.load 之前设置)
(window as any)._AMapSecurityConfig = {
securityJsCode: "xxx" // 安全密钥
};
② 也可以在自己封装的方法顶部引入。
实现组件 ReAMap, 依然由useAMap.ts和 index.vue构成
① useAMap.ts
// 高德地图安全密钥配置 (重要:必须在 AMapLoader.load 之前设置)
(window as any)._AMapSecurityConfig = {
securityJsCode: "xxx"
};
import { shallowRef } from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";
/**
* @description 高德地图组合式函数
*/
export function useAMap() {
// shallowRef 用于存储地图实例,避免深层响应式带来的性能问题
const map = shallowRef(null);
/**
* @description 初始化地图
* @param container 地图容器的 DOM 元素或 ID
* @param options 地图配置项
*/
const initMap = async (container: string | HTMLElement, options?: object) => {
try {
// 异步加载高德地图 JSAPI
const AMap = await AMapLoader.load({
key: "xxx", // Web端开发者Key
version: "2.0", // 指定要加载的 JSAPI 的版本
plugins: ["AMap.Geocoder"] // 需要使用的插件列表,例如:地理编码插件
});
// 创建地图实例
map.value = new AMap.Map(container, {
zoom: 11, // 默认缩放级别
center: [116.39, 39.9], // 默认中心点
...options // 合并外部传入的配置
});
} catch (error) {
console.error("地图加载失败:", error);
}
};
/**
* @description 销毁地图实例
*/
const destroyMap = () => {
if (map.value) {
map.value.destroy();
map.value = null;
}
};
return { map, initMap, destroyMap };
}
②index.vue
<template>
<div ref="mapContainer" class="re-amap-container" />
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, defineExpose } from "vue";
import { useAMap } from "./useAMap";
// 定义组件接收的 props
const props = defineProps({
// 用于地址解析模式的地址字符串
address: String,
// 地图初始化时的高级配置项
options: Object,
// (模式1) 手动传入的标记点配置
markerOptions: Object,
// (模式2) 手动传入的信息窗体配置
infoWindow: Object
});
// 用于挂载地图容器的 DOM 引用
const mapContainer = ref<HTMLElement | null>(null);
// 从组合式函数中获取地图实例和控制方法
const { map, initMap, destroyMap } = useAMap();
/**
* @description 根据地址字符串渲染地图标记点
* @param address 详细地址
*/
const renderAddressMarker = (address: string) => {
const AMap = (window as any).AMap;
// 确保 AMap 和地图实例都已加载
if (!AMap || !map.value) return;
// 渲染前清除旧的覆盖物
map.value.clearMap();
// 实例化地理编码服务
const geocoder = new AMap.Geocoder({ city: "全国" });
// 调用 getLocation 将地址转换为经纬度
geocoder.getLocation(address, (status, result) => {
if (status === "complete" && result.info === "OK") {
const loc = result.geocodes[0].location;
const pos = [loc.lng, loc.lat];
// 设置地图中心点
map.value.setCenter(pos);
// 创建并添加标记点
const marker = new AMap.Marker({ position: pos });
map.value.add(marker);
// 创建并配置信息窗体
const infoWindow = new AMap.InfoWindow({
isCustom: false,
autoMove: true,
offset: [0, -30],
content: `<div style="color: #333; font-size: 14px; padding: 10px;">地址:${address}</div>`
});
// 绑定标记点点击事件,打开信息窗体
marker.on("click", () => infoWindow.open(map.value, marker.getPosition()));
// 默认直接打开信息窗体
infoWindow.open(map.value, marker.getPosition());
} else {
console.error("高德地图地址解析失败:", { address, status, result });
}
});
};
/**
* @description 根据手动传入的 markerOptions 渲染标记点 (兼容旧模式)
*/
const renderOptionsMarker = () => {
const AMap = (window as any).AMap;
if (!AMap || !map.value || !props.markerOptions) return;
map.value.clearMap();
const marker = new AMap.Marker(props.markerOptions);
map.value.add(marker);
if (props.infoWindow) {
const infoWindow = new AMap.InfoWindow(props.infoWindow);
marker.on("click", () => infoWindow.open(map.value, marker.getPosition()));
}
};
// 组件挂载后初始化地图,并根据 props 执行首次渲染
onMounted(async () => {
if (mapContainer.value) {
await initMap(mapContainer.value, props.options);
// 优先使用 address 模式
if (props.address) {
renderAddressMarker(props.address);
} else if (props.markerOptions) {
// 其次使用 markerOptions 兼容模式
renderOptionsMarker();
}
}
});
// 监听 address prop 的变化,以动态更新地图
watch(
() => props.address,
newAddr => {
if (map.value && newAddr) renderAddressMarker(newAddr);
}
);
// 监听 markerOptions prop 的变化 (兼容旧模式)
watch(
() => props.markerOptions,
() => {
// 仅在 address 模式不生效时,才启用此 watch
if (map.value && props.markerOptions && !props.address) {
renderOptionsMarker();
}
},
{ deep: true }
);
// 组件卸载时销毁地图实例,防止内存泄漏
onUnmounted(() => {
destroyMap();
});
// 将 map 实例暴露给父组件,允许父组件直接操作地图
defineExpose({
map
});
</script>
<style scoped>
.re-amap-container {
width: 100%;
height: 100%;
}
</style>
使用组件
<script setup lang="ts">
import ReAMap from "@/components/ReAMap/index.vue";
const amapRef = ref(null);
const companyAddressForMap = ref("");
// 自己写方法获取要的地址
</script>
<template>
<ReAMap ref="amapRef" :address="companyAddressForMap" />
</template>