hejianhao
2026-05-19 c71f47d346a7692efde733ba84bd4dee87ed6e28
feat(robotaxi): 优化上车点持久化逻辑并修复修改目的地返回提示

- 新增use-home-start-site-display钩子管理首页上车点展示状态,持久化用户手动选择的上车点
- 修复从修改目的地页面返回时错误弹出「已有进行中订单」提示的问题
- 优化首页导航、站点搜索与地图选点页面的上车点状态同步逻辑
- 调整站点选择后的返回路由适配修改目的地业务流程
1个文件已添加
10个文件已修改
245 ■■■■■ 已修改文件
src/pages/robotaxi/home/components/home-content-panel.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-home-bootstrap.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-home-start-site-display.ts 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-location-flow.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-navigation.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-region-change.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-page-restore.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/components/home-content-panel.tsx
@@ -25,6 +25,8 @@
  isAdsorbed: boolean;
  /** 当前站点。 */
  currentSite: any;
  /** 首页确认上车点。 */
  preferredStartSite: any;
  /** 是否展示快捷胶囊。 */
  showQuickCapsule: boolean;
  /** 快捷胶囊文案。 */
@@ -74,6 +76,9 @@
 * 首页底部内容面板。
 */
export default function HomeContentPanel(props: Props) {
  const startSiteText = props.preferredStartSite?.name
    || ((!props.isOperatingArea || props.isAdsorbed) ? (props.currentSite?.name || '请选择上车点') : '推荐上车点');
  return (
    <View
      className="content-box"
@@ -102,7 +107,7 @@
                <View className="now-site-address-content">
                  从
                  <Text className="now-site-address-text">
                    {(!props.isOperatingArea || props.isAdsorbed) ? (props.currentSite?.name || '请选择上车点') : '推荐上车点'}
                    {startSiteText}
                  </Text>
                  出发
                </View>
src/pages/robotaxi/home/hooks/use-home-bootstrap.ts
@@ -2,6 +2,14 @@
import wsManager from '../../../../utils/websocket';
import { mapOrderStatusToTripStatus } from '../../../../utils/Robotaxi-public-method';
import { TripStatus, toTripStatus } from '../../../../robotaxi/pages/begin-route/constants/trip-status';
import {
  clearHomeStartSiteDisplayState,
  HOME_START_SITE_SOURCE_ACTIVE_ORDER,
  isManualSelectedHomeStartSiteDisplayState,
  readHomeStartSiteDisplayState,
  resolveHomeStartSiteDisplaySite,
  writeHomeStartSiteDisplayState
} from './use-home-start-site-display';
type HomeTripStatus = TripStatus | 0;
@@ -11,6 +19,8 @@
  hasLogin: boolean;
  /** 当前是否存在活跃订单。 */
  hasActiveOrder: boolean;
  /** 当前首页确认上车点。 */
  preferredStartSite: any;
};
type Deps = {
@@ -68,6 +78,8 @@
  ensureSelectedCityReady: (lng: number, lat: number, skipCityEffect?: boolean, forceRefresh?: boolean) => Promise<any>;
  /** 设置当前站点 */
  setCurrentSiteFromStart: (value: any) => void;
  /** 设置首页确认上车点。 */
  setPreferredStartSite: (value: any) => void;
  /** 首次进入 key */
  firstEnterKey: string;
  /** 默认纬度 */
@@ -118,7 +130,8 @@
    const otp = await deps.checkOtp(false);
    deps.setNeedLogin(!otp);
    if (!otp) {
      return { hasLogin: false, hasActiveOrder: false } as AuthOrderSyncResult;
      deps.setPreferredStartSite(null);
      return { hasLogin: false, hasActiveOrder: false, preferredStartSite: null } as AuthOrderSyncResult;
    }
    wsManager.connect(deps.wsHost);
    const agreement = await deps.getAgreement();
@@ -131,13 +144,25 @@
    }
    const activeOrder = await deps.getOrderActive();
    if (!activeOrder) {
      const preferredState = readHomeStartSiteDisplayState();
      const preferredStartSite = isManualSelectedHomeStartSiteDisplayState(preferredState)
        ? resolveHomeStartSiteDisplaySite()
        : null;
      Taro.removeStorageSync('orderId');
      Taro.removeStorageSync('tripStatus');
      Taro.removeStorageSync('startSite');
      Taro.removeStorageSync('endSite');
      if (!preferredStartSite) {
        Taro.removeStorageSync('startSite');
      } else {
        Taro.setStorageSync('startSite', preferredStartSite);
      }
      if (preferredState?.source === HOME_START_SITE_SOURCE_ACTIVE_ORDER) {
        clearHomeStartSiteDisplayState();
      }
      deps.setTripStatus(0);
      deps.setOrderData({});
      return { hasLogin: true, hasActiveOrder: false } as AuthOrderSyncResult;
      deps.setPreferredStartSite(preferredStartSite);
      return { hasLogin: true, hasActiveOrder: false, preferredStartSite } as AuthOrderSyncResult;
    }
    deps.setOrderData(activeOrder || {});
    const mappedStatus = toHomeTripStatus(mapOrderStatusToTripStatus(activeOrder.status || 0));
@@ -148,7 +173,9 @@
    Taro.setStorageSync('orderId', activeOrder.id || '');
    Taro.setStorageSync('startSite', startSite);
    Taro.setStorageSync('endSite', endSite);
    return { hasLogin: true, hasActiveOrder: true } as AuthOrderSyncResult;
    writeHomeStartSiteDisplayState(startSite, HOME_START_SITE_SOURCE_ACTIVE_ORDER);
    deps.setPreferredStartSite(startSite);
    return { hasLogin: true, hasActiveOrder: true, preferredStartSite: startSite } as AuthOrderSyncResult;
  };
  /**
@@ -249,7 +276,9 @@
        const auth = res.authSetting['scope.userLocation'];
        Promise.resolve(authSyncTask)
          .then((syncResult) => {
            const startSite = syncResult.hasActiveOrder ? Taro.getStorageSync('startSite') : null;
            const startSite = syncResult.hasActiveOrder
              ? Taro.getStorageSync('startSite')
              : syncResult.preferredStartSite;
            processLocationSetting(auth, startSite);
          })
          .catch(() => {
src/pages/robotaxi/home/hooks/use-home-start-site-display.ts
New file
@@ -0,0 +1,108 @@
import Taro from '@tarojs/taro';
export const HOME_START_SITE_SOURCE_MANUAL_SELECTED = 'manual_selected';
export const HOME_START_SITE_SOURCE_ACTIVE_ORDER = 'active_order';
export const HOME_START_SITE_DISPLAY_SOURCES = [
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  HOME_START_SITE_SOURCE_ACTIVE_ORDER
] as const;
export type HomeStartSiteDisplaySource = typeof HOME_START_SITE_DISPLAY_SOURCES[number];
export type HomeStartSiteDisplayState = {
  site: any;
  source: HomeStartSiteDisplaySource;
  cityCode: string;
  updatedAt: number;
};
export const HOME_START_SITE_DISPLAY_STATE_KEY = 'homeStartSiteDisplayState';
/**
 * 判断站点是否具备有效经纬度。
 */
