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