import * as React from 'react'; import { getUrlParam } from '../../utils/url'; import './style.scss'; // 扩展 Window 接口以支持 AMap declare global { interface Window { AMap: any; } } interface MapWidgetProps { // 经度 longitude?: number; // 纬度 latitude?: number; // 位置名称(支持地址搜索) locationName?: string; // 地图缩放级别 (3-18) zoom?: number; // 标记点标题 markerTitle?: string; // 地图高度 height?: number; data?: any; } interface MapWidgetState { isLoading: boolean; error?: string; currentAddress?: string; urlLocationName?: string; } /** * 地图展示组件 * 支持传入经纬度坐标或位置名称,展示对应的地图区域并标注当前位置 * 基于高德地图API实现 */ export default class MapWidget extends React.PureComponent< MapWidgetProps, MapWidgetState > { private map: any = null; private AMap: any = null; private marker: any = null; private mapContainer: string = 'map-widget-container'; private locationName?: string = ''; constructor(props: MapWidgetProps) { super(props); this.state = { isLoading: false, error: undefined, currentAddress: '', urlLocationName: getUrlParam('location') || props.locationName || '', }; } componentDidMount() { this.loadAMapScript(); // 监听 URL 变化 window.addEventListener('popstate', this.handleUrlChange); window.addEventListener('hashchange', this.handleUrlChange); } componentDidUpdate(prevProps: Readonly): void { const { longitude, latitude, locationName, zoom = 14 } = this.props; const { longitude: prevLongitude, latitude: prevLatitude, zoom: prevZoom = 14, locationName: prevLocationName, } = prevProps; if ( longitude !== prevLongitude || latitude !== prevLatitude || locationName !== prevLocationName || zoom !== prevZoom ) { this.initMap(locationName); } } componentWillUnmount() { // 移除 URL 监听器 window.removeEventListener('popstate', this.handleUrlChange); window.removeEventListener('hashchange', this.handleUrlChange); if (this.map) { this.map.destroy(); this.map = null; } } /** * 处理 URL 变化事件 */ handleUrlChange = () => { const newLocationName = getUrlParam('location'); // 如果 URL 中的 location 参数发生变化,则重新初始化地图 if (newLocationName !== this.locationName) { this.setState( { urlLocationName: newLocationName, error: undefined, }, () => { // 重新初始化地图 this.initMap(newLocationName); }, ); } }; /** * 动态加载高德地图脚本 */ loadAMapScript = () => { // 检查是否已加载 if (window.AMap) { this.AMap = window.AMap; this.initMap(); return; } this.setState({ isLoading: true, }); // 使用 Amis 部署的高德地图脚本: 使用企业内部部署的高德地图脚本,无需单独申请 API Key const scriptUrl = 'https://amisrs.ingageapp.com/base/js/amap_2.0.5.21.js'; // 备用方案:使用高德官方API(需要申请key) // const amapKey = 'your-amap-key-here'; // const scriptUrl = `https://webapi.amap.com/maps?v=2.0&key=${amapKey}&plugin=AMap.Geocoder,AMap.Marker`; const script = document.createElement('script'); script.type = 'text/javascript'; script.src = scriptUrl; script.async = true; script.onload = () => { this.AMap = window.AMap; this.initMap(); this.setState({ isLoading: false, }); }; script.onerror = () => { this.setState({ isLoading: false, error: '地图加载失败,请检查网络连接或配置地图API Key', }); }; document.head.appendChild(script); }; /** * 初始化地图 */ initMap = (_locationName?: string) => { const { longitude, latitude, locationName, zoom = 14 } = this.props; const { urlLocationName } = this.state; // 优先使用 URL 中的 location 参数 const finalLocationName = _locationName || urlLocationName || locationName; this.locationName = finalLocationName; // 如果提供了位置名称(URL 或 props),先进行地理编码 if (finalLocationName) { this.geocodeAddress(finalLocationName, zoom); } // 如果提供了经纬度,直接初始化地图 else if (longitude !== undefined && latitude !== undefined) { this.createMap([longitude, latitude], zoom); } // 默认显示北京天安门 else { this.createMap([116.397428, 39.90923], zoom); } }; /** * 地理编码:先将地址转换为经纬度,再创建地图 */ geocodeAddress = (address: string, zoom: number) => { if (!this.AMap) { return; } // 动态加载 Geocoder 插件 this.AMap.plugin('AMap.Geocoder', () => { const geocoder = new this.AMap.Geocoder(); geocoder.getLocation(address, (status: string, result: any) => { if (status === 'complete' && result.geocodes.length > 0) { const location = result.geocodes[0].location; this.createMap([location.lng, location.lat], zoom); this.setState({ currentAddress: result.geocodes[0].formattedAddress, }); } else { this.setState({ error: `地址解析失败: ${address}`, }); // 使用默认位置 this.createMap([116.397428, 39.90923], zoom); } }); }); }; /** * 创建地图实例 */ createMap = (center: [number, number], zoom: number) => { if (!this.AMap) { return; } try { // 创建地图实例 this.map = new this.AMap.Map(this.mapContainer, { zoom, center, resizeEnable: true, viewMode: '2D', }); // 添加标记点 this.addMarker(center); // 获取当前位置的地址信息 this.getAddressByLocation(center); } catch (error) { console.log('地图初始化失败 / error:', error); this.setState({ error: '地图初始化失败', }); } }; /** * 添加标记点 */ addMarker = (position: [number, number]) => { if (!this.AMap || !this.map) { return; } const { markerTitle } = this.props; // 移除已存在的标记 if (this.marker) { this.map.remove(this.marker); } // 创建自定义红色图标 const redIcon = new this.AMap.Icon({ size: new this.AMap.Size(50, 68), image: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png', imageSize: new this.AMap.Size(50, 68), }); // 创建标记点(红色) this.marker = new this.AMap.Marker({ position, title: markerTitle || '当前位置', map: this.map, anchor: 'bottom-center', icon: redIcon, }); // 点击标记显示信息窗体 this.marker.on('click', () => { const infoWindow = new this.AMap.InfoWindow({ content: `
${markerTitle || '当前位置'}
${ this.state.currentAddress || '正在获取地址...' }
`, offset: new this.AMap.Pixel(0, -30), }); infoWindow.open(this.map, position); }); }; /** * 逆地理编码:根据经纬度获取地址信息 */ getAddressByLocation = (location: [number, number]) => { if (!this.AMap) { return; } // 动态加载 Geocoder 插件 this.AMap.plugin('AMap.Geocoder', () => { const geocoder = new this.AMap.Geocoder(); geocoder.getAddress(location, (status: string, result: any) => { if (status === 'complete' && result.regeocode) { this.setState({ currentAddress: result.regeocode.formattedAddress, }); } }); }); }; render() { const { height = 400, data } = this.props; const { isLoading, error } = this.state; const curAmisData = data || {}; const systemInfo = curAmisData.__AmisSystemInfo || {}; console.log('this.props:', this.props); return (
{/* // 地图头部: 显示地图标题和当前位置地址

地图展示

{this.state.currentAddress && (
📍 {this.state.currentAddress}
)}
*/}
{isLoading && (
地图加载中...
)} {error && (
⚠️
{error}
)}
经度: {this.props.longitude || '-'} | 纬度: {this.props.latitude || '-'} | 缩放: {this.props.zoom || 14}
); } }