export const isValidHomeStartSite = (site: any) => (
  Number.isFinite(Number(site?.latitude)) && Number.isFinite(Number(site?.longitude))
);
/**
 * 读取当前已选城市编码。
 */
const getSelectedCityCode = () => String(Taro.getStorageSync('selectedCity')?.code || '');
/**
 * 读取首页确认上车点显示态。
 */
export const readHomeStartSiteDisplayState = () => {
  const cachedState = Taro.getStorageSync(HOME_START_SITE_DISPLAY_STATE_KEY);
  if (!cachedState || !isValidHomeStartSite(cachedState?.site)) {
    return null;
  }
  const source = String(cachedState?.source || '') as HomeStartSiteDisplaySource;
  if (!HOME_START_SITE_DISPLAY_SOURCES.includes(source)) {
    return null;
  }
  return {
    site: cachedState.site,
    source,
    cityCode: String(cachedState?.cityCode || ''),
    updatedAt: Number(cachedState?.updatedAt || Date.now())
  } as HomeStartSiteDisplayState;
};
/**
 * 清理首页确认上车点显示态。
 */
export const clearHomeStartSiteDisplayState = () => {
  Taro.removeStorageSync(HOME_START_SITE_DISPLAY_STATE_KEY);
};
/**
 * 判断缓存显示态是否仍可用于当前城市。
 */
export const isHomeStartSiteDisplayStateUsable = (state: HomeStartSiteDisplayState | null, cityCodeArg?: string) => {
  if (!state || !isValidHomeStartSite(state.site)) {
    return false;
  }
  const currentCityCode = String(cityCodeArg || getSelectedCityCode() || '');
  if (!state.cityCode || !currentCityCode) {
    return true;
  }
  return state.cityCode === currentCityCode;
};
/**
 * 判断当前显示态是否为用户手动确认的上车点。
 */
export const isManualSelectedHomeStartSiteDisplayState = (state: HomeStartSiteDisplayState | null) => (
  state?.source === HOME_START_SITE_SOURCE_MANUAL_SELECTED
);
/**
 * 写入首页确认上车点显示态。
 */
export const writeHomeStartSiteDisplayState = (site: any, source: HomeStartSiteDisplaySource, cityCodeArg?: string) => {
  if (!isValidHomeStartSite(site)) {
    clearHomeStartSiteDisplayState();
    return;
  }
  const nextState: HomeStartSiteDisplayState = {
    site,
    source,
    cityCode: String(cityCodeArg || getSelectedCityCode() || ''),
    updatedAt: Date.now()
  };
  Taro.setStorageSync(HOME_START_SITE_DISPLAY_STATE_KEY, nextState);
};
/**
 * 解析当前可用的首页确认上车点。
 */
export const resolveHomeStartSiteDisplaySite = (cityCodeArg?: string) => {
  const cachedState = readHomeStartSiteDisplayState();
  if (!isHomeStartSiteDisplayStateUsable(cachedState, cityCodeArg)) {
    clearHomeStartSiteDisplayState();
    return null;
  }
  return cachedState?.site || null;
};
src/pages/robotaxi/home/hooks/use-location-flow.ts
@@ -6,6 +6,11 @@
  getLocationByIP
} from '../../../../utils/Robotaxi-public-method';
import { DEFAULT_LATITUDE, DEFAULT_LONGITUDE } from '../../../../robotaxi/constants/map';
import {
  isHomeStartSiteDisplayStateUsable,
  isManualSelectedHomeStartSiteDisplayState,
  readHomeStartSiteDisplayState
} from './use-home-start-site-display';
type Deps = {
  mapState: any;
@@ -19,6 +24,20 @@
 * 定位流程编排:不持有状态,仅组合 map/site/city 状态 hook 完成定位主链路。
 */
export default function useLocationFlow({ mapState, siteState, cityState, isLoggedIn, onLocationSettled }: Deps) {
  /**
   * 解析当前城市下仍然有效的手动确认上车点。
   */
  const resolveLockedManualStartSite = (cityCodeArg?: string) => {
    const preferredState = readHomeStartSiteDisplayState();
    if (!isManualSelectedHomeStartSiteDisplayState(preferredState)) {
      return null;
    }
    if (!isHomeStartSiteDisplayStateUsable(preferredState, cityCodeArg)) {
      return null;
    }
    return preferredState.site;
  };
  const updateByStartSite = async () => {
    const startSite = Taro.getStorageSync('startSite');
    if (!(startSite && startSite.latitude && startSite.longitude)) return false;
@@ -123,8 +142,11 @@
          );
          if (cityInfo?.code) {
            const { isInside, newMarkers, sites } = await mapState.updateMapData(res.latitude, res.longitude, cityInfo.code);
            const lockedManualStartSite = resolveLockedManualStartSite(cityInfo.code);
            const nearestCandidates = sites.length > 0 ? sites : newMarkers;
            const { adsorbed, marker } = siteState.tryAdsorb(res.latitude, res.longitude, nearestCandidates);
            const { adsorbed, marker } = lockedManualStartSite
              ? { adsorbed: false, marker: null }
              : siteState.tryAdsorb(res.latitude, res.longitude, nearestCandidates);
            const finalLat = adsorbed && marker ? marker.latitude : res.latitude; // 最终纬度
            const finalLng = adsorbed && marker ? marker.longitude : res.longitude; // 最终经度
            onLocationSettled(finalLat, finalLng, !!isInside);
src/pages/robotaxi/home/hooks/use-navigation.ts
@@ -9,6 +9,7 @@
import { jumpTo } from '../../../../utils/jump';
import { getGeofence } from '../../../../robotaxi/api/home';
import { GeofenceType } from '../../../../robotaxi/constants/geofence';
import { resolveHomeStartSiteDisplaySite } from './use-home-start-site-display';
type Deps = {
  /** 是否未登录 */
@@ -123,11 +124,20 @@
      return;
    }
    if (type == 'end') {
      if (!deps.currentSite || !deps.currentSite.id) {
      const preferredStartSite = resolveHomeStartSiteDisplaySite();
      const effectiveStartSite = preferredStartSite || deps.currentSite;
      if (!effectiveStartSite || !effectiveStartSite.id) {
        Taro.showToast({ title: '请先选择起点站点', icon: 'none' });
        return;
      }
      Taro.setStorageSync('startSite', deps.currentSite);
      Taro.setStorageSync('startSite', effectiveStartSite);
      Taro.navigateTo({
        url: `/robotaxi/pages/site-search/index?pageType=${type}&electronicAreaId=${effectiveStartSite.electronicAreaId}&longitude=${effectiveStartSite.longitude}&latitude=${effectiveStartSite.latitude}`,
        success: () => {
          reportEvent('RT_xcx_search_click', { element_type: type == 'start' ? 'start' : 'dest' });
        }
      });
      return;
    }
    Taro.navigateTo({
      url: `/robotaxi/pages/site-search/index?pageType=${type}&electronicAreaId=${deps.electronicAreaId}&longitude=${deps.currentSite.longitude}&latitude=${deps.currentSite.latitude}`,
src/pages/robotaxi/home/hooks/use-region-change.ts
@@ -1,5 +1,9 @@
import Taro from '@tarojs/taro';
import { DEFAULT_LATITUDE, DEFAULT_LONGITUDE } from '../../../../robotaxi/constants/map';
import {
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  writeHomeStartSiteDisplayState
} from './use-home-start-site-display';
type Deps = {
  mapState: any;
@@ -13,6 +17,16 @@
 * 地图 region-change 编排:处理地图拖拽/缩放后的吸附、回填和自动归位。
 */
export default function useRegionChange({ mapState, siteState, toOrigin, onRegionBegin, onRegionSettled }: Deps) {
  /**
   * 将用户显式换点后的吸附结果同步为首页确认上车点。
   */
  const syncManualSelectedStartSite = (site: any) => {
    if (!site?.latitude || !site?.longitude) {
      return;
    }
    writeHomeStartSiteDisplayState(site, HOME_START_SITE_SOURCE_MANUAL_SELECTED);
  };
  /**
   * 按最终落点回传地图结算结果,保证场站围栏与吸附后的中心点一致。
   */
@@ -88,6 +102,9 @@
          const { adsorbed, marker } = siteState.tryAdsorb(lat, lng, nearestCandidates);
          const finalLat = adsorbed && marker ? marker.latitude : lat; // 最终纬度
          const finalLng = adsorbed && marker ? marker.longitude : lng; // 最终经度
          if (adsorbed && marker && (causedBy === 'gesture' || causedBy === 'drag' || wasReturningToOrigin)) {
            syncManualSelectedStartSite(marker);
          }
          settleRegionResult({ lat: finalLat, lon: finalLng, isInside: true });
          if ((causedBy === 'gesture' || causedBy === 'drag' || wasReturningToOrigin) && marker) {
            mapCtx.moveToLocation({
src/pages/robotaxi/home/index.tsx
@@ -45,6 +45,10 @@
  OVERLAY_ANCHOR_OFFSET,
  WAITING_RADAR_SRC
} from './constants';
import {
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  writeHomeStartSiteDisplayState
} from './hooks/use-home-start-site-display';
type HomeTripStatus = TripStatus | 0;
@@ -60,6 +64,7 @@
  const [needLogin, setNeedLogin] = useState(false); // 当前是否需要登录
  const [overlayAnchorBottom, setOverlayAnchorBottom] = useState<number | null>(null); // 地图浮层锚点
  const [temperatureUnit, setTemperatureUnit] = useState(DEFAULT_TEMPERATURE_UNIT); // 温度单位配置
  const [preferredStartSite, setPreferredStartSite] = useState<any>(null); // 首页确认上车点
  const mapState = useMapState(); // 地图领域状态
  const siteState = useSiteState(); // 站点吸附状态
@@ -188,6 +193,7 @@
    syncStationFenceVisibility,
    ensureSelectedCityReady,
    setCurrentSiteFromStart: setCurrentSite,
    setPreferredStartSite,
    firstEnterKey: FIRST_ENTER_KEY,
    defaultLatitude: DEFAULT_LATITUDE,
    defaultLongitude: DEFAULT_LONGITUDE,
@@ -354,7 +360,9 @@
      setBounce(false);
      setIsAdsorbed(true);
      setCurrentSite(targetMarker);
      setPreferredStartSite(targetMarker);
      Taro.setStorageSync('startSite', targetMarker);
      writeHomeStartSiteDisplayState(targetMarker, HOME_START_SITE_SOURCE_MANUAL_SELECTED);
      setShowToolTip(true);
    }, MARKER_TAP_SETTLE_DELAY);
  };
@@ -454,6 +462,7 @@
        isOperatingArea={isOperatingArea}
        isAdsorbed={isAdsorbed}
        currentSite={currentSite}
        preferredStartSite={preferredStartSite}
        showQuickCapsule={showQuickCapsule}
        quickCapsuleText={quickCapsuleText}
        onSelectSite={toSelectSitePage}
src/robotaxi/pages/begin-route/hooks/use-page-restore.ts
@@ -149,10 +149,11 @@
  /**
   * DidShow 阶段入口。
   */
  const handleDidShow = (skipPageFlag?: string) => {
  const handleDidShow = (skipPageFlag?: string, fromModifyDestination?: string) => {
    ensurePageSocketReady();
    const isModifyDestinationSessionActive = !!Taro.getStorageSync(MODIFY_DESTINATION_SESSION_KEY)?.active; // 修改目的地返回链路不提示“已有进行中订单”
    if (skipPageFlag && !isModifyDestinationSessionActive) {
    const isModifyDestinationReturn = String(fromModifyDestination || '') === '1';
    if (skipPageFlag && !isModifyDestinationReturn && !isModifyDestinationSessionActive) {
      Taro.showToast({
        title: '您已有进行中的订单',
        icon: 'none',
src/robotaxi/pages/begin-route/index.tsx
@@ -777,7 +777,7 @@
  useDidShow(() => {
    setPageDidShowVersion((v) => v + 1);
    handleDidShow(router.params?.skipPageFlag);
    handleDidShow(router.params?.skipPageFlag, router.params?.fromModifyDestination);
    updateMapViewportPadding();
  });
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts
@@ -22,6 +22,10 @@
  MODIFY_DESTINATION_SKIP_AC_QUERY_ON_RETURN_KEY,
  MODIFY_DESTINATION_SESSION_KEY
} from '../../begin-route/constants/modify-destination';
import {
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  writeHomeStartSiteDisplayState
} from '../../../../pages/robotaxi/home/hooks/use-home-start-site-display';
const AUTO_SCALE_MAX = 17; // 自动缩放最大等级
const AUTO_SCALE_MIN = 11; // 自动缩放最小等级
@@ -286,6 +290,7 @@
        }
      }
      Taro.setStorageSync('startSite', state.selectedSite);
      writeHomeStartSiteDisplayState(state.selectedSite, HOME_START_SITE_SOURCE_MANUAL_SELECTED);
      if (state.router.params.skipSource) {
        Taro.navigateBack();
        return;
@@ -318,8 +323,11 @@
    } else {
      Taro.setStorageSync('endSite', state.selectedSite);
    }
    const returnToBeginRouteUrl = modifySession?.active
      ? '/robotaxi/pages/begin-route/index?skipPageFlag=1&fromModifyDestination=1'
      : (Taro.getStorageSync('orderId') ? '/robotaxi/pages/begin-route/index?skipPageFlag=1' : '/robotaxi/pages/begin-route/index');
    Taro.reLaunch({
      url: Taro.getStorageSync('orderId') ? '/robotaxi/pages/begin-route/index?skipPageFlag=1' : '/robotaxi/pages/begin-route/index',
      url: returnToBeginRouteUrl,
    });
  };
src/robotaxi/pages/site-search/index.tsx
@@ -27,6 +27,10 @@
  MODIFY_DESTINATION_SKIP_AC_QUERY_ON_RETURN_KEY,
  MODIFY_DESTINATION_SESSION_KEY
} from '../begin-route/constants/modify-destination';
import {
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  writeHomeStartSiteDisplayState
} from '../../../pages/robotaxi/home/hooks/use-home-start-site-display';
/**
 * 安全解析路由关键字,兼容已编码和未编码两种入参。
@@ -145,8 +149,11 @@
    } else {
      Taro.setStorageSync('endSite', item)
    }
    const returnToBeginRouteUrl = modifySession?.active
      ? '/robotaxi/pages/begin-route/index?skipPageFlag=1&fromModifyDestination=1'
      : (Taro.getStorageSync('orderId') ? '/robotaxi/pages/begin-route/index?skipPageFlag=1' : '/robotaxi/pages/begin-route/index');
    Taro.reLaunch({
      url: Taro.getStorageSync('orderId') ? '/robotaxi/pages/begin-route/index?skipPageFlag=1' : '/robotaxi/pages/begin-route/index',
      url: returnToBeginRouteUrl,
    })
  }
@@ -223,6 +230,7 @@
        }
      } catch { }
      Taro.setStorageSync('startSite', item);
      writeHomeStartSiteDisplayState(item, HOME_START_SITE_SOURCE_MANUAL_SELECTED);
      Taro.navigateBack()
    } else if (pageType == 'end') {
      const otp = await checkOtp(false);