feat(begin-route): 完善车门解锁UI状态的持久化与同步逻辑
- 新增车门解锁本地缓存工具类,保存订单级别的解锁状态
- 页面显示与订单切换时恢复解锁UI,避免串单和页面重启后状态丢失
- 传递解锁成功回调至组件链路,同步更新页面状态与缓存
- 修复上车点标记气泡箭头的显示条件问题
- 更新websocket解锁事件的缓存写入逻辑
- 优化手动解锁的提示文案与回调触发
| | |
| | | orderTrunkStatus?: boolean; |
| | | doorUnlockedInCurrentPage?: boolean; |
| | | bluetoothDoorUnlockSuccess?: boolean; |
| | | onDoorUnlockSuccess?: () => void; |
| | | handleOpenOverRoutePopup?: () => void; |
| | | }; |
| | | |
| | |
| | | orderTrunkStatus, |
| | | doorUnlockedInCurrentPage = false, |
| | | bluetoothDoorUnlockSuccess = false, |
| | | onDoorUnlockSuccess, |
| | | handleOpenOverRoutePopup |
| | | }: Props) { |
| | | const [trunkStatus, setTrunkStatus] = useState(orderTrunkStatus); // 后备箱开关状态 |
| | |
| | | // Taro.hideLoading(); |
| | | syncDoorUnlockButtonUI(); |
| | | stopLoading(doorType); |
| | | Taro.showToast({ title: '开门指令已发送', icon: 'none' }); |
| | | onDoorUnlockSuccess?.(); |
| | | Taro.showToast({ title: '右侧车门已解锁', icon: 'none' }); |
| | | onSuccess?.(); |
| | | } |
| | | }).catch(() => { |
| | |
| | | orderTrunkStatus?: boolean; |
| | | /** 当前页面内是否已经解锁过车门 */ |
| | | doorUnlockedInCurrentPage?: boolean; |
| | | /** 手动解锁成功后同步页面级 UI */ |
| | | onDoorUnlockSuccess?: () => void; |
| | | bluetoothStatusReady?: boolean; |
| | | bluetoothCardDescription?: string; |
| | | bluetoothCardActionText?: string; |
| | |
| | | hasPreviousOrder = false, |
| | | orderTrunkStatus, |
| | | doorUnlockedInCurrentPage = false, |
| | | onDoorUnlockSuccess, |
| | | bluetoothStatusReady = false, |
| | | bluetoothCardDescription = '授权并打开蓝牙链接,享受无感出行', |
| | | bluetoothCardActionText = '去开启', |
| | |
| | | orderId={orderId} |
| | | orderTrunkStatus={orderTrunkStatus} |
| | | doorUnlockedInCurrentPage={doorUnlockedInCurrentPage} |
| | | onDoorUnlockSuccess={onDoorUnlockSuccess} |
| | | bluetoothDoorUnlockSuccess={bluetoothDoorUnlockSuccess} |
| | | handleOpenOverRoutePopup={handleOpenOverRoutePopup} |
| | | /> |
| | |
| | | orderId={orderId} |
| | | orderTrunkStatus={orderTrunkStatus} |
| | | doorUnlockedInCurrentPage={doorUnlockedInCurrentPage} |
| | | onDoorUnlockSuccess={onDoorUnlockSuccess} |
| | | bluetoothDoorUnlockSuccess={bluetoothDoorUnlockSuccess} |
| | | handleOpenOverRoutePopup={handleOpenOverRoutePopup} |
| | | /> |
| New file |
| | |
| | | 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); |
| | | } |
| | | }; |
| | |
| | | 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 影响,从而规避气泡丢失。 |
| | |
| | | ? { |
| | | callout: { |
| | | content: |
| | | deps.tripStatus === TripStatus.PublishRoute || |
| | | deps.showTrip1StyleEndpoint |
| | | shouldShowPickupArrow |
| | | ? `${pickupSite?.name || "上车点"} >` |
| | | : pickupSite?.name || "上车点", |
| | | color: "#000000", |
| | |
| | | trunkStatus: boolean; |
| | | /** 当前页面内是否已经解锁过车门 */ |
| | | doorUnlockedInCurrentPage: boolean; |
| | | /** 手动解锁成功后同步页面级 UI */ |
| | | onDoorUnlockSuccess: () => void; |
| | | /** 路线选项集合 */ |
| | | routeOptions: any[]; |
| | | /** 选中路线索引 */ |
| | |
| | | onToggleDetailFade={deps.handleToggleDetailFade} |
| | | orderTrunkStatus={deps.trunkStatus} |
| | | doorUnlockedInCurrentPage={deps.doorUnlockedInCurrentPage} |
| | | onDoorUnlockSuccess={deps.onDoorUnlockSuccess} |
| | | bluetoothStatusReady={deps.bluetoothStatusReady} |
| | | bluetoothCardDescription={deps.bluetoothCardDescription} |
| | | bluetoothCardActionText={deps.bluetoothCardActionText} |
| | |
| | | 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, |
| | |
| | | payload, |
| | | deps: depsRef.current, |
| | | resolveIncomingOrderId: resolvePayloadOrderIdFromTopLevelFirst, |
| | | onMatched: () => { |
| | | onMatched: ({ currentOrderId }) => { |
| | | depsRef.current.setDoorUnlockedInCurrentPage(true); |
| | | setDoorUnlockCache(String(currentOrderId || depsRef.current.orderId || '')); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | 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'; |
| | |
| | | if (!isOpenDoor) { |
| | | return; |
| | | } |
| | | const targetOrderId = getOrderId(orderId || Taro.getStorageSync('orderId') || ''); |
| | | if (!targetOrderId) { |
| | | return; |
| | | } |
| | | setDoorUnlockedInCurrentPage(true); |
| | | setDoorUnlockCache(targetOrderId); |
| | | }, |
| | | setCurrentUserLocation, |
| | | handleOpenNoRoutePopup |
| | |
| | | }, []); |
| | | |
| | | /** |
| | | * 手动解锁成功后,同步页面级开门态并写入订单级缓存,供重新进页恢复 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({ |
| | |
| | | |
| | | 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(); |
| | | // 完成态下隐藏小程序再恢复时,受控中心点可能与原生地图实际中心脱钩,主动触发一次 |
| | |
| | | handleShareTrip, |
| | | trunkStatus, |
| | | doorUnlockedInCurrentPage, |
| | | onDoorUnlockSuccess: handleDoorUnlockSuccess, |
| | | routeOptions, |
| | | selectedRouteIndex, |
| | | handleSelectRoute, |