首先,先创建一个地图页面,用于显示地图组件,我是在pages文件中创建了一个mapSearch组件。
然后在routes.ts中注册页面。
{
path: '/mapSearch',
name: 'mapSearch',
icon: 'smile',
component: './mapSearch',
},
第三步就是使用高德地图来创建地图。
关键步骤:
1. 初始化状态和引用
- 使用了
useRef
和useState
钩子初始化了一些状态:mapContainerRef
: 引用地图容器,用于在页面中渲染地图。mapRef
: 引用地图实例和标记,用于在后续操作中修改地图或标记。geocoder
: 用于逆地理编码,转换坐标到实际地址。mapLoaded
: 用于标记地图是否加载完成。placeSearchRef
: 用于搜索地址的插件实例。latitude
和longitude
: 存储当前地图中心的纬度和经度。form
: Ant Design 的表单实例,用于表单的操作和数据绑定。
2. 加载高德地图 API
- 在
useEffect
中加载高德地图 API:- 通过判断
window.AMap
是否已加载,如果已经加载则直接初始化相关插件(AMap.PlaceSearch
和AMap.Geocoder
),并将它们存储在placeSearchRef
和geocoder
状态中。 - 如果
window.AMap
未加载,动态加载地图脚本,并在脚本加载完成后初始化插件。
- 通过判断
3. 地图初始化和点击事件
- 在另一个
useEffect
中初始化地图:new window.AMap.Map
: 创建并渲染地图实例。new window.AMap.Marker
: 创建一个标记点,显示在地图上,标记当前的位置(默认位置为北京的经纬度)。- 监听地图的点击事件 (
map.on('click')
),用户点击地图时会更新标记位置和地图中心,同时更新表单中的坐标。 - 使用
geocoder.getAddress
获取逆地理编码信息,填充地址到表单中。
4. 地址搜索功能
handleSearch
:当用户在输入框中输入地址并点击搜索时,执行以下操作:- 获取用户输入的地址值。
- 使用
placeSearchRef.search
进行地址搜索,返回结果后获取第一个地点(POI)的坐标。 - 更新地图和标记的位置,并使用逆地理编码获取该地点的完整地址,更新到表单中。
5. 表单项
- 在页面中渲染了一个表单:
- 地址搜索框 (
<Input.Search />
):允许用户输入地址并点击搜索。 - 经纬度输入框:用户可以直接输入经纬度来定位地图,输入后会更新地图的中心点和标记位置。
- 地址搜索框 (
6. 地图渲染
mapContainerRef
:用来显示地图的容器。地图会渲染在这里,容器的大小和样式由style
属性控制。
代码如下:
import React from 'react';
import { useEffect, useState, useRef } from 'react';
import { message, Input } from 'antd';
import { Form } from 'antd';
// 地图页面 搜索地图的组件
const mapSearch: React.FC = () => {
const mapContainerRef = useRef<HTMLDivElement>(null);
const mapRef = useRef<any>(null);
const [geocoder, setGeocoder] = useState<any>(null);
const [mapLoaded, setMapLoaded] = useState(false);
const [placeSearchRef, setPlaceSearchRef] = useState<any>(null);
const [latitude, setLatitude] = useState<number>(39.9042); // 默认纬度:北京
const [longitude, setLongitude] = useState<number>(116.4074); // 默认经度:北京
const [form] = Form.useForm();
// 加载高德地图 API
useEffect(() => {
window._AMapSecurityConfig = { securityJsCode: '填写你的密钥' };
if (window.AMap) {
setMapLoaded(true);
window.AMap.plugin(['AMap.PlaceSearch', 'AMap.Geocoder'], () => {
setPlaceSearchRef(
new window.AMap.PlaceSearch({
pageSize: 5,
city: '全国',
}),
);
const geo = new window.AMap.Geocoder({
radius: 1000, // 查询半径,单位:米
extensions: 'all', // 返回更多详情
});
setGeocoder(geo);
});
} else {
const script = document.createElement('script');
script.src = 'https://webapi.amap.com/maps?v=1.4.15&key=填写你的key';
script.async = true;
script.onload = () => {
setMapLoaded(true);
window.AMap.plugin(['AMap.PlaceSearch', 'AMap.Geocoder'], () => {
setPlaceSearchRef(
new window.AMap.PlaceSearch({
pageSize: 5,
city: '全国',
}),
);
const geo = new window.AMap.Geocoder({
radius: 1000,
extensions: 'all',
});
setGeocoder(geo);
});
};
script.onerror = () => {
message.error('高德地图 API 加载失败');
};
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}
}, []);
// 初始化或更新地图
useEffect(() => {
if (mapLoaded && mapContainerRef.current) {
const map = new window.AMap.Map(mapContainerRef.current, {
center: [longitude, latitude], // 定位到北京
zoom: 15,
});
const marker = new window.AMap.Marker({
map,
position: [longitude, latitude],
});
// 在初始化地图的useEffect中修改点击事件处理
map.on('click', (e: any) => {
const { lat, lng } = e.lnglat;
// 更新marker与中心
marker.setPosition([lng, lat]);
map.setCenter([lng, lat]);
// 更新状态和表单
setLatitude(lat);
setLongitude(lng);
// 逆地理编码获取地址
if (geocoder) {
geocoder.getAddress([lng, lat], (status: string, result: any) => {
if (status === 'complete' && result.regeocode) {
const addr = result.regeocode.formattedAddress;
form.setFieldsValue({
address: addr,
lat: lat,
lng: lng,
});
} else {
form.setFieldsValue({
lat: lat,
lng: lng,
});
message.error('获取地址失败');
}
});
} else {
form.setFieldsValue({
lat: lat,
lng: lng,
});
}
});
mapRef.current = { map, marker };
}
}, [mapLoaded, longitude, latitude, geocoder]);
// 地址搜索
const handleSearch = () => {
const address = form.getFieldValue('address');
if (!address) {
message.warning('请输入地址');
return;
}
if (!placeSearchRef || !placeSearchRef.search) {
message.error('搜索插件尚未初始化,请稍后再试');
return;
}
placeSearchRef.search(address, (status: string, result: any) => {
if (status === 'complete' && result.poiList.pois.length) {
const selectedPoi = result.poiList.pois[0];
const { lat, lng } = selectedPoi.location;
setLatitude(lat);
setLongitude(lng);
// 使用逆地理编码获取完整地址
geocoder.getAddress([lng, lat], (status: string, result: any) => {
if (status === 'complete' && result.regeocode) {
const addr = result.regeocode.formattedAddress;
form.setFieldsValue({
address: addr, // 使用完整地址而不是POI名称
lat,
lng,
});
if (mapRef.current) {
mapRef.current.map.setCenter([lng, lat]);
mapRef.current.marker.setPosition([lng, lat]);
}
message.success('定位成功');
}
});
} else {
message.error('未找到该地址');
}
});
};
return (
<div>
<Form form={form}>
<div>
<Form.Item name="address">
<Input.Search
placeholder="搜索地址"
enterButton="搜索"
style={{ width: 400 }}
onSearch={handleSearch}
/>
</Form.Item>
</div>
<div style={{ display: 'flex', gap: 16 }}>
<Form.Item name="lng">
<Input
placeholder="经度"
onChange={(e) => {
const value = parseFloat(e.target.value);
if (!isNaN(value)) {
setLongitude(value);
if (mapRef.current) {
mapRef.current.map.setCenter([value, latitude]);
mapRef.current.marker.setPosition([value, latitude]);
}
}
}}
style={{ width: 180 }}
/>
</Form.Item>
<Form.Item name="lat">
<Input
placeholder="纬度"
onChange={(e) => {
const value = parseFloat(e.target.value);
if (!isNaN(value)) {
setLatitude(value);
if (mapRef.current) {
mapRef.current.map.setCenter([longitude, value]);
mapRef.current.marker.setPosition([longitude, value]);
}
}
}}
style={{ width: 180 }}
/>
</Form.Item>
</div>
</Form>
<div
ref={mapContainerRef}
style={{ height: 500, border: '1px solid #ddd', marginBottom: 16 }}
></div>
</div>
);
};
export default mapSearch;
关键步骤总结:
- 加载高德地图 API 和插件:确保地图和插件已正确加载。
- 初始化地图:创建地图实例,并为地图添加点击事件处理器。
- 处理地址搜索:用户输入地址后,进行搜索并更新地图位置。
- 更新经纬度输入框和标记:用户在输入框中修改经纬度时,地图和标记同步更新。
这些步骤协同工作,提供了一个动态的地图交互界面,用户可以通过搜索地址或直接输入经纬度来改变地图上的标记位置。