From be89bb996f63b74df9e3ef010a92d4a7dc34b60e Mon Sep 17 00:00:00 2001
From: 卢春龙 <lucl@xiaopeng.com>
Date: 星期三, 22 四月 2026 10:17:09 +0800
Subject: [PATCH] Merge branch 'feature_search_fix' into 'develop'

---
 src/robotaxi/pages/site-search/hooks/use-site-search.ts |  121 ++++++++++++++++++++++++++++++++--------
 1 files changed, 96 insertions(+), 25 deletions(-)

diff --git a/src/robotaxi/pages/site-search/hooks/use-site-search.ts b/src/robotaxi/pages/site-search/hooks/use-site-search.ts
index 8f8b302..a02364e 100644
--- a/src/robotaxi/pages/site-search/hooks/use-site-search.ts
+++ b/src/robotaxi/pages/site-search/hooks/use-site-search.ts
@@ -33,11 +33,19 @@
   const [isSearching, setIsSearching] = useState(false);
   // 最近一次完成搜索的关键词
   const [lastCompletedKeyword, setLastCompletedKeyword] = useState('');
+  // 初始化数据是否已完成加载
+  const [isInitLoaded, setIsInitLoaded] = useState(false);
   const searchSeqRef = useRef(0);
   const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
+  const pendingSearchValueRef = useRef('');
 
   // 拉取附近站点
-  const fetchNearbySites = async (lat: number, lon: number, electronicAreaId?: string) => {
+  const fetchNearbySites = async (
+    lat: number,
+    lon: number,
+    electronicAreaId?: string,
+    canUpdate?: () => boolean
+  ) => {
     const selectedCity = Taro.getStorageSync('selectedCity');
     try {
       const res = await getNearestSite({
@@ -46,9 +54,15 @@
         longitude: Number(lon),
         electronicAreaId
       });
+      if (canUpdate && !canUpdate()) {
+        return;
+      }
       const points = Array.isArray(res?.points) ? res.points : [];
       setNearbySiteList(points.map(normalizeSiteItem));
     } catch {
+      if (canUpdate && !canUpdate()) {
+        return;
+      }
       setNearbySiteList([]);
     }
   };
@@ -139,21 +153,55 @@
   // 搜索输入防抖
   const debouncedSearch = useMemo(() => {
     return (val: string) => {
+      pendingSearchValueRef.current = val;
       if (debounceTimerRef.current) {
         clearTimeout(debounceTimerRef.current);
       }
       debounceTimerRef.current = setTimeout(() => {
+        debounceTimerRef.current = null;
         searchSiteRef.current(val);
       }, 300);
     };
   }, []);
 
-  // 清空搜索结果列表
-  const clearSearchResults = () => {
+  // 取消搜索输入防抖定时器
+  const cancelDebouncedSearch = () => {
     if (debounceTimerRef.current) {
       clearTimeout(debounceTimerRef.current);
       debounceTimerRef.current = null;
     }
+    pendingSearchValueRef.current = '';
+  };
+
+  // 立即执行待触发的防抖搜索
+  const flushDebouncedSearch = () => {
+    if (!debounceTimerRef.current) {
+      return;
+    }
+    const pendingValue = pendingSearchValueRef.current;
+    clearTimeout(debounceTimerRef.current);
+    debounceTimerRef.current = null;
+    pendingSearchValueRef.current = '';
+    searchSiteRef.current(pendingValue);
+  };
+
+  // 确认搜索:优先使用显式关键字,其次使用防抖待执行关键字,最后回退到当前输入值
+  const confirmSearch = (val?: string) => {
+    const explicitValue = typeof val === 'string' ? val : '';
+    const pendingValue = pendingSearchValueRef.current;
+    const fallbackValue = deps.searchValue;
+    const targetValue = explicitValue || pendingValue || fallbackValue || '';
+    if (debounceTimerRef.current) {
+      clearTimeout(debounceTimerRef.current);
+      debounceTimerRef.current = null;
+    }
+    pendingSearchValueRef.current = '';
+    searchSiteRef.current(targetValue);
+  };
+
+  // 清空搜索结果列表
+  const clearSearchResults = () => {
+    cancelDebouncedSearch();
     // 推进请求序号,使清空前已发出的请求结果失效
     searchSeqRef.current += 1;
     setSiteList([]);
@@ -164,36 +212,55 @@
 
   useEffect(() => {
     return () => {
-      if (debounceTimerRef.current) {
-        clearTimeout(debounceTimerRef.current);
-      }
+      cancelDebouncedSearch();
     };
   }, []);
 
   // 初始化:拉取附近站点、历史记录、页面类型
   useEffect(() => {
-    const { pageType: type, latitude: lat, longitude: lon, electronicAreaId } = deps.router.params;
-    if (lat && lon) {
-      fetchNearbySites(Number(lat), Number(lon), electronicAreaId);
-    }
-    checkOtp(false).then(otp => {
-      if (otp && lat && lon) {
-        const payload = { lat: Number(lat), lon: Number(lon), electronicAreaId };
-        const handleSuccess = (res: any) => {
-          if (Array.isArray(res)) {
+    let isUnmounted = false;
+    const initPageData = async () => {
+      const { pageType: type, latitude: lat, longitude: lon, electronicAreaId } = deps.router.params;
+      const initTasks: Promise<unknown>[] = [];
+      if (lat && lon) {
+        initTasks.push(
+          fetchNearbySites(
+            Number(lat),
+            Number(lon),
+            electronicAreaId,
+            () => !isUnmounted
+          )
+        );
+      }
+      initTasks.push((async () => {
+        try {
+          const otp = await checkOtp(false);
+          if (!otp || !lat || !lon) {
+            return;
+          }
+          const payload = { lat: Number(lat), lon: Number(lon), electronicAreaId };
+          const res = type === 'start'
+            ? await getStartPoints(payload)
+            : type === 'end'
+              ? await getEndPoints(payload)
+              : [];
+          if (Array.isArray(res) && !isUnmounted) {
             setHistorySite(res.map(normalizeSiteItem));
           }
-        };
-        if (type === 'start') {
-          getStartPoints(payload).then(handleSuccess);
-        } else if (type === 'end') {
-          getEndPoints(payload).then(handleSuccess);
-        }
+        } catch { }
+      })());
+      if (type) {
+        setPageType(type);
       }
-    });
-    if (type) {
-      setPageType(type);
-    }
+      await Promise.allSettled(initTasks);
+      if (!isUnmounted) {
+        setIsInitLoaded(true);
+      }
+    };
+    initPageData();
+    return () => {
+      isUnmounted = true;
+    };
   }, []);
 
   return {
@@ -204,8 +271,12 @@
     nearbySiteList,
     isSearching,
     lastCompletedKeyword,
+    isInitLoaded,
     searchSite,
     debouncedSearch,
+    cancelDebouncedSearch,
+    flushDebouncedSearch,
+    confirmSearch,
     clearSearchResults
   };
 }

--
Gitblit v1.7.1