210 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
| import { Button, Col, Form, Input, Modal, Row, Spin } from "antd";
 | ||
| import { useEffect, useRef, useState } from "react";
 | ||
| 
 | ||
| /**
 | ||
|  * 定位组件弹窗
 | ||
|  */
 | ||
| const MapSelector = (props) => {
 | ||
|   const {
 | ||
|     visible,
 | ||
|     onClose,
 | ||
|     longitude,
 | ||
|     latitude,
 | ||
|     onConfirm,
 | ||
|   } = props;
 | ||
| 
 | ||
|   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;
 |