| 
									
										
										
										
											2025-10-23 16:52:53 +08:00
										 |  |  |  | import { Button, Col, Form, Input, Modal, Row, Spin } from "antd"; | 
					
						
							|  |  |  |  | import { useEffect, useRef, useState } from "react"; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-28 15:16:58 +08:00
										 |  |  |  | /** | 
					
						
							|  |  |  |  |  * 定位组件弹窗 | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | const MapSelector = (props) => { | 
					
						
							|  |  |  |  |   const { | 
					
						
							|  |  |  |  |     visible, | 
					
						
							|  |  |  |  |     onClose, | 
					
						
							|  |  |  |  |     longitude, | 
					
						
							|  |  |  |  |     latitude, | 
					
						
							|  |  |  |  |     onConfirm, | 
					
						
							|  |  |  |  |   } = props; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-23 16:52:53 +08:00
										 |  |  |  |   const mapContainerRef = useRef(null); | 
					
						
							|  |  |  |  |   const mapInstanceRef = useRef(null); | 
					
						
							|  |  |  |  |   const [loading, setLoading] = useState(false); | 
					
						
							|  |  |  |  |   const [currentLongitude, setCurrentLongitude] = useState(longitude || ""); | 
					
						
							|  |  |  |  |   const [currentLatitude, setCurrentLatitude] = useState(latitude || ""); | 
					
						
							|  |  |  |  |   const [localSearch, setLocalSearch] = useState(""); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 当外部经纬度变化时,更新内部状态
 | 
					
						
							|  |  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |  |     setCurrentLongitude(longitude || ""); | 
					
						
							|  |  |  |  |     setCurrentLatitude(latitude || ""); | 
					
						
							|  |  |  |  |   }, [longitude, latitude]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 初始化地图
 | 
					
						
							|  |  |  |  |   const initMap = async () => { | 
					
						
							|  |  |  |  |     if (!window.BMapGL) { | 
					
						
							|  |  |  |  |       console.error("BMapGL is not loaded"); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     setLoading(true); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 确保DOM已经渲染
 | 
					
						
							|  |  |  |  |     await new Promise(resolve => setTimeout(resolve, 100)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (mapContainerRef.current) { | 
					
						
							|  |  |  |  |       // 只有在没有地图实例时才创建新地图
 | 
					
						
							|  |  |  |  |       if (!mapInstanceRef.current) { | 
					
						
							|  |  |  |  |         const map = new window.BMapGL.Map(mapContainerRef.current); | 
					
						
							|  |  |  |  |         mapInstanceRef.current = map; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         map.centerAndZoom( | 
					
						
							|  |  |  |  |           new window.BMapGL.Point( | 
					
						
							|  |  |  |  |             longitude || "119.69457721306945", | 
					
						
							|  |  |  |  |             latitude || "39.940504336846665", | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |           16, | 
					
						
							|  |  |  |  |         ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         map.enableScrollWheelZoom(true); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 如果有初始坐标,添加标记
 | 
					
						
							|  |  |  |  |         if (longitude && latitude) { | 
					
						
							|  |  |  |  |           const point = new window.BMapGL.Point(longitude, latitude); | 
					
						
							|  |  |  |  |           const marker = new window.BMapGL.Marker(point); | 
					
						
							|  |  |  |  |           map.addOverlay(marker); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 添加点击事件
 | 
					
						
							|  |  |  |  |         map.addEventListener("click", (event) => { | 
					
						
							|  |  |  |  |           map.clearOverlays(); | 
					
						
							|  |  |  |  |           const point = new window.BMapGL.Point(event.latlng.lng, event.latlng.lat); | 
					
						
							|  |  |  |  |           const marker = new window.BMapGL.Marker(point); | 
					
						
							|  |  |  |  |           map.addOverlay(marker); | 
					
						
							|  |  |  |  |           setCurrentLatitude(event.latlng.lat); | 
					
						
							|  |  |  |  |           setCurrentLongitude(event.latlng.lng); | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       setLoading(false); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 搜索功能
 | 
					
						
							|  |  |  |  |   const handleLocalSearch = () => { | 
					
						
							|  |  |  |  |     if (localSearch && mapInstanceRef.current) { | 
					
						
							|  |  |  |  |       const local = new window.BMapGL.LocalSearch(mapInstanceRef.current, { | 
					
						
							|  |  |  |  |         renderOptions: { map: mapInstanceRef.current }, | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       local.search(localSearch); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 关闭弹窗
 | 
					
						
							|  |  |  |  |   const handleClose = () => { | 
					
						
							|  |  |  |  |     setLocalSearch(""); | 
					
						
							|  |  |  |  |     if (onClose) | 
					
						
							|  |  |  |  |       onClose(); | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 确认选择
 | 
					
						
							|  |  |  |  |   const handleConfirm = () => { | 
					
						
							|  |  |  |  |     if (onConfirm) { | 
					
						
							|  |  |  |  |       onConfirm(currentLongitude, currentLatitude); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     handleClose(); | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 监听visible变化
 | 
					
						
							|  |  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |  |     let initTimer; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (visible) { | 
					
						
							|  |  |  |  |       // 延迟初始化地图,确保DOM完全渲染
 | 
					
						
							|  |  |  |  |       initTimer = setTimeout(() => { | 
					
						
							|  |  |  |  |         initMap(); | 
					
						
							|  |  |  |  |       }, 100); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return () => { | 
					
						
							|  |  |  |  |       if (initTimer) { | 
					
						
							|  |  |  |  |         clearTimeout(initTimer); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   }, [visible]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const handleAfterClose = () => { | 
					
						
							|  |  |  |  |     if (mapInstanceRef.current) { | 
					
						
							|  |  |  |  |       try { | 
					
						
							|  |  |  |  |         mapInstanceRef.current.clearOverlays(); | 
					
						
							|  |  |  |  |         mapInstanceRef.current.destroy(); | 
					
						
							|  |  |  |  |         mapInstanceRef.current = null; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       catch (e) { | 
					
						
							|  |  |  |  |         console.warn("Error destroying map on unmount:", e); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       mapInstanceRef.current = null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 组件卸载时清理地图
 | 
					
						
							|  |  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |  |     return () => { | 
					
						
							|  |  |  |  |       handleAfterClose(); | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   }, []); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return ( | 
					
						
							|  |  |  |  |     <Modal | 
					
						
							|  |  |  |  |       open={visible} | 
					
						
							|  |  |  |  |       title="坐标" | 
					
						
							|  |  |  |  |       onCancel={handleClose} | 
					
						
							|  |  |  |  |       onOk={handleConfirm} | 
					
						
							|  |  |  |  |       width={800} | 
					
						
							|  |  |  |  |       destroyOnHidden={false} | 
					
						
							|  |  |  |  |       afterClose={handleAfterClose} | 
					
						
							|  |  |  |  |     > | 
					
						
							|  |  |  |  |       <Form labelAlign="right" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}> | 
					
						
							|  |  |  |  |         <Row gutter={24}> | 
					
						
							|  |  |  |  |           <Col span={12}> | 
					
						
							|  |  |  |  |             <Form.Item label="关键字搜索"> | 
					
						
							|  |  |  |  |               <Input | 
					
						
							|  |  |  |  |                 value={localSearch} | 
					
						
							|  |  |  |  |                 onChange={e => setLocalSearch(e.target.value)} | 
					
						
							|  |  |  |  |                 allowClear | 
					
						
							|  |  |  |  |               /> | 
					
						
							|  |  |  |  |             </Form.Item> | 
					
						
							|  |  |  |  |           </Col> | 
					
						
							|  |  |  |  |           <Col span={12}> | 
					
						
							|  |  |  |  |             <Form.Item label=" " colon={false}> | 
					
						
							|  |  |  |  |               <Button type="primary" onClick={handleLocalSearch}> | 
					
						
							|  |  |  |  |                 搜索 | 
					
						
							|  |  |  |  |               </Button> | 
					
						
							|  |  |  |  |             </Form.Item> | 
					
						
							|  |  |  |  |           </Col> | 
					
						
							|  |  |  |  |         </Row> | 
					
						
							|  |  |  |  |         <Row gutter={24}> | 
					
						
							|  |  |  |  |           <Col span={12}> | 
					
						
							|  |  |  |  |             <Form.Item label="经度"> | 
					
						
							|  |  |  |  |               <Input disabled value={currentLongitude} /> | 
					
						
							|  |  |  |  |             </Form.Item> | 
					
						
							|  |  |  |  |           </Col> | 
					
						
							|  |  |  |  |           <Col span={12}> | 
					
						
							|  |  |  |  |             <Form.Item label="纬度"> | 
					
						
							|  |  |  |  |               <Input disabled value={currentLatitude} /> | 
					
						
							|  |  |  |  |             </Form.Item> | 
					
						
							|  |  |  |  |           </Col> | 
					
						
							|  |  |  |  |         </Row> | 
					
						
							|  |  |  |  |       </Form> | 
					
						
							|  |  |  |  |       <div | 
					
						
							|  |  |  |  |         ref={mapContainerRef} | 
					
						
							|  |  |  |  |         style={{ width: "100%", height: "500px", position: "relative" }} | 
					
						
							|  |  |  |  |       > | 
					
						
							|  |  |  |  |         <Spin size="large" tip="地图正在加载中..." spinning={loading}> | 
					
						
							|  |  |  |  |           <div style={{ | 
					
						
							|  |  |  |  |             position: "absolute", | 
					
						
							|  |  |  |  |             top: 0, | 
					
						
							|  |  |  |  |             left: 0, | 
					
						
							|  |  |  |  |             right: 0, | 
					
						
							|  |  |  |  |             bottom: 0, | 
					
						
							|  |  |  |  |             display: "flex", | 
					
						
							|  |  |  |  |             justifyContent: "center", | 
					
						
							|  |  |  |  |             alignItems: "center", | 
					
						
							|  |  |  |  |             backgroundColor: "rgba(0, 0, 0, 0.5)", | 
					
						
							|  |  |  |  |             zIndex: 1000, | 
					
						
							|  |  |  |  |           }} | 
					
						
							|  |  |  |  |           /> | 
					
						
							|  |  |  |  |         </Spin> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     </Modal> | 
					
						
							|  |  |  |  |   ); | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export default MapSelector; |