hejianhao
2026-07-01 a945e66614765a82ca1d9f73aa39e1471dda708c
feat(begin-route): 完善车门解锁UI状态的持久化与同步逻辑

- 新增车门解锁本地缓存工具类,保存订单级别的解锁状态
- 页面显示与订单切换时恢复解锁UI,避免串单和页面重启后状态丢失
- 传递解锁成功回调至组件链路,同步更新页面状态与缓存
- 修复上车点标记气泡箭头的显示条件问题
- 更新websocket解锁事件的缓存写入逻辑
- 优化手动解锁的提示文案与回调触发
1个文件已添加
6个文件已修改
101 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/components/calling-car-modules/door-trunk-control.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/door-unlock-cache.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-map-markers.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/web-socket-handlers/create-order-meta-socket-handlers.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/door-trunk-control.tsx
@@ -18,6 +18,7 @@
    orderTrunkStatus?: boolean;
    doorUnlockedInCurrentPage?: boolean;
    bluetoothDoorUnlockSuccess?: boolean;
    onDoorUnlockSuccess?: () => void;
    handleOpenOverRoutePopup?: () => void;
};
@@ -27,6 +28,7 @@
    orderTrunkStatus,
    doorUnlockedInCurrentPage = false,
    bluetoothDoorUnlockSuccess = false,
    onDoorUnlockSuccess,
    handleOpenOverRoutePopup
}: Props) {
    const [trunkStatus, setTrunkStatus] = useState(orderTrunkStatus); // 后备箱开关状态
@@ -234,7 +236,8 @@
                // Taro.hideLoading();
                syncDoorUnlockButtonUI();
                stopLoading(doorType);
                Taro.showToast({ title: '开门指令已发送', icon: 'none' });
                onDoorUnlockSuccess?.();
                Taro.showToast({ title: '右侧车门已解锁', icon: 'none' });
                onSuccess?.();
            }
        }).catch(() => {
src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -79,6 +79,8 @@
    orderTrunkStatus?: boolean;
    /** 当前页面内是否已经解锁过车门 */
    doorUnlockedInCurrentPage?: boolean;
    /** 手动解锁成功后同步页面级 UI */
    onDoorUnlockSuccess?: () => void;
    bluetoothStatusReady?: boolean;
    bluetoothCardDescription?: string;
    bluetoothCardActionText?: string;
@@ -130,6 +132,7 @@
    hasPreviousOrder = false,
    orderTrunkStatus,
    doorUnlockedInCurrentPage = false,
    onDoorUnlockSuccess,
    bluetoothStatusReady = false,
    bluetoothCardDescription = '授权并打开蓝牙链接,享受无感出行',
    bluetoothCardActionText = '去开启',
@@ -219,6 +222,7 @@
                        orderId={orderId}
                        orderTrunkStatus={orderTrunkStatus}
                        doorUnlockedInCurrentPage={doorUnlockedInCurrentPage}
                        onDoorUnlockSuccess={onDoorUnlockSuccess}
                        bluetoothDoorUnlockSuccess={bluetoothDoorUnlockSuccess}
                        handleOpenOverRoutePopup={handleOpenOverRoutePopup}
                    />
@@ -253,6 +257,7 @@
                orderId={orderId}
                orderTrunkStatus={orderTrunkStatus}
                doorUnlockedInCurrentPage={doorUnlockedInCurrentPage}
                onDoorUnlockSuccess={onDoorUnlockSuccess}
                bluetoothDoorUnlockSuccess={bluetoothDoorUnlockSuccess}
                handleOpenOverRoutePopup={handleOpenOverRoutePopup}
            />
src/robotaxi/pages/begin-route/hooks/door-unlock-cache.ts
New file
@@ -0,0 +1,55 @@
import Taro from "@tarojs/taro";
const DOOR_UNLOCK_CACHE_KEY = "beginRouteDoorUnlockCache";
type DoorUnlockCache = {
  orderId: string;
  unlocked: boolean;
};
/**
 * 读取指定订单的开门缓存。
 */
export const getDoorUnlockCache = (orderId?: string | null) => {
  const normalizedOrderId = String(orderId || "");
  if (!normalizedOrderId) {
    return false;
  }
  const cache = Taro.getStorageSync(DOOR_UNLOCK_CACHE_KEY) as
    | DoorUnlockCache
    | undefined;
  return (
    String(cache?.orderId || "") === normalizedOrderId &&
    cache?.unlocked === true
  );
};
/**
 * 写入指定订单的开门缓存。
 */
export const setDoorUnlockCache = (orderId?: string | null) => {
  const normalizedOrderId = String(orderId || "");
  if (!normalizedOrderId) {
    return;
  }
  Taro.setStorageSync(DOOR_UNLOCK_CACHE_KEY, {
    orderId: normalizedOrderId,
    unlocked: true
  });
};
/**
 * 清理指定订单的开门缓存。
 */
export const clearDoorUnlockCache = (orderId?: string | null) => {
  const normalizedOrderId = String(orderId || "");
  const cache = Taro.getStorageSync(DOOR_UNLOCK_CACHE_KEY) as
    | DoorUnlockCache
    | undefined;
  if (!cache) {
    return;
  }
  if (!normalizedOrderId || String(cache?.orderId || "") === normalizedOrderId) {
    Taro.removeStorageSync(DOOR_UNLOCK_CACHE_KEY);
  }
};
src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
@@ -169,6 +169,8 @@
    if (!(pickupSite?.latitude && pickupSite?.longitude)) return null;
    const shouldUseChangePickupIcon = deps.isModifyDestinationReviewVisible && deps.tripStatus >= TripStatus.VehicleArrivedPickup; // 修改目的地确认态且进入行程阶段时使用换点上车图标
    const shouldHidePickupName = deps.tripStatus === TripStatus.InService && deps.isModifyDestinationReviewVisible; // 行程中修改目的地时只展示换点图标,不展示上车点名称
    const shouldShowPickupArrow = (deps.tripStatus === TripStatus.PublishRoute || deps.showTrip1StyleEndpoint)
      && !deps.isModifyDestinationReviewVisible; // 修改目的地确认态隐藏上车点气泡箭头,和下车点保持一致
    // iOS 接驾态(VehicleApproaching):车辆由 translateMarker 平移,会把「另一个静止 marker」的 customCallout
    // 丢弃(必现,上车点气泡消失而车辆自身气泡不受影响)。这里让上车点改用原生 callout(与送驾态下车点同款),
    // 原生 callout 不受 translateMarker 影响,从而规避气泡丢失。
@@ -191,8 +193,7 @@
        ? {
            callout: {
              content:
                deps.tripStatus === TripStatus.PublishRoute ||
                deps.showTrip1StyleEndpoint
                shouldShowPickupArrow
                  ? `${pickupSite?.name || "上车点"} >`
                  : pickupSite?.name || "上车点",
              color: "#000000",
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -140,6 +140,8 @@
  trunkStatus: boolean;
  /** 当前页面内是否已经解锁过车门 */
  doorUnlockedInCurrentPage: boolean;
  /** 手动解锁成功后同步页面级 UI */
  onDoorUnlockSuccess: () => void;
  /** 路线选项集合 */
  routeOptions: any[];
  /** 选中路线索引 */
@@ -772,6 +774,7 @@
          onToggleDetailFade={deps.handleToggleDetailFade}
          orderTrunkStatus={deps.trunkStatus}
          doorUnlockedInCurrentPage={deps.doorUnlockedInCurrentPage}
          onDoorUnlockSuccess={deps.onDoorUnlockSuccess}
          bluetoothStatusReady={deps.bluetoothStatusReady}
          bluetoothCardDescription={deps.bluetoothCardDescription}
          bluetoothCardActionText={deps.bluetoothCardActionText}
src/robotaxi/pages/begin-route/hooks/web-socket-handlers/create-order-meta-socket-handlers.ts
@@ -2,6 +2,7 @@
import { queryVehicleByOrderId } from '../../../../api/begin-route';
import { MODIFY_DESTINATION_SOCKET_STATUS_KEY } from '../../../../constants/modify-destination';
import { TripStatus } from '../../../../constants/trip-status';
import { setDoorUnlockCache } from '../door-unlock-cache';
import {
  executeWhenOrderMatched,
  resolvePayloadOrderIdFromContentOnly,
@@ -76,8 +77,9 @@
    payload,
    deps: depsRef.current,
    resolveIncomingOrderId: resolvePayloadOrderIdFromTopLevelFirst,
    onMatched: () => {
    onMatched: ({ currentOrderId }) => {
      depsRef.current.setDoorUnlockedInCurrentPage(true);
      setDoorUnlockCache(String(currentOrderId || depsRef.current.orderId || ''));
    }
  });
};
src/robotaxi/pages/begin-route/index.tsx
@@ -54,6 +54,7 @@
import useModifyDestinationFlow from './hooks/use-modify-destination-flow';
import useModifyDestinationAvailability from './hooks/use-modify-destination-availability';
import useModifyDestinationPreviewRoute from './hooks/use-modify-destination-preview-route';
import { getDoorUnlockCache, setDoorUnlockCache } from './hooks/door-unlock-cache';
import usePreloadedMapAssets from './hooks/use-preloaded-map-assets';
// 蓝牙状态与弹窗编排
import useBluetoothAccess from './hooks/use-bluetooth-access';
@@ -667,7 +668,12 @@
      if (!isOpenDoor) {
        return;
      }
      const targetOrderId = getOrderId(orderId || Taro.getStorageSync('orderId') || '');
      if (!targetOrderId) {
        return;
      }
      setDoorUnlockedInCurrentPage(true);
      setDoorUnlockCache(targetOrderId);
    },
    setCurrentUserLocation,
    handleOpenNoRoutePopup
@@ -1453,10 +1459,22 @@
  }, []);
  /**
   * 手动解锁成功后,同步页面级开门态并写入订单级缓存,供重新进页恢复 UI。
   */
  const handleDoorUnlockSuccess = useCallback(() => {
    const targetOrderId = getOrderId(orderId || Taro.getStorageSync('orderId') || '');
    if (!targetOrderId) {
      return;
    }
    setDoorUnlockedInCurrentPage(true);
    setDoorUnlockCache(targetOrderId);
  }, [orderId]);
  /**
   * 订单切换后重置当前页面内的车门打开态,避免串单。
   */
  useEffect(() => {
    setDoorUnlockedInCurrentPage(false);
    setDoorUnlockedInCurrentPage(getDoorUnlockCache(getOrderId(orderId)));
  }, [orderId]);
  const { queryPaymentResult: queryOrderPaymentResult } = useOrderPaymentResultQuery({
@@ -1595,7 +1613,8 @@
  useDidShow(() => {
    setPageDidShowVersion((v) => v + 1);
    setDoorUnlockedInCurrentPage(false);
    const targetOrderId = getOrderId(orderId || Taro.getStorageSync('orderId') || '');
    setDoorUnlockedInCurrentPage(getDoorUnlockCache(targetOrderId));
    handleDidShow(router.params?.skipPageFlag, router.params?.fromModifyDestination);
    updateMapViewportPadding();
    // 完成态下隐藏小程序再恢复时,受控中心点可能与原生地图实际中心脱钩,主动触发一次
@@ -2146,6 +2165,7 @@
    handleShareTrip,
    trunkStatus,
    doorUnlockedInCurrentPage,
    onDoorUnlockSuccess: handleDoorUnlockSuccess,
    routeOptions,
    selectedRouteIndex,
    handleSelectRoute,