소스 검색

feat[pc-components]: map新增仅限规定市区内范围搜索参数

chenlei 1 년 전
부모
커밋
6b768eaa32

+ 85 - 30
packages/pc-components/src/components/DageMap/index.tsx

@@ -1,15 +1,22 @@
-import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
-import { Amap, Marker } from '@amap/amap-react';
-import { AutoComplete, Button, Input, Space } from 'antd';
-import { debounce } from 'lodash';
+import {
+  forwardRef,
+  useCallback,
+  useEffect,
+  useImperativeHandle,
+  useRef,
+  useState,
+} from "react";
+import { Amap, Marker } from "@amap/amap-react";
+import { AutoComplete, Button, Input, Space, message } from "antd";
+import { debounce, isNumber } from "lodash";
 import {
   AMapGeocoderGetAddressResult,
   DageMapMethods,
   DageMapPoisType,
   DageMapProps,
-} from './types';
-import { DageMapGeocoder } from './plugins';
-import { getAmapInputTips } from './utils';
+} from "./types";
+import { DageMapGeocoder } from "./plugins";
+import { getAmapInputTips } from "./utils";
 
 export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
   (
@@ -19,28 +26,43 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
       disabled,
       address,
       city,
+      onlyCityArea,
       inputTipsApi,
+      outCityAreaMessage,
       onAddressChange,
       onLngLatChange,
       onMapComplete,
     },
-    ref,
+    ref
   ) => {
     const mapRef = useRef<AMap.Map_2>();
     /** 是否禁用坐标解析地址 */
     const geocoderDisabled = useRef(false);
     const [loaded, setLoaded] = useState(false);
-    /** 经度 */
+    /** 上一个坐标地址 */
+    const prevPosition = useRef([longitude, latitude]);
+    /** 输入框经度 */
     const [lng, setLng] = useState<number>(longitude);
-    /** 纬度 */
+    /** 输入框纬度 */
     const [lat, setLat] = useState<number>(latitude);
     const [options, setOptions] = useState<DageMapPoisType[]>([]);
-    const position = useMemo(() => [longitude, latitude], [longitude, latitude]);
+    const [position, setPosition] = useState([longitude, latitude]);
 
     useImperativeHandle(ref, () => ({
       mapRef: mapRef.current,
     }));
 
+    useEffect(() => {
+      if (longitude === lng && latitude === lat) return;
+
+      // 外部改动经纬度,不需要解析地址
+      geocoderDisabled.current = true;
+      setPosition([longitude, latitude]);
+      setLat(latitude);
+      setLng(longitude);
+      prevPosition.current = [longitude, latitude];
+    }, [longitude, latitude]);
+
     const handleSearch = useCallback(
       debounce(async (value: string) => {
         if (!value) {
@@ -51,7 +73,7 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
         const { tips } = await getAmapInputTips(inputTipsApi, value, city);
         setOptions(tips || []);
       }, 500),
-      [city],
+      [city]
     );
 
     const handleClick = useCallback(
@@ -64,12 +86,13 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
 
         setLat(lat);
         setLng(lng);
+        setPosition([lng, lat]);
         onLngLatChange?.({
           lat,
           lng,
         });
       },
-      [disabled, onLngLatChange],
+      [disabled, onLngLatChange]
     );
 
     /**
@@ -91,20 +114,24 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
           lat,
           lng,
         });
+        setPosition(e._position);
       }, 200),
-      [onLngLatChange],
+      [onLngLatChange]
     );
 
     const handlePosition = useCallback(
       (long = lng, lati = lat) => {
         if (!long || !lati) return;
+
         onLngLatChange?.({
           lat: lati,
           lng: long,
         });
+        setPosition([long, lati]);
+
         mapRef.current?.setCenter([long, lati], false, 1000);
       },
-      [lng, lat, onLngLatChange],
+      [lng, lat, onLngLatChange]
     );
 
     /**
@@ -117,7 +144,7 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
 
     const handleSelect = useCallback(
       (val: string, opts: DageMapPoisType) => {
-        const [lng, lat] = opts.location.split(',').map((i) => Number(i));
+        const [lng, lat] = opts.location.split(",").map((i) => Number(i));
 
         setLng(lng);
         setLat(lat);
@@ -129,23 +156,42 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
           lat,
           lng,
         });
+
+        prevPosition.current = [lng, lat];
       },
-      [handlePosition, onLngLatChange],
+      [handlePosition, onLngLatChange]
     );
 
     const handleAutoCompleteChange = useCallback(
       (val: string) => {
         onAddressChange?.(val);
       },
-      [onAddressChange],
+      [onAddressChange]
     );
 
-    const handleGeocoderAddress = useCallback(
-      (res: AMapGeocoderGetAddressResult) => {
-        !geocoderDisabled.current && onAddressChange?.(res.regeocode.formattedAddress);
-      },
-      [onAddressChange],
-    );
+    const handleGeocoderAddress = (res: AMapGeocoderGetAddressResult) => {
+      if (geocoderDisabled.current) return;
+
+      if (
+        onlyCityArea &&
+        isNumber(city) &&
+        !res.regeocode.addressComponent.adcode.startsWith(
+          city.toString().substring(0, 2)
+        )
+      ) {
+        // 超出市区范围
+        outCityAreaMessage && message.error(outCityAreaMessage, 2);
+        setTimeout(() => {
+          setLat(prevPosition.current[1]);
+          setLng(prevPosition.current[0]);
+          handlePosition(prevPosition.current[0], prevPosition.current[1]);
+        }, 2000);
+        return;
+      }
+
+      onAddressChange?.(res.regeocode.formattedAddress);
+      prevPosition.current = [lng, lat];
+    };
 
     return (
       <>
@@ -167,7 +213,7 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
           </AutoComplete>
         </div>
 
-        <Space style={{ margin: '15px 0' }}>
+        <Space style={{ margin: "15px 0" }}>
           <Input
             readOnly={disabled}
             value={lng}
@@ -190,10 +236,19 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
         </Space>
 
         <div style={{ width: 650, height: 400 }}>
-          <Amap ref={mapRef} zoom={17} onComplete={handleComplete} onClick={handleClick}>
+          <Amap
+            ref={mapRef}
+            zoom={17}
+            onComplete={handleComplete}
+            onClick={handleClick}
+          >
             {loaded && position ? (
               <>
-                <Marker position={position} draggable={!disabled} onDragging={handleMarker} />
+                <Marker
+                  position={position}
+                  draggable={!disabled}
+                  onDragging={handleMarker}
+                />
 
                 {!disabled && (
                   <DageMapGeocoder
@@ -210,8 +265,8 @@ export const DageMap = forwardRef<DageMapMethods, DageMapProps>(
         </div>
       </>
     );
-  },
+  }
 );
 
-export * from './types';
-export * from './utils';
+export * from "./types";
+export * from "./utils";

+ 35 - 30
packages/pc-components/src/components/DageMap/plugins/Geocoder/index.ts

@@ -1,6 +1,6 @@
-import { useAmap, usePlugins } from '@amap/amap-react';
-import { FC, memo, useEffect, useRef } from 'react';
-import { AMapGeocoderGetAddressResult } from '../../types';
+import { useAmap, usePlugins } from "@amap/amap-react";
+import { FC, memo, useEffect, useRef } from "react";
+import { AMapGeocoderGetAddressResult } from "../../types";
 
 export interface DageMapGeocoderProps {
   position: number[];
@@ -8,35 +8,40 @@ export interface DageMapGeocoderProps {
   onChange?: (res: AMapGeocoderGetAddressResult) => void;
 }
 
-export const DageMapGeocoder: FC<DageMapGeocoderProps> = memo(({ city, position, onChange }) => {
-  const map = useAmap();
-  const AMap = usePlugins(['AMap.Geocoder']);
-  const geocoder = useRef<any>();
+export const DageMapGeocoder: FC<DageMapGeocoderProps> = memo(
+  ({ city, position, onChange }) => {
+    const map = useAmap();
+    const AMap = usePlugins(["AMap.Geocoder"]);
+    const geocoder = useRef<any>();
 
-  useEffect(() => {
-    if (!map || !AMap) return;
-    // @ts-ignore
-    geocoder.current = new AMap.Geocoder({
-      city,
-    });
-    map.add(geocoder.current);
+    useEffect(() => {
+      if (!map || !AMap) return;
+      // @ts-ignore
+      geocoder.current = new AMap.Geocoder({
+        city,
+      });
+      map.add(geocoder.current);
 
-    return () => {
-      map.remove(geocoder.current);
-    };
-  }, [map, AMap, city]);
+      return () => {
+        map.remove(geocoder.current);
+      };
+    }, [map, AMap, city]);
 
-  useEffect(() => {
-    geocoder.current &&
-      geocoder.current.getAddress(
-        position,
-        (status: string, result: AMapGeocoderGetAddressResult) => {
-          if (status === 'complete' && result.info === 'OK') {
-            onChange?.(result);
+    useEffect(() => {
+      geocoder.current &&
+        geocoder.current.getAddress(
+          position,
+          (status: string, result: AMapGeocoderGetAddressResult) => {
+            if (status === "complete" && result.info === "OK") {
+              onChange?.(result);
+            }
           }
-        },
-      );
-  }, [position, onChange]);
+        );
+    }, [position, onChange]);
 
-  return null;
-});
+    return null;
+  },
+  (prevProps, nextProps) =>
+    prevProps.position[0] === nextProps.position[0] &&
+    prevProps.position[1] === nextProps.position[1]
+);

+ 11 - 1
packages/pc-components/src/components/DageMap/types.ts

@@ -19,6 +19,16 @@ export interface DageMapProps {
    */
   city?: string | number;
   /**
+   * 仅限规定市区内范围搜索
+   * city 值为 number 类型生效
+   */
+  onlyCityArea?: boolean;
+  /**
+   * 超出市区范围提示语
+   * `onlyCityArea` 为 `true` 生效
+   */
+  outCityAreaMessage?: string;
+  /**
    * 输入提示调用的接口地址
    */
   inputTipsApi: string;
@@ -49,4 +59,4 @@ export interface AMapTipsResponse {
   tips: DageMapPoisType[];
 }
 
-export * from './plugins/Geocoder/types';
+export * from "./plugins/Geocoder/types";

+ 6 - 9
packages/pc-components/src/components/DageMap/utils.ts

@@ -1,5 +1,4 @@
 import { config as AmapReactConfig } from "@amap/amap-react";
-import { queryString } from "@dage/utils";
 import { requestByGet } from "@dage/service";
 import { AMapTipsResponse } from "./types";
 
@@ -34,17 +33,15 @@ export const setAmapJSKey = (key: string, serviceHost?: string) => {
  * @param citylimit 是否限制城市搜索 默认-true
  * @returns
  */
-export const getAmapInputTips = async (
+export const getAmapInputTips = (
   path: string,
   value: string,
   city?: string | number,
   citylimit: "true" | "false" = "true"
 ) => {
-  const data = await requestByGet<AMapTipsResponse>(
-    queryString.stringifyUrl({
-      url: path,
-      query: { keywords: value, city, citylimit },
-    })
-  );
-  return data;
+  return requestByGet<AMapTipsResponse>(path, {
+    keywords: value,
+    city,
+    citylimit,
+  });
 };