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;
							 |