205 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
		
			
		
	
	
			205 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
|  | import { Button, Col, Form, Input, Modal, Row, Spin } from "antd"; | |||
|  | import { useEffect, useRef, useState } from "react"; | |||
|  | 
 | |||
|  | const MapSelector = ({ | |||
|  |                        visible, | |||
|  |                        onClose, | |||
|  |                        longitude, | |||
|  |                        latitude, | |||
|  |                        onConfirm, // 新增确认回调
 | |||
|  |                      }) => { | |||
|  |   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; |