src/pages/robotaxi/home/index.tsx
@@ -72,11 +72,7 @@ import { subscribeOperationCouponGroup } from '../../../utils/subscribe'; import { GeofenceType } from '../../../robotaxi/constants/geofence'; enum HomePageTripStatus { None = 0 } type HomeTripStatus = TripStatus | HomePageTripStatus; type HomeTripStatus = TripStatus | 0; export default function Home() { const renderTailPreserveText = (text: string, tailCount = 3) => { @@ -144,7 +140,7 @@ const [isMaintain, setIsMaintain] = useState(false); // 是否维护中 const [maintainBounce, setMaintainBounce] = useState(false); const [hotfixEndText, setHotfixEndText] = useState(''); const [tripStatus, setTripStatus] = useState<HomeTripStatus>(HomePageTripStatus.None); // 行程状态(None 表示无进行中行程) const [tripStatus, setTripStatus] = useState<HomeTripStatus>(0); // 行程状态(0 表示无进行中行程) const [bannerList, setBannerList] = useState<any[]>([]); // 轮播图列表 const [weatherData, setWeatherData] = useState<any>(null); // 天气数据 const [orderData, setOrderData] = useState<any>({}); // 订单数据 @@ -155,10 +151,7 @@ const [stationGeofenceList, setStationGeofenceList] = useState<any[]>([]); // 场站围栏原始数据 const operatingGeofenceListRef = useRef<any[]>([]); // 运营区围栏最新缓存 const stationGeofenceListRef = useRef<any[]>([]); // 场站围栏最新缓存 const shouldShowOrderStatusCard = useMemo( () => tripStatus === TripStatus.CallingCar || Boolean(orderData?.id), [orderData?.id, tripStatus] ); // 订单状态卡片展示时机,避免真实数据未返回时先展示默认图 const shouldShowOrderStatusCard = useMemo(() => tripStatus === 3 || Boolean(orderData?.id), [orderData?.id, tripStatus]); // 订单状态卡片展示时机 const { boxHeight, originOpacity, @@ -296,11 +289,7 @@ const orderStatusHandler = (payload) => { const mappedTripStatus = payload?.tripStatus; // ws 推送的行程状态 if (typeof mappedTripStatus === 'number') { setTripStatus( mappedTripStatus === HomePageTripStatus.None ? HomePageTripStatus.None : toTripStatus(mappedTripStatus) ); setTripStatus(mappedTripStatus === 0 ? 0 : toTripStatus(mappedTripStatus)); } }; let openHandler: (() => void) | null = null; src/robotaxi/pages/begin-route/components/calling-car-modules/door-trunk-control.tsx
@@ -35,39 +35,27 @@ }; }, []); // 获取解锁请求所需定位参数,优先高精定位,失败后降级普通定位 // 获取解锁请求所需定位参数,失败时回退到缓存上车点 const getUnlockLocationPayload = async () => { Taro.showLoading({ title: '正在获取位置...', mask: true }); try { const location = await Taro.getLocation({ type: 'gcj02', isHighAccuracy: true, highAccuracyExpireTime: 4000 }); Taro.showLoading({ title: '正在获取位置...', mask: true }); const location = await Taro.getLocation({ type: 'gcj02', isHighAccuracy: true }); Taro.hideLoading(); const longitude = Number(location?.longitude); const latitude = Number(location?.latitude); if (!Number.isNaN(longitude) && !Number.isNaN(latitude)) { Taro.hideLoading(); return { longitude, latitude }; } } catch (error) { console.error('高精度定位获取失败:', error); } catch (_) { Taro.hideLoading(); } try { const location = await Taro.getLocation({ type: 'gcj02' }); const longitude = Number(location?.longitude); const latitude = Number(location?.latitude); if (!Number.isNaN(longitude) && !Number.isNaN(latitude)) { Taro.hideLoading(); return { longitude, latitude }; } } catch (error) { console.error('普通定位获取失败:', error); const startSite = Taro.getStorageSync('startSite') || {}; const longitude = Number(startSite?.longitude); const latitude = Number(startSite?.latitude); if (!Number.isNaN(longitude) && !Number.isNaN(latitude)) { return { longitude, latitude }; } Taro.hideLoading(); return null; return {}; }; // 解锁车门/后备箱,后备箱场景会轮询确认结果 @@ -76,10 +64,6 @@ if (!targetOrderId) return; const locationPayload = await getUnlockLocationPayload(); if (!locationPayload) { Taro.showToast({ title: '获取定位失败,请打开定位开关', icon: 'none' }); return; } const isCloseAction = doorType === 'trunk' ? !!trunkStatus : false; const actionLoadingText = doorType === 'trunk' ? (isCloseAction ? '正在关闭后备箱' : '正在开启后备箱') src/robotaxi/pages/begin-route/hooks/runtime-sync.ts
File was deleted src/robotaxi/pages/begin-route/hooks/trip-motion-utils.ts
File was deleted src/robotaxi/pages/begin-route/hooks/use-android-overlays.ts
@@ -15,7 +15,6 @@ }; export default function useAndroidOverlays(deps: Deps) { const CAR_CALLOUT_TOP_OFFSET = -10; // 车辆气泡顶部基准偏移,保证与车 icon 保持约 10px 间距 // 等待雷达样式与显示状态 const [waitingRadarStyle, setWaitingRadarStyle] = useState<any>({}); const [waitingRadarVisible, setWaitingRadarVisible] = useState(true); @@ -92,13 +91,13 @@ return; } const rect = await ensureMapRect(); const pos = await computeScreenPosition(deps.vehicleLocation, rect, CAR_CALLOUT_TOP_OFFSET); const pos = await computeScreenPosition(deps.vehicleLocation, rect, 60); if (!pos) return; setCarCalloutStyle({ position: 'fixed', left: pos.left, top: pos.top, transform: 'translate(-50%, calc(-100% - 10px))' transform: 'translate(-50%, -120%)' }); setCarCalloutVisible(true); }; src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
@@ -3,45 +3,9 @@ import { TripStatus } from '../constants/trip-status'; import changeSiteUpIcon from '../../../assets/change-site-up.png'; export enum MarkerType { PICKUP_ICON = 'pickup_icon', PICKUP_TEXT = 'pickup_text', DROPOFF_ICON = 'dropoff_icon', DROPOFF_TEXT = 'dropoff_text', BEFORE_USER_DROPOFF = 'before_user_dropoff', CAR = 'car' } type MarkerItem = { id: number; latitude: number; longitude: number; iconPath: string; width: number; height: number; anchor: { x: number; y: number }; zIndex: number; name: string; markerType: MarkerType; alpha?: number; rotate?: number; customCallout?: any; callout?: any; isBeforeUserSite?: boolean; hideNameCallout?: boolean; isRouteSummaryBubble?: boolean; }; type StatusRule = { showPickup: boolean; showDropoff: boolean; showCar: boolean; markerOrder?: MarkerType[]; }; type Deps = { tripStatus: TripStatus; statusRule: StatusRule; statusRule: any; isAndroid: boolean; showTrip1StyleEndpoint: boolean; isModifyDestinationReviewVisible: boolean; @@ -71,36 +35,21 @@ arrivedTooltipReady: false }; const PICKUP_ICON_MARKER_ID = 11; // 上车点图标 marker ID const DROPOFF_ICON_MARKER_ID = 12; // 下车点图标 marker ID const BEFORE_USER_DROPOFF_MARKER_ID = 31; // 上一单终点 marker ID const SITE_TEXT_MARKER_SIZE = 1; // 站点文字锚点 marker 尺寸 const SITE_TEXT_CALLOUT_ANCHOR = -5; // 站点文字默认锚点偏移 const CAR_CALLOUT_ANCHOR = -10; // 车辆里程气泡锚点偏移,保证与车 icon 保持约 10px 间距 const DEFAULT_MARKER_ORDER: MarkerType[] = [ MarkerType.PICKUP_ICON, MarkerType.CAR, MarkerType.DROPOFF_ICON, MarkerType.BEFORE_USER_DROPOFF, MarkerType.PICKUP_TEXT, MarkerType.DROPOFF_TEXT ]; // 默认图层顺序 export default function useMapMarkers(rawDeps?: Partial<Deps>) { const deps = { ...defaultDeps, ...(rawDeps || {}) } as Deps; const [markers, setMarkers] = useState<MarkerItem[]>([]); const markersRef = useRef<MarkerItem[]>([]); // 地图 marker 集合 const [markers, setMarkers] = useState<any[]>([]); const markersRef = useRef<any[]>([]); const markersSignatureRef = useRef<string>(''); const carMarkerInitialized = useRef(false); const tripStatus6MarkerRefreshedRef = useRef(false); const createPickupIconMarker = (pickupSite: any): MarkerItem | null => { // 构建上车点 marker const createPickupMarker = (pickupSite: any, useAndroidNativeTrip1Callout: boolean) => { if (!(pickupSite?.latitude && pickupSite?.longitude)) return null; const shouldUseChangePickupIcon = deps.isModifyDestinationReviewVisible && deps.tripStatus >= TripStatus.VehicleArrivedPickup; const shouldUseChangePickupIcon = deps.isModifyDestinationReviewVisible && deps.tripStatus >= TripStatus.VehicleArrivedPickup; // 修改目的地确认态且进入行程阶段时使用换点上车图标 return { id: PICKUP_ICON_MARKER_ID, id: 1, latitude: pickupSite.latitude, longitude: pickupSite.longitude, iconPath: shouldUseChangePickupIcon @@ -111,40 +60,32 @@ anchor: { x: 0.5, y: 1 }, zIndex: 1, name: pickupSite?.name || '上车点', markerType: MarkerType.PICKUP_ICON hideNameCallout: deps.isModifyDestinationReviewVisible, // 修改目的地确认态隐藏上车点名称气泡 ...(useAndroidNativeTrip1Callout ? { callout: { content: (deps.tripStatus === TripStatus.PublishRoute || deps.showTrip1StyleEndpoint) ? `${pickupSite?.name || '上车点'} >` : (pickupSite?.name || '上车点'), color: '#000000', fontSize: 12, bgColor: '#FFFFFF', borderRadius: 8, padding: 8, display: 'ALWAYS', textAlign: 'center', }, } : { customCallout: { anchorY: [TripStatus.Publishing, TripStatus.CallingCar].includes(deps.tripStatus) ? 150 : -5, anchorX: 0, display: 'ALWAYS' }, }) }; }; const createPickupTextMarker = (pickupSite: any): MarkerItem | null => { if (!(pickupSite?.latitude && pickupSite?.longitude)) return null; return { id: 1, latitude: pickupSite.latitude, longitude: pickupSite.longitude, iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/maker-up.png', width: SITE_TEXT_MARKER_SIZE, height: SITE_TEXT_MARKER_SIZE, alpha: 0, anchor: { x: 0.5, y: 1 }, zIndex: 3, name: pickupSite?.name || '上车点', markerType: MarkerType.PICKUP_TEXT, customCallout: { anchorY: SITE_TEXT_CALLOUT_ANCHOR, anchorX: 0, display: 'ALWAYS' } }; }; const createDropoffIconMarker = (dropoffSite: any): MarkerItem | null => { // 构建下车点 marker const createDropoffMarker = (dropoffSite: any, useAndroidNativeTrip1Callout: boolean) => { if (!(dropoffSite?.latitude && dropoffSite?.longitude)) return null; const dropoffName = deps.tripStatus === TripStatus.ArrivedDestination ? '已到达' : (dropoffSite.name || '目的地'); const dropoffName = [TripStatus.ArrivedDestination].includes(deps.tripStatus) ? '已到达' : (dropoffSite.name || '目的地'); return { id: DROPOFF_ICON_MARKER_ID, id: 2, latitude: dropoffSite.latitude, longitude: dropoffSite.longitude, iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/maker-down.png', @@ -153,42 +94,32 @@ anchor: { x: 0.5, y: 1 }, zIndex: 1, name: dropoffName, markerType: MarkerType.DROPOFF_ICON ...(useAndroidNativeTrip1Callout || deps.tripStatus === TripStatus.InService ? { callout: { content: (deps.tripStatus === TripStatus.PublishRoute || deps.showTrip1StyleEndpoint) ? `${dropoffName} >` : dropoffName, color: '#000000', fontSize: 12, bgColor: '#FFFFFF', borderRadius: 8, padding: 8, display: 'ALWAYS', textAlign: 'center', }, } : { customCallout: { anchorY: -5, anchorX: 0, display: 'ALWAYS' }, }) }; }; const createDropoffTextMarker = (dropoffSite: any): MarkerItem | null => { if (!(dropoffSite?.latitude && dropoffSite?.longitude)) return null; const dropoffName = deps.tripStatus === TripStatus.ArrivedDestination ? '已到达' : (dropoffSite.name || '目的地'); return { id: 2, latitude: dropoffSite.latitude, longitude: dropoffSite.longitude, iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/maker-down.png', width: SITE_TEXT_MARKER_SIZE, height: SITE_TEXT_MARKER_SIZE, alpha: 0, anchor: { x: 0.5, y: 1 }, zIndex: 3, name: dropoffName, markerType: MarkerType.DROPOFF_TEXT, customCallout: { anchorY: SITE_TEXT_CALLOUT_ANCHOR, anchorX: 0, display: 'ALWAYS' } }; }; const createBeforeUserDropoffMarker = (): MarkerItem | null => { // 构建上一单终点 marker const createBeforeUserDropoffMarker = () => { const latitude = Number(deps.beforeUserEndSite?.latitude); const longitude = Number(deps.beforeUserEndSite?.longitude); if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) return null; return { id: BEFORE_USER_DROPOFF_MARKER_ID, id: 31, latitude, longitude, iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/before-user-site.png', @@ -197,17 +128,13 @@ anchor: { x: 0.5, y: 1 }, zIndex: 1, name: '上一单终点', markerType: MarkerType.BEFORE_USER_DROPOFF, isBeforeUserSite: true, customCallout: { anchorY: SITE_TEXT_CALLOUT_ANCHOR, anchorX: 0, display: 'ALWAYS' } customCallout: { anchorY: -5, anchorX: 0, display: 'ALWAYS' } }; }; const createCarMarker = (): MarkerItem | null => { // 构建车辆 marker const createCarMarker = () => { if (!deps.vehicleLocation) return null; return { id: deps.vehicleMarkerId, @@ -220,22 +147,16 @@ zIndex: 2, rotate: typeof deps.vehicleDirection === 'number' ? deps.vehicleDirection : 0, name: '车辆位置', markerType: MarkerType.CAR, customCallout: { anchorY: CAR_CALLOUT_ANCHOR, anchorX: 0, display: 'ALWAYS' } customCallout: { anchorY: 70, anchorX: 0, display: 'ALWAYS' } }; }; const applyMarkerState = ( nextMarkers: MarkerItem[], nextMarkersSignature: string, shouldSkipUpdate: boolean, carMarker: MarkerItem | null ) => { // 应用 marker 状态并处理“行驶中”阶段的刷新标记 const applyMarkerState = (nextMarkers: any[], nextMarkersSignature: string, shouldSkipUpdate: boolean, carMarker: any) => { if (shouldSkipUpdate) { setMarkers((prevMarkers: MarkerItem[]) => { const targetCarIndex = prevMarkers.findIndex( (item: MarkerItem) => item.id === deps.vehicleMarkerId ); // 跳过整包 markers 更新时,仍兜底同步车辆 marker 位置与角度,避免朝向卡旧值 setMarkers((prevMarkers: any[]) => { const targetCarIndex = prevMarkers.findIndex((item: any) => item.id === deps.vehicleMarkerId); if (targetCarIndex === -1) { if (markersSignatureRef.current !== nextMarkersSignature) { markersSignatureRef.current = nextMarkersSignature; @@ -244,8 +165,7 @@ return prevMarkers; } const prevCar = prevMarkers[targetCarIndex] || {}; const nextCarRotate = typeof carMarker?.rotate === 'number' ? carMarker.rotate : prevCar.rotate; const nextCarRotate = typeof carMarker?.rotate === 'number' ? carMarker.rotate : prevCar.rotate; const carLocationChanged = Number(prevCar.latitude) !== Number(carMarker?.latitude) || Number(prevCar.longitude) !== Number(carMarker?.longitude); @@ -267,13 +187,7 @@ markersSignatureRef.current = nextMarkersSignature; setMarkers(nextMarkers); } if ( !deps.isAndroid && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) && deps.statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current ) { if (!deps.isAndroid && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) && deps.statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current) { tripStatus6MarkerRefreshedRef.current = true; } carMarkerInitialized.current = deps.statusRule.showCar && !!carMarker; @@ -284,86 +198,35 @@ }, [markers]); useEffect(() => { const pickupSite = deps.startSite?.latitude && deps.startSite?.longitude ? deps.startSite : Taro.getStorageSync('startSite'); const dropoffSite = deps.endSite?.latitude && deps.endSite?.longitude ? deps.endSite : Taro.getStorageSync('endSite'); const pickupIconMarker = createPickupIconMarker(pickupSite); const pickupTextMarker = createPickupTextMarker(pickupSite); const dropoffIconMarker = createDropoffIconMarker(dropoffSite); const dropoffTextMarker = createDropoffTextMarker(dropoffSite); const pickupSite = deps.startSite?.latitude && deps.startSite?.longitude ? deps.startSite : Taro.getStorageSync('startSite'); const dropoffSite = deps.endSite?.latitude && deps.endSite?.longitude ? deps.endSite : Taro.getStorageSync('endSite'); const useAndroidNativeTrip1Callout = deps.isAndroid && ([TripStatus.PublishRoute, TripStatus.VehicleArrivedPickup, TripStatus.TripFinished, TripStatus.PaymentSuccess, TripStatus.CancelOnlyPickup].includes(deps.tripStatus) || deps.showTrip1StyleEndpoint); const pickupMarker = createPickupMarker(pickupSite, useAndroidNativeTrip1Callout); const dropoffMarker = createDropoffMarker(dropoffSite, useAndroidNativeTrip1Callout); const beforeUserDropoffMarker = createBeforeUserDropoffMarker(); const shouldWaitArrivedDirection = deps.tripStatus === TripStatus.ArrivedDestination && !deps.arrivedMarkerReady; const shouldWaitArrivedDirection = deps.tripStatus === TripStatus.ArrivedDestination && !deps.arrivedMarkerReady; const effectiveShowCar = deps.statusRule.showCar && !shouldWaitArrivedDirection; const carMarker = createCarMarker(); const markerMap: Partial<Record<MarkerType, MarkerItem>> = { [MarkerType.PICKUP_ICON]: deps.statusRule.showPickup ? pickupIconMarker ?? undefined : undefined, [MarkerType.PICKUP_TEXT]: deps.statusRule.showPickup ? pickupTextMarker ?? undefined : undefined, [MarkerType.DROPOFF_ICON]: deps.statusRule.showDropoff ? dropoffIconMarker ?? undefined : undefined, [MarkerType.DROPOFF_TEXT]: deps.statusRule.showDropoff ? dropoffTextMarker ?? undefined : undefined, [MarkerType.CAR]: effectiveShowCar ? carMarker ?? undefined : undefined, [MarkerType.BEFORE_USER_DROPOFF]: beforeUserDropoffMarker ?? undefined }; const incomingMarkerOrder = Array.isArray(deps.statusRule.markerOrder) ? deps.statusRule.markerOrder : []; const mergedMarkerOrder = [ ...incomingMarkerOrder, ...DEFAULT_MARKER_ORDER.filter((item) => !incomingMarkerOrder.includes(item)) ]; const nextMarkers: MarkerItem[] = mergedMarkerOrder .map((type) => markerMap[type]) .filter((item): item is MarkerItem => Boolean(item)); const nextMarkers: any[] = []; if (deps.statusRule.showPickup && pickupMarker) nextMarkers.push(pickupMarker); if (effectiveShowCar && carMarker) nextMarkers.push(carMarker); if (deps.statusRule.showDropoff && dropoffMarker) nextMarkers.push(dropoffMarker); if (beforeUserDropoffMarker) nextMarkers.push(beforeUserDropoffMarker); const nextMarkersSignature = nextMarkers .map((m: MarkerItem) => [ .map((m: any) => [ m.id, m.markerType, Number(m.latitude).toFixed(6), Number(m.longitude).toFixed(6), m.name || '', typeof m.rotate === 'number' ? m.rotate : '', m?.customCallout?.anchorY ?? '' m?.customCallout?.anchorY ?? '', ].join('|')) .join(';'); const hasDropoffMarker = markersRef.current.some( (m: MarkerItem) => m.markerType === MarkerType.DROPOFF_ICON || m.markerType === MarkerType.DROPOFF_TEXT ); const shouldHoldMarkerByTranslate = [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) && carMarkerInitialized.current && deps.statusRule.showCar && (!deps.statusRule.showDropoff || hasDropoffMarker); const shouldSkipUpdate = shouldHoldMarkerByTranslate && (deps.isAndroid || tripStatus6MarkerRefreshedRef.current); const hasDropoffMarker = markersRef.current.some((m: any) => m.id === 2); const shouldHoldMarkerByTranslate = [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) && carMarkerInitialized.current && deps.statusRule.showCar && (!deps.statusRule.showDropoff || hasDropoffMarker); const shouldSkipUpdate = shouldHoldMarkerByTranslate && (deps.isAndroid || tripStatus6MarkerRefreshedRef.current); applyMarkerState(nextMarkers, nextMarkersSignature, shouldSkipUpdate, carMarker); }, [ deps.tripStatus, deps.startSite?.name, deps.startSite?.latitude, deps.startSite?.longitude, deps.endSite?.name, deps.endSite?.latitude, deps.endSite?.longitude, deps.beforeUserEndSite?.latitude, deps.beforeUserEndSite?.longitude, deps.vehicleLocation, deps.vehicleDirection, deps.vehicleMarkerId, deps.isAndroid, deps.statusRule.showPickup, deps.statusRule.showCar, deps.statusRule.showDropoff, deps.statusRule.markerOrder, deps.arrivedMarkerReady ]); }, [deps.tripStatus, deps.startSite?.name, deps.startSite?.latitude, deps.startSite?.longitude, deps.endSite?.name, deps.endSite?.latitude, deps.endSite?.longitude, deps.beforeUserEndSite?.latitude, deps.beforeUserEndSite?.longitude, deps.vehicleLocation, deps.vehicleDirection, deps.vehicleMarkerId, deps.isAndroid, deps.statusRule.showPickup, deps.statusRule.showCar, deps.statusRule.showDropoff]); return { markers, src/robotaxi/pages/begin-route/hooks/use-page-restore.ts
@@ -1,12 +1,10 @@ import Taro from '@tarojs/taro'; import wsManager from '../../../../utils/websocket'; import { mapOrderStatusToTripStatus } from '../../../../utils/Robotaxi-public-method'; import { getOrderActive, } from '../../../api/home'; import { queryVehicleByOrderId } from '../../../api/begin-route'; import { toTripStatus, TripStatus } from '../constants/trip-status'; import { MODIFY_DESTINATION_SESSION_KEY } from '../constants/modify-destination'; import { normalizeActiveOrderSnapshot } from './runtime-sync'; type Deps = { wsHost: string; @@ -64,27 +62,30 @@ getOrderActive() .then((res: any) => { if (!res) return; const currentOrderId = String(Taro.getStorageSync('orderId') || ''); const orderSnapshot = normalizeActiveOrderSnapshot(res, currentOrderId); if (!orderSnapshot) return; const nextStatus = toTripStatus(orderSnapshot.tripStatus, deps.getCurrentTripStatus()); deps.setTrunkStatus(![0, 2].includes(res.openTrunk)); const nextStartSite = { id: res.startPointId || '', name: res.startPointName || '', latitude: res.startLat || 0, longitude: res.startLon || 0, electronicAreaId: res.xpElectronicFenceAreaId || '', time: res.startParkingTimeLimit || 0, }; const nextEndSite = { id: res.endPointId || '', name: res.endPointName || '', latitude: res.endLat || 0, longitude: res.endLon || 0, electronicAreaId: res.xpElectronicFenceAreaId || '', time: res.endParkingTimeLimit || 0, }; const nextStatus = toTripStatus(mapOrderStatusToTripStatus(res.status || 1)); Taro.setStorageSync('tripStatus', nextStatus); deps.setTripStatus(nextStatus); if (orderSnapshot.orderId) { deps.setOrderId(orderSnapshot.orderId); Taro.setStorageSync('orderId', orderSnapshot.orderId); } if (typeof orderSnapshot.trunkStatus === 'boolean') { deps.setTrunkStatus(orderSnapshot.trunkStatus); } if (orderSnapshot.startSite) { Taro.setStorageSync('startSite', orderSnapshot.startSite); deps.setStartSite(orderSnapshot.startSite); } if (orderSnapshot.endSite) { Taro.setStorageSync('endSite', orderSnapshot.endSite); deps.setEndSite(orderSnapshot.endSite); } Taro.setStorageSync('startSite', nextStartSite); Taro.setStorageSync('endSite', nextEndSite); deps.setStartSite(nextStartSite); deps.setEndSite(nextEndSite); }) .catch(() => { }); }; src/robotaxi/pages/begin-route/hooks/use-route-manager.ts
@@ -156,12 +156,6 @@ orderRouteInitializedRef.current = false; }; // 失效路线渲染签名,确保下一次路线落地时强制刷新 polyline const resetRoutePolylineCache = () => { polylineKeyRef.current = ''; lastPolylineUpdateAtRef.current = 0; }; // 擦除已行驶路段(仅基于前后两次路线点差分,不依赖后端 served) const erasePassedRouteSegments = (newPoints: any[], _servedPoints: any[] = []) => { const oldPoints = currentFullRouteRef.current; @@ -778,7 +772,6 @@ applyRoutePoints, reducePoints, resetRouteRuntimeState, resetRoutePolylineCache, erasePassedRouteSegments, requestRouteAndFare, requestOrderRoute, src/robotaxi/pages/begin-route/hooks/use-smooth-route-motion.ts
File was deleted src/robotaxi/pages/begin-route/hooks/use-socket-fallback-polling.ts
File was deleted src/robotaxi/pages/begin-route/hooks/use-vehicle-tracking.ts
@@ -6,10 +6,6 @@ vehicleMarkerId: number; }; enum VehicleTranslateConfig { DURATION_MS = 100, } export default function useVehicleTracking({ statusRule, vehicleMarkerId @@ -26,6 +22,7 @@ const [trunkStatus, setTrunkStatus] = useState(false); const lastVehiclePointRef = useRef<{ latitude: number; longitude: number } | null>(null); const lastVehicleUpdateAtRef = useRef<number>(0); const TRANSLATE_DURATION_MS = 100; const isTranslatingRef = useRef(false); // 当前是否有进行中的平移动画 const pendingTranslateRef = useRef<{ latitude: number; @@ -51,7 +48,7 @@ autoRotate: false, moveWithRotate: true, rotate: task.rotate, duration: VehicleTranslateConfig.DURATION_MS, duration: TRANSLATE_DURATION_MS, complete: () => { isTranslatingRef.current = false; if (pendingTranslateRef.current) { src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -5,92 +5,160 @@ import CallingCar from '../components/calling-car'; import ModifyDestinationReview from '../components/modify-destination-review'; import { TripStatus } from '../constants/trip-status'; import { MarkerType } from './use-map-markers'; import { formatRemainingDurationDisplay } from '../../../utils/format'; import summaryIcon from '../../../assets/summary-icon.png'; type Deps = { /** 是否安卓 */ isAndroid: boolean; /** 行程状态 */ tripStatus: TripStatus; /** 地图缩放等级 */ mapScale: number; /** 地图 markers */ markers: any[]; /** 是否显示等待雷达 */ shouldShowWaitingRadar: boolean; /** 等待雷达可见 */ waitingRadarVisible: boolean; /** 等待雷达样式 */ waitingRadarStyle: any; /** 等待雷达资源 */ waitingRadarSrc: string; customCalloutIconSrc: string; /** 车辆气泡可见 */ carCalloutVisible: boolean; /** 车辆气泡样式 */ carCalloutStyle: any; /** 到达气泡可见 */ arriveCalloutVisible: boolean; /** 到达气泡样式 */ arriveCalloutStyle: any; /** 到达态气泡渲染就绪(角度与缩放均就绪) */ arrivedTooltipReady: boolean; /** 路线距离 */ routeDistance: { value: number; unit: 'km' | 'm' } | null; /** 路线时长 */ routeDuration: number | null; /** 是否存在上一单终点 */ hasBeforeUserEndSite: boolean; /** 自定义气泡图标 */ customCalloutIconSrc: string; /** 是否使用下单前端点气泡样式 */ showTrip1StyleEndpoint: boolean; /** 上车点 */ startSite: any; /** 下车点 */ endSite: any; /** 发单 */ callImmediately: () => void; /** 费用信息 */ fareEstimate: any; /** 打开优惠券选择弹窗 */ handleOpenCouponSelect: () => void; /** 打开乘客须知 */ handleOpenPassengerNotice: () => void; /** 步行时长 */ walkingTime: number; /** 发单禁用 */ isCallDisabled: boolean; /** 打开取消弹窗 */ handleOpenCancelPopupByTripStatus: (status: TripStatus, durationDisplay?: any) => void; /** 取消超时 */ handleOpenCancelPopupWithType: (type: number) => void; /** 行程状态设置 */ setTripStatus: (value: TripStatus) => void; /** 完成倒计时启用 */ finishCountdownEnabled: boolean; /** 订单 ID */ orderId: string; /** 车辆信息 */ vehicleInfo: any; /** 完成倒计时超时 */ handleFinishCountdownTimeout: () => void; /** 重置版本 */ resetDetailVersion: number; /** 价格版本 */ priceUpdateVersion: number; /** 评价提交版本 */ evaluateSubmitVersion: number; /** 评价关闭版本 */ evaluateCloseVersion: number; /** 打开结束弹窗 */ handleOpenOverRoutePopup: () => void; /** 支付 */ handlePay: () => void; /** 打开评价 */ handleOpenEvaluatePopup: () => void; /** 详情展开回调 */ handleToggleDetailFade: (expanded: boolean, hasFare: boolean) => void; /** 修改目的地 */ handleModifyDestination: () => void; /** 分享行程 */ handleShareTrip: () => void; /** 路线选择展开状态 */ isRouteSelectExpanded: boolean; /** 路线选择状态切换 */ onToggleRouteSelect: (expanded: boolean) => void; /** 后备箱状态 */ trunkStatus: boolean; /** 路线选项集合 */ routeOptions: any[]; /** 选中路线索引 */ selectedRouteIndex: number; /** 切换路线回调 */ handleSelectRoute: (index: number) => void; /** 地图点击 */ handleMapTap?: (e: any) => void; /** 修改目的地确认态 */ isModifyDestinationReviewVisible?: boolean; /** 修改目的地确认请求中 */ isConfirmingModifyDestination?: boolean; /** 修改前目的地 */ previousEndSite?: any; /** 修改前费用 */ previousFareEstimate?: any; /** 修改后目的地 */ previewEndSite?: any; /** 确认修改目的地 */ handleConfirmModifyDestination?: (selectedRoute?: any) => void; /** 蓝牙卡片描述文案 */ bluetoothStatusReady: boolean; /** 蓝牙卡片描述文案 */ bluetoothCardDescription: string; /** 蓝牙卡片右侧动作文案 */ bluetoothCardActionText: string; /** 点击蓝牙卡片 */ handleBluetoothCardClick: () => void; /** 蓝牙授权弹窗是否展示 */ showAuthBluetoothPopup: boolean; /** 蓝牙授权成功弹窗是否展示 */ showSuccessAuthPopup: boolean; /** 关闭蓝牙授权弹窗 */ handleCloseAuthBluetoothPopup: () => void; /** 关闭蓝牙授权成功弹窗 */ handleCloseSuccessAuthPopup: () => void; /** 去授权蓝牙 */ handleAuthorizeBluetooth: () => void; /** 页面 didShow 版本号 */ pageDidShowVersion: number; /** 排队位次轮询频率(秒) */ queuePollingTimeSeconds: number; }; /** * 页面展示委托:地图覆盖物与行程卡片渲染。 */ export default function useViewDelegates(deps: Deps) { const durationDisplay = formatRemainingDurationDisplay(deps.routeDuration); /** 时长文案 */ const durationDisplay = formatRemainingDurationDisplay(deps.routeDuration); const routeSummaryText = deps.routeDistance && deps.routeDuration !== null ? `${durationDisplay.value}${durationDisplay.unit} ${deps.routeDistance.value}${deps.routeDistance.unit === 'm' ? '米' : '公里'}` : ''; const shouldShowSiteTextBelow = [ TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService ].includes(deps.tripStatus); const shouldShowSiteArrow = deps.tripStatus === TripStatus.PublishRoute || deps.showTrip1StyleEndpoint; const shouldHideAndroidMarkerCallout = deps.isAndroid && [TripStatus.PublishRoute, TripStatus.VehicleArrivedPickup, TripStatus.TripFinished, TripStatus.PaymentSuccess, TripStatus.CancelOnlyPickup].includes(deps.tripStatus); // Android 指定状态下隐藏常规 marker 气泡 /** * 渲染等待态气泡。 */ const renderWaitingCallout = (item: any, calloutKey: string) => ( <CoverView slot='callout' marker-id={item.id} key={calloutKey} className='waiting-callout'> <CoverView className='waiting-callout-content'> @@ -103,100 +171,73 @@ </CoverView> ); const renderRouteSummaryCallout = (item: any, calloutKey: string) => ( /** * 渲染默认气泡。 */ const renderDefaultCallout = (item: any, calloutKey: string) => ( <CoverView slot='callout' marker-id={item.id} key={calloutKey} className='custom-callout'> {routeSummaryText ? ( <CoverView className='route-summary-callout'> <CoverImage className='route-summary-callout-bg' src={summaryIcon} /> <CoverView className='route-summary-callout-text'>{routeSummaryText}</CoverView> </CoverView> ) : null} </CoverView> ); const renderSiteTextCallout = (item: any, calloutKey: string) => { if ( item.markerType === MarkerType.DROPOFF_TEXT && deps.tripStatus === TripStatus.ArrivedDestination && !deps.arrivedTooltipReady ) { return null; } return ( <CoverView slot='callout' marker-id={item.id} key={calloutKey} className={`custom-callout site-text-callout ${shouldShowSiteTextBelow ? 'site-text-callout-bottom' : 'site-text-callout-top'}`} > {shouldShowSiteTextBelow && <CoverView className='site-text-bubble-arrow site-text-bubble-arrow-top' />} <CoverView className='site-text-bubble'> <CoverView className='site-text-bubble-content'> <CoverView className='site-text-label'>{item.name}</CoverView> {shouldShowSiteArrow && <CoverImage className='site-text-action-icon' src={deps.customCalloutIconSrc} />} {item.isRouteSummaryBubble ? ( routeSummaryText ? ( <CoverView className='route-summary-callout'> <CoverImage className='route-summary-callout-bg' src={summaryIcon} /> <CoverView className='route-summary-callout-text'> {routeSummaryText} </CoverView> </CoverView> </View> {!shouldShowSiteTextBelow && <CoverView className='site-text-bubble-arrow site-text-bubble-arrow-bottom' />} </CoverView> ); }; const renderBeforeUserCallout = (item: any, calloutKey: string) => ( <CoverView slot='callout' marker-id={item.id} key={calloutKey} className='custom-callout'> <CoverView className='before-user-site-callout'> <CoverView className='before-user-site-txt'>上一站终点</CoverView> </CoverView> </CoverView> ); const renderCarCallout = (item: any, calloutKey: string) => ( <CoverView slot='callout' marker-id={item.id} key={calloutKey} className='custom-callout'> <CoverView className='custom-callout-content car-callout-with-before' style={{ padding: '6px 10px', minWidth: '40px', justifyContent: 'center', zIndex: 2 }}> {deps.hasBeforeUserEndSite && ( <CoverView className='car-time car-before-order-tip'>即将完成上一单</CoverView> )} <CoverView className='car-time'> <CoverView className='car-time-num'>{deps.routeDistance?.value ?? 0}</CoverView> <CoverView>{deps.routeDistance?.unit === 'm' ? '米' : '公里'}</CoverView> <CoverView style={{ marginLeft: '2.5px' }} className='car-time-num'>{durationDisplay.value}</CoverView> <CoverView>{durationDisplay.unit}</CoverView> ) : null ) : item.isBeforeUserSite ? ( <CoverView className='before-user-site-callout'> <CoverView className='before-user-site-txt'>上一站终点</CoverView> </CoverView> </CoverView> ) : ( <> {(item.name !== '车辆位置' && !(item.id === 2 && [TripStatus.ArrivedDestination].includes(deps.tripStatus)) && !item.hideNameCallout) && ( <CoverView className={`custom-callout-content ${deps.isAndroid && 'txt-center '}`} style={{ zIndex: 1 }}> <CoverView style={{ lineHeight: '22px', display: 'flex', alignItems: 'center' }}>{item.name}</CoverView> {deps.isAndroid && <CoverView style={{ width: '2px' }}></CoverView>} {(deps.tripStatus === TripStatus.PublishRoute || deps.showTrip1StyleEndpoint) && <CoverImage className='custom-callout-icon' src={deps.customCalloutIconSrc} />} </CoverView> )} {(item.name === '车辆位置' && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) && !deps.isAndroid) && ( <CoverView className='custom-callout-content car-callout-with-before' style={{ padding: '6px 10px', minWidth: '40px', justifyContent: 'center', zIndex: 2 }}> {deps.hasBeforeUserEndSite && ( <CoverView className='car-time car-before-order-tip'>即将完成上一单</CoverView> )} <CoverView className='car-time'> <CoverView className='car-time-num'>{deps.routeDistance?.value ?? 0}</CoverView> <CoverView>{deps.routeDistance?.unit === 'm' ? '米' : '公里'}</CoverView> <CoverView style={{ marginLeft: '2.5px' }} className='car-time-num'>{durationDisplay.value}</CoverView> <CoverView>{durationDisplay.unit}</CoverView> </CoverView> </CoverView> )} {(item.id === 2 && [TripStatus.ArrivedDestination].includes(deps.tripStatus) && !deps.isAndroid && deps.arrivedTooltipReady) && ( <CoverView className='custom-callout-content'> 已到达 </CoverView> )} </> )} </CoverView> ); const renderMarkerCallout = (item: any) => { /** * 渲染单个 marker 气泡。 */ const renderMarkerCallout = (item: any, index: number) => { const calloutKey = `callout-${item.id}-${item.name || ''}`; if (item.isRouteSummaryBubble) { return renderRouteSummaryCallout(item, calloutKey); } if (item.markerType === MarkerType.BEFORE_USER_DROPOFF || item.isBeforeUserSite) { return renderBeforeUserCallout(item, calloutKey); } const isSiteIconMarker = [MarkerType.PICKUP_ICON, MarkerType.DROPOFF_ICON].includes(item.markerType); const isSiteTextMarker = [MarkerType.PICKUP_TEXT, MarkerType.DROPOFF_TEXT].includes(item.markerType); const isWaitingPickupText = [TripStatus.Publishing, TripStatus.CallingCar].includes(deps.tripStatus) && item.markerType === MarkerType.PICKUP_TEXT; if (isSiteIconMarker) return null; if (isWaitingPickupText) return renderWaitingCallout(item, calloutKey); if ( item.markerType === MarkerType.CAR && deps.isAndroid && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) ) { return null; } if (isSiteTextMarker) return renderSiteTextCallout(item, calloutKey); if ( item.markerType === MarkerType.CAR && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) ) { return renderCarCallout(item, calloutKey); } return null; // 路线摘要气泡需跨平台强制显示,避免被 Android 普通气泡隐藏规则误伤 if (item.isRouteSummaryBubble) return renderDefaultCallout(item, calloutKey); if (shouldHideAndroidMarkerCallout && !item.isBeforeUserSite) return null; const isWaiting = [TripStatus.Publishing, TripStatus.CallingCar].includes(deps.tripStatus) && index === 0; if (isWaiting) return renderWaitingCallout(item, calloutKey); if (item.name === '车辆位置' && deps.isAndroid && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus)) return null; return renderDefaultCallout(item, calloutKey); }; /** * 渲染地图覆盖层。 */ const renderMapOverlays = () => ( <> {deps.markers.map(renderMarkerCallout)} @@ -218,9 +259,19 @@ </View> </View> )} {deps.isAndroid && deps.arriveCalloutVisible && deps.arrivedTooltipReady && ( <View className="car-callout-android" style={deps.arriveCalloutStyle}> <View className="custom-callout-content"> 已到达 </View> </View> )} </> ); /** * 渲染底部行程卡片。 */ const renderTripCard = () => { if (deps.tripStatus === TripStatus.PublishRoute && deps.startSite && deps.endSite) { return ( src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts
@@ -1,345 +1,492 @@ import { useEffect, useRef } from 'react'; import type { MutableRefObject } from 'react'; import Taro from '@tarojs/taro'; import wsManager from '../../../../utils/websocket'; import { queryVehicleByOrderId } from '../../../api/begin-route'; import { clearOrderTripStorage } from '../../../../utils/Robotaxi-public-method'; import { TripStatus, toTripStatus } from '../constants/trip-status'; import { applyOrderSnapshot, normalizeSocketOrderStatusSnapshot, normalizeSocketRouteSnapshot } from './runtime-sync'; import { clearOrderTripStorage, safeParse } from '../../../../utils/Robotaxi-public-method'; import { normalizeDistanceKm, normalizeDurationSeconds } from '../../../../robotaxi/utils/format'; import { toTripStatus, TripStatus } from '../constants/trip-status'; type UseWebSocketEventsDeps = { tripStatusRef: MutableRefObject<TripStatus>; orderId: string; setOrderId: (orderId: string) => void; setTripStatus: (status: TripStatus) => void; setVehicleInfo: (vehicle: any) => void; setPriceUpdateVersion: (updater: (v: number) => number) => void; setFinishCountdownEnabled: (value: boolean) => void; setShowAnnouncementsPopup: (value: boolean) => void; setRoutePoints?: (points: any[]) => void; setPolyline?: (lines: any[]) => void; setCancelType: (value: number) => void; setShowCancelPopup: (value: boolean) => void; checkAndShowPassengerNotice: () => void; setVehicleMoveAlongPoints?: (points: any[]) => void; setBeforeUserEndSite?: (site: { longitude: number; latitude: number } | null) => void; setStartSite?: (value: any) => void; setEndSite?: (value: any) => void; setTrunkStatus?: (value: boolean) => void; applySmoothRouteSnapshot?: (payload: any) => void; resetSmoothRouteMotion?: () => void; polylineKeyRef?: MutableRefObject<string>; lastPolylineUpdateAtRef?: MutableRefObject<number>; lastRoutePointRef?: MutableRefObject<any>; lastRouteUpdateAtRef?: MutableRefObject<number>; lastVehiclePointRef?: MutableRefObject<any>; lastVehicleUpdateAtRef?: MutableRefObject<number>; currentFullRouteRef?: MutableRefObject<any[]>; lastErasedIndexRef?: MutableRefObject<number>; orderRouteInitializedRef?: MutableRefObject<boolean>; tripStatusRef: React.MutableRefObject<TripStatus>; orderId: string; setOrderId: (orderId: string) => void; setTripStatus: (status: TripStatus) => void; setVehicleInfo: (vehicle: any) => void; vehicleInfo: any; setPriceUpdateVersion: (updater: (v: number) => number) => void; setFinishCountdownEnabled: (value: boolean) => void; setShowAnnouncementsPopup: (value: boolean) => void; setRoutePoints: (points: any[]) => void; setPolyline: (lines: any[]) => void; polylineKeyRef: any; lastPolylineUpdateAtRef: any; lastRoutePointRef: any; lastRouteUpdateAtRef: any; lastVehiclePointRef: any; lastVehicleUpdateAtRef: any; currentFullRouteRef: any; lastErasedIndexRef: any; orderRouteInitializedRef: any; setCancelType: (value: number) => void; setShowCancelPopup: (value: boolean) => void; checkAndShowPassengerNotice: () => void; setVehicleDirection: (value: number) => void; setVehicleMoveAlongPoints: (points: any[]) => void; setRouteDistance: (value: any) => void; setRouteDuration: (value: number) => void; setBeforeUserEndSite: (site: { longitude: number; latitude: number } | null) => void; erasePassedRouteSegments: (points: any[], servedPoints?: any[]) => void; applyRoutePoints: (points: any[]) => boolean; updateVehicleLocationThrottled: (lat: number, lng: number, force?: boolean) => void; onOrderUpdateLineRendered?: (points: any[]) => void; }; const resolveOrderId = (payload: any, fallbackOrderId: string) => payload?.content?.orderId || payload?.orderId || fallbackOrderId || Taro.getStorageSync('orderId'); // 解析“上一单乘客下车点”坐标,任一坐标无效则返回空,供 marker 显隐控制。 const parseBeforeUserEndSite = (payload: any) => { const longitude = Number(payload?.content?.endLongitude); const latitude = Number(payload?.content?.endlatitude); if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) { return null; } return { longitude, latitude }; }; const isSameOrder = (incomingOrderId: string, currentOrderId: string) => { if (!incomingOrderId && !currentOrderId) return true; if (!incomingOrderId || !currentOrderId) return false; return String(incomingOrderId) === String(currentOrderId); }; const parseBeforeUserEndSite = (payload: any) => { const longitude = Number(payload?.content?.endLongitude); const latitude = Number(payload?.content?.endlatitude); if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) { return null; } return { longitude, latitude }; if (!incomingOrderId && !currentOrderId) return true; if (!incomingOrderId || !currentOrderId) return false; return String(incomingOrderId) === String(currentOrderId); }; const resetRouteRuntimeState = (deps: UseWebSocketEventsDeps) => { deps.setRoutePoints?.([]); deps.setPolyline?.([]); deps.setVehicleMoveAlongPoints?.([]); deps.resetSmoothRouteMotion?.(); if (deps.polylineKeyRef) deps.polylineKeyRef.current = ''; if (deps.lastPolylineUpdateAtRef) deps.lastPolylineUpdateAtRef.current = 0; if (deps.lastRoutePointRef) deps.lastRoutePointRef.current = null; if (deps.lastRouteUpdateAtRef) deps.lastRouteUpdateAtRef.current = 0; if (deps.lastVehiclePointRef) deps.lastVehiclePointRef.current = null; if (deps.lastVehicleUpdateAtRef) deps.lastVehicleUpdateAtRef.current = 0; if (deps.currentFullRouteRef) deps.currentFullRouteRef.current = []; if (deps.lastErasedIndexRef) deps.lastErasedIndexRef.current = 0; if (deps.orderRouteInitializedRef) deps.orderRouteInitializedRef.current = false; deps.setRoutePoints([]); deps.setPolyline([]); deps.setVehicleMoveAlongPoints([]); deps.polylineKeyRef.current = ''; deps.lastPolylineUpdateAtRef.current = 0; deps.lastRoutePointRef.current = null; deps.lastRouteUpdateAtRef.current = 0; deps.lastVehiclePointRef.current = null; deps.lastVehicleUpdateAtRef.current = 0; deps.currentFullRouteRef.current = []; deps.lastErasedIndexRef.current = 0; deps.orderRouteInitializedRef.current = false; }; const syncTripStatus = (payload: any, deps: UseWebSocketEventsDeps) => { const incomingTripStatus = Number(payload?.tripStatus); if (!Number.isFinite(incomingTripStatus)) { return null; } const currentTripStatus = deps.tripStatusRef.current; const mappedTripStatus = toTripStatus(incomingTripStatus, currentTripStatus); // 行程进行关键态不允许被回退,避免 4/6 被延迟状态覆盖导致路线链路重置 if ([TripStatus.VehicleApproaching, TripStatus.InService].includes(currentTripStatus) && mappedTripStatus < currentTripStatus) { return currentTripStatus; } if (currentTripStatus === mappedTripStatus) { return mappedTripStatus; } deps.setTripStatus(mappedTripStatus); deps.tripStatusRef.current = mappedTripStatus; Taro.setStorageSync('tripStatus', mappedTripStatus); return mappedTripStatus; }; const syncVehicleInfo = ( effectiveOrderId: string, deps: UseWebSocketEventsDeps, lastFetchedVehicleOrderIdRef: MutableRefObject<string | null> effectiveOrderId: string, deps: UseWebSocketEventsDeps, lastFetchedVehicleOrderIdRef: any ) => { if (!effectiveOrderId) return; const shouldFetchVehicle = lastFetchedVehicleOrderIdRef.current !== effectiveOrderId; if (deps.orderId !== effectiveOrderId) { deps.setOrderId(effectiveOrderId); Taro.setStorageSync('orderId', effectiveOrderId); } if (!shouldFetchVehicle) return; lastFetchedVehicleOrderIdRef.current = effectiveOrderId; const requestedOrderId = String(effectiveOrderId); queryVehicleByOrderId({ orderId: effectiveOrderId }) .then((res: any) => { const latestOrderId = String(Taro.getStorageSync('orderId') || ''); if (latestOrderId !== requestedOrderId) return; deps.setVehicleInfo(res); }) .catch(() => { }); if (!effectiveOrderId) return; const shouldFetchVehicle = lastFetchedVehicleOrderIdRef.current !== effectiveOrderId; if (deps.orderId !== effectiveOrderId) { deps.setOrderId(effectiveOrderId); Taro.setStorageSync('orderId', effectiveOrderId); } if (!shouldFetchVehicle) return; lastFetchedVehicleOrderIdRef.current = effectiveOrderId; const requestedOrderId = String(effectiveOrderId); queryVehicleByOrderId({ orderId: effectiveOrderId }) .then((res: any) => { const latestOrderId = String(Taro.getStorageSync('orderId') || ''); if (latestOrderId !== requestedOrderId) { return; } deps.setVehicleInfo(res); }) .catch(() => { }); }; const handleCancelPopupPush = ( showCancelPopup: boolean, changeStatusOld: TripStatus, deps: UseWebSocketEventsDeps ) => { if (!showCancelPopup) return; if ([TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(changeStatusOld)) { deps.setOrderId(''); deps.setVehicleInfo({}); clearOrderTripStorage(); Taro.removeStorageSync('pickupEndTime'); Taro.removeStorageSync('waitingTimerStartAt'); Taro.removeStorageSync('waitingTimerElapsed'); deps.setCancelType(4); } else { deps.setCancelType(5); } deps.setTripStatus(TripStatus.CancelOnlyPickup); deps.setShowCancelPopup(true); const handleCancelPopupPush = (payload: any, changeStatusOld: TripStatus, deps: UseWebSocketEventsDeps) => { if (!payload?.showCancelPopup) return; if (changeStatusOld === TripStatus.VehicleApproaching || changeStatusOld === TripStatus.VehicleArrivedPickup) { deps.setOrderId(''); deps.setVehicleInfo({}); clearOrderTripStorage(); Taro.removeStorageSync('pickupEndTime'); Taro.removeStorageSync('waitingTimerStartAt'); Taro.removeStorageSync('waitingTimerElapsed'); deps.setCancelType(4); } else { deps.setCancelType(5); } deps.setTripStatus(TripStatus.CancelOnlyPickup); deps.setShowCancelPopup(true); }; const handleStatusSideEffects = ( mappedTripStatus: TripStatus | null, changeStatusOld: TripStatus, deps: UseWebSocketEventsDeps ) => { if (mappedTripStatus === TripStatus.InService) { deps.setShowAnnouncementsPopup(false); } if ( mappedTripStatus === TripStatus.VehicleArrivedPickup && changeStatusOld !== TripStatus.VehicleArrivedPickup ) { resetRouteRuntimeState(deps); } if ( mappedTripStatus === TripStatus.VehicleApproaching && changeStatusOld !== TripStatus.VehicleApproaching ) { resetRouteRuntimeState(deps); } const handleStatusSideEffects = (mappedTripStatus: TripStatus | null, changeStatusOld: TripStatus, deps: UseWebSocketEventsDeps) => { if (mappedTripStatus === TripStatus.InService) { deps.setShowAnnouncementsPopup(false); } if (mappedTripStatus === TripStatus.VehicleArrivedPickup && changeStatusOld !== TripStatus.VehicleArrivedPickup) { resetRouteRuntimeState(deps); } if (mappedTripStatus === TripStatus.VehicleApproaching && changeStatusOld !== TripStatus.VehicleApproaching) { resetRouteRuntimeState(deps); } }; const handleOrderStatusPayload = ( payload: any, deps: UseWebSocketEventsDeps, lastFetchedVehicleOrderIdRef: MutableRefObject<string | null> payload: any, deps: UseWebSocketEventsDeps, lastFetchedVehicleOrderIdRef: any ) => { const changeStatusOld = deps.tripStatusRef.current; const currentOrderId = deps.orderId || Taro.getStorageSync('orderId'); const incomingOrderId = payload?.content?.orderId || payload?.orderId; if (!isSameOrder(incomingOrderId, currentOrderId)) { return; } deps.setBeforeUserEndSite?.(parseBeforeUserEndSite(payload)); const changeStatusOld = deps.tripStatusRef.current; const currentOrderId = deps.orderId || Taro.getStorageSync('orderId'); const incomingOrderId = payload?.content?.orderId || payload?.orderId; if (!isSameOrder(incomingOrderId, currentOrderId)) { return; } deps.setBeforeUserEndSite(parseBeforeUserEndSite(payload)); const orderSnapshot = normalizeSocketOrderStatusSnapshot(payload, currentOrderId); const { nextOrderId, nextTripStatus } = applyOrderSnapshot(orderSnapshot, { orderId: String(deps.orderId || ''), tripStatusRef: deps.tripStatusRef as MutableRefObject<number>, setOrderId: deps.setOrderId, setTripStatus: (value: number) => deps.setTripStatus(toTripStatus(value, deps.tripStatusRef.current)), setStartSite: deps.setStartSite || (() => { }), setEndSite: deps.setEndSite || (() => { }), setTrunkStatus: deps.setTrunkStatus || (() => { }) }, { protectCriticalTripStatus: true }); const resolvedTripStatus = toTripStatus(nextTripStatus, changeStatusOld); syncVehicleInfo(nextOrderId, deps, lastFetchedVehicleOrderIdRef); handleStatusSideEffects(resolvedTripStatus, changeStatusOld, deps); handleCancelPopupPush(orderSnapshot.showCancelPopup === true, changeStatusOld, deps); if (orderSnapshot.showPassengerNotice) { deps.checkAndShowPassengerNotice(); } const mappedTripStatus = syncTripStatus(payload, deps); const effectiveOrderId = resolveOrderId(payload, currentOrderId); syncVehicleInfo(effectiveOrderId, deps, lastFetchedVehicleOrderIdRef); handleStatusSideEffects(mappedTripStatus, changeStatusOld, deps); handleCancelPopupPush(payload, changeStatusOld, deps); if (payload?.showPassengerNotice) { deps.checkAndShowPassengerNotice(); } }; // 统一解析方向值 const resolveDirectionValue = (payload: any) => { const rawDirection = payload?.content?.direction; const direction = Number(rawDirection); if (!Number.isFinite(direction)) { return null; } return ((direction % 360) + 360) % 360; }; const updateRouteSummary = (payload: any, deps: UseWebSocketEventsDeps) => { const normalizedDirection = resolveDirectionValue(payload); if (typeof normalizedDirection === 'number') { deps.setVehicleDirection(normalizedDirection); } const route = safeParse(payload?.content?.route, {}); const distanceKm = normalizeDistanceKm(Number(route?.distance)); if (distanceKm !== null) { deps.setRouteDistance(distanceKm); } const durationSeconds = normalizeDurationSeconds(Number(route?.duration)); if (durationSeconds !== null) { deps.setRouteDuration(durationSeconds); } }; const resolveVehiclePoint = (points: any[], servedPoints: any[] = []) => { const servedLastPoint = Array.isArray(servedPoints) && servedPoints.length > 0 ? servedPoints[servedPoints.length - 1] : null; if (servedLastPoint && typeof servedLastPoint.latitude === 'number' && typeof servedLastPoint.longitude === 'number') { return servedLastPoint; } const firstPoint = points?.[0]; if (firstPoint && typeof firstPoint.latitude === 'number' && typeof firstPoint.longitude === 'number') { return firstPoint; } return null; }; const resolveVehiclePointForErase = (points: any[], servedPoints: any[] = []) => { const firstPoint = points?.[0]; if (firstPoint && typeof firstPoint.latitude === 'number' && typeof firstPoint.longitude === 'number') { return firstPoint; } return resolveVehiclePoint(points, servedPoints); }; const syncVehiclePointWithRoute = (points: any[], deps: UseWebSocketEventsDeps, servedPoints: any[] = []) => { const targetPoint = resolveVehiclePoint(points, servedPoints); if (!targetPoint) { return; } const currentTripStatus = deps.tripStatusRef.current; if (currentTripStatus === TripStatus.ArrivedDestination) { const lastPoint = deps.lastVehiclePointRef.current; const latDiff = Math.abs(Number(lastPoint?.latitude || 0) - targetPoint.latitude); const lngDiff = Math.abs(Number(lastPoint?.longitude || 0) - targetPoint.longitude); const almostSamePoint = latDiff < 0.000003 && lngDiff < 0.000003; if (almostSamePoint) { return; } } deps.updateVehicleLocationThrottled(targetPoint.latitude, targetPoint.longitude, true); }; const handleEraseRouteStatus = (points: any[], servedPoints: any[], deps: UseWebSocketEventsDeps) => { const targetPoint = resolveVehiclePointForErase(points, servedPoints); if (targetPoint) { deps.updateVehicleLocationThrottled(targetPoint.latitude, targetPoint.longitude, true); } if (deps.orderRouteInitializedRef.current && deps.currentFullRouteRef.current.length > 0) { deps.erasePassedRouteSegments(points); } else { deps.orderRouteInitializedRef.current = true; deps.currentFullRouteRef.current = [...points]; deps.lastErasedIndexRef.current = 0; Taro.nextTick(() => { deps.applyRoutePoints(points); }); } }; const handleNormalRouteStatus = (points: any[], deps: UseWebSocketEventsDeps, servedPoints: any[] = []) => { Taro.nextTick(() => { const updated = deps.applyRoutePoints(points); if (!updated) return; if (![TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService, TripStatus.ArrivedDestination].includes(deps.tripStatusRef.current)) return; syncVehiclePointWithRoute(points, deps, servedPoints); }); }; const handleForceFullRouteRedraw = (points: any[], deps: UseWebSocketEventsDeps, servedPoints: any[] = []) => { deps.setRoutePoints([]); deps.setPolyline([]); deps.orderRouteInitializedRef.current = false; deps.currentFullRouteRef.current = []; deps.lastErasedIndexRef.current = 0; deps.polylineKeyRef.current = ''; deps.lastPolylineUpdateAtRef.current = 0; deps.lastRoutePointRef.current = null; deps.lastRouteUpdateAtRef.current = 0; Taro.nextTick(() => { deps.orderRouteInitializedRef.current = points.length > 0; deps.currentFullRouteRef.current = [...points]; deps.lastErasedIndexRef.current = 0; deps.applyRoutePoints(points); if (points.length > 0) { syncVehiclePointWithRoute(points, deps, servedPoints); } if (typeof deps.onOrderUpdateLineRendered === 'function') { // 等路线点与折线提交渲染后再触发视口重算,避免 includePoints 早于渲染导致跳动。 Taro.nextTick(() => { deps.onOrderUpdateLineRendered?.(points); }); } }); }; const handleOrderRoutePayload = (payload: any, deps: UseWebSocketEventsDeps) => { deps.setBeforeUserEndSite?.(parseBeforeUserEndSite(payload)); const routeSnapshot = normalizeSocketRouteSnapshot(payload); deps.applySmoothRouteSnapshot?.(routeSnapshot); deps.setBeforeUserEndSite(parseBeforeUserEndSite(payload)); const points = payload?.points || []; const servedPoints = Array.isArray(payload?.servedPoints) ? payload.servedPoints : []; const forceFullRedraw = payload?.forceFullRedraw === true; const currentTripStatus = deps.tripStatusRef.current; updateRouteSummary(payload, deps); // 行程结束态仅更新概要信息,不再消费路线点,避免视口反复重算造成跳动 if ([TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(currentTripStatus)) { return; } if ([TripStatus.VehicleApproaching, TripStatus.InService].includes(currentTripStatus)) { deps.setVehicleMoveAlongPoints(servedPoints); } if (forceFullRedraw) { handleForceFullRouteRedraw(points, deps, servedPoints); return; } const isEraseStatus = ![TripStatus.ArrivedDestination, TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(currentTripStatus); if (isEraseStatus) { handleEraseRouteStatus(points, servedPoints, deps); return; } handleNormalRouteStatus(points, deps, servedPoints); }; export default function useWebSocketEvents(deps: UseWebSocketEventsDeps) { const depsRef = useRef(deps); useEffect(() => { depsRef.current = deps; }, [deps]); // 持有最新依赖,避免渲染过程反复解绑/重绑监听 const depsRef = useRef(deps); useEffect(() => { depsRef.current = deps; }, [deps]); const authSuccessBound = useRef(false); const messageBound = useRef(false); const orderStatusBound = useRef(false); const orderRouteBound = useRef(false); const openTrunkBound = useRef(false); const orderUpdatePriceBound = useRef(false); const orderReassignBound = useRef(false); // 各事件是否已绑定标记 const authSuccessBound = useRef(false); const messageBound = useRef(false); const orderStatusBound = useRef(false); const orderRouteBound = useRef(false); const openTrunkBound = useRef(false); const orderUpdatePriceBound = useRef(false); const orderReassignBound = useRef(false); const authSuccessHandlerRef = useRef<(data: any) => void>(); const messageHandlerRef = useRef<(data: any) => void>(); const orderStatusHandlerRef = useRef<(data: any) => void>(); const orderRouteHandlerRef = useRef<(data: any) => void>(); const openTrunkHandlerRef = useRef<(data: any) => void>(); const orderUpdatePriceHandlerRef = useRef<(data: any) => void>(); const orderReassignHandlerRef = useRef<(data: any) => void>(); const socketOpenHandlerRef = useRef<(() => void) | null>(null); const lastFetchedVehicleOrderIdRef = useRef<string | null>(null); const authSuccessHandlerRef = useRef<(data: any) => void>(); const messageHandlerRef = useRef<(data: any) => void>(); const orderStatusHandlerRef = useRef<(data: any) => void>(); const orderRouteHandlerRef = useRef<(data: any) => void>(); const openTrunkHandlerRef = useRef<(data: any) => void>(); const orderUpdatePriceHandlerRef = useRef<(data: any) => void>(); const orderReassignHandlerRef = useRef<(data: any) => void>(); const socketOpenHandlerRef = useRef<(() => void) | null>(null); const lastFetchedVehicleOrderIdRef = useRef<string | null>(null); const bindSocketListeners = () => { if (!authSuccessBound.current && authSuccessHandlerRef.current) { wsManager.on('auth_success', authSuccessHandlerRef.current); authSuccessBound.current = true; } if (!orderStatusBound.current && orderStatusHandlerRef.current) { wsManager.on('order_status', orderStatusHandlerRef.current); orderStatusBound.current = true; } if (!orderRouteBound.current && orderRouteHandlerRef.current) { wsManager.on('order_route', orderRouteHandlerRef.current); orderRouteBound.current = true; } if (!openTrunkBound.current && openTrunkHandlerRef.current) { wsManager.on('open_trunk', openTrunkHandlerRef.current); openTrunkBound.current = true; } if (!orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) { wsManager.on('order_update_price', orderUpdatePriceHandlerRef.current); orderUpdatePriceBound.current = true; } if (!orderReassignBound.current && orderReassignHandlerRef.current) { wsManager.on('order_reassign', orderReassignHandlerRef.current); orderReassignBound.current = true; } }; // 仅初始化一次事件处理器与解绑逻辑 useEffect(() => { authSuccessHandlerRef.current = () => { if (!messageBound.current && messageHandlerRef.current) { wsManager.on('message', messageHandlerRef.current); messageBound.current = true; } }; messageHandlerRef.current = (data) => { console.log('ws message:', data); }; openTrunkHandlerRef.current = (payload) => { const currentDeps = depsRef.current; const isOpen = Number(payload?.isOpen); if (isOpen === 1) { currentDeps.setFinishCountdownEnabled(true); } }; orderUpdatePriceHandlerRef.current = (payload) => { const currentDeps = depsRef.current; const incomingOrderId = payload?.orderId || payload?.content?.orderId || Taro.getStorageSync('orderId'); const currentOrderId = currentDeps.orderId || Taro.getStorageSync('orderId'); if (!isSameOrder(incomingOrderId, currentOrderId)) { return; } currentDeps.setPriceUpdateVersion((v: number) => v + 1); }; orderReassignHandlerRef.current = (payload) => { const currentDeps = depsRef.current; const incomingOrderId = payload?.orderId || payload?.content?.orderId || Taro.getStorageSync('orderId'); const currentOrderId = currentDeps.orderId || Taro.getStorageSync('orderId'); if (!isSameOrder(incomingOrderId, currentOrderId)) { return; } lastFetchedVehicleOrderIdRef.current = null; const requestedOrderId = String(currentOrderId || ''); queryVehicleByOrderId({ orderId: currentOrderId }) .then((res: any) => { const latestOrderId = String((depsRef.current.orderId || Taro.getStorageSync('orderId') || '')); if (!requestedOrderId || latestOrderId !== requestedOrderId) { return; } currentDeps.setVehicleInfo(res); }) .catch(() => { }); }; orderStatusHandlerRef.current = (payload) => { handleOrderStatusPayload(payload, depsRef.current, lastFetchedVehicleOrderIdRef); }; orderRouteHandlerRef.current = (payload) => { handleOrderRoutePayload(payload, depsRef.current); }; // 处理重进页面时 useDidShow 先于 handler 初始化的场景,确保已连接状态下也能完成监听绑定 if (wsManager.isConnectedStatus()) { bindSocketListeners(); } useEffect(() => { authSuccessHandlerRef.current = () => { if (!messageBound.current && messageHandlerRef.current) { wsManager.on('message', messageHandlerRef.current); messageBound.current = true; } return () => { if (authSuccessBound.current && authSuccessHandlerRef.current) { wsManager.off('auth_success', authSuccessHandlerRef.current); authSuccessBound.current = false; } if (messageBound.current && messageHandlerRef.current) { wsManager.off('message', messageHandlerRef.current); messageBound.current = false; } if (orderStatusBound.current && orderStatusHandlerRef.current) { wsManager.off('order_status', orderStatusHandlerRef.current); orderStatusBound.current = false; } if (orderRouteBound.current && orderRouteHandlerRef.current) { wsManager.off('order_route', orderRouteHandlerRef.current); orderRouteBound.current = false; } if (openTrunkBound.current && openTrunkHandlerRef.current) { wsManager.off('open_trunk', openTrunkHandlerRef.current); openTrunkBound.current = false; } if (orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) { wsManager.off('order_update_price', orderUpdatePriceHandlerRef.current); orderUpdatePriceBound.current = false; } if (orderReassignBound.current && orderReassignHandlerRef.current) { wsManager.off('order_reassign', orderReassignHandlerRef.current); orderReassignBound.current = false; } if (socketOpenHandlerRef.current) { wsManager.off('open', socketOpenHandlerRef.current); socketOpenHandlerRef.current = null; } }; }, []); // 绑定业务事件监听 const bindSocketListeners = () => { if (!authSuccessBound.current && authSuccessHandlerRef.current) { wsManager.on('auth_success', authSuccessHandlerRef.current); authSuccessBound.current = true; } if (!orderStatusBound.current && orderStatusHandlerRef.current) { wsManager.on('order_status', orderStatusHandlerRef.current); orderStatusBound.current = true; } if (!orderRouteBound.current && orderRouteHandlerRef.current) { wsManager.on('order_route', orderRouteHandlerRef.current); orderRouteBound.current = true; } if (!openTrunkBound.current && openTrunkHandlerRef.current) { wsManager.on('open_trunk', openTrunkHandlerRef.current); openTrunkBound.current = true; } if (!orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) { wsManager.on('order_update_price', orderUpdatePriceHandlerRef.current); orderUpdatePriceBound.current = true; } if (!orderReassignBound.current && orderReassignHandlerRef.current) { wsManager.on('order_reassign', orderReassignHandlerRef.current); orderReassignBound.current = true; } }; messageHandlerRef.current = (data) => { console.log('ws message:', data); }; openTrunkHandlerRef.current = (payload) => { const currentDeps = depsRef.current; const isOpen = Number(payload?.isOpen); if (isOpen === 1) { currentDeps.setFinishCountdownEnabled(true); } }; orderUpdatePriceHandlerRef.current = (payload) => { const currentDeps = depsRef.current; const incomingOrderId = payload?.orderId || payload?.content?.orderId || Taro.getStorageSync('orderId'); const currentOrderId = currentDeps.orderId || Taro.getStorageSync('orderId'); if (!isSameOrder(incomingOrderId, currentOrderId)) { return; } currentDeps.setPriceUpdateVersion((v: number) => v + 1); }; orderReassignHandlerRef.current = (payload) => { const currentDeps = depsRef.current; const incomingOrderId = payload?.orderId || payload?.content?.orderId || Taro.getStorageSync('orderId'); const currentOrderId = currentDeps.orderId || Taro.getStorageSync('orderId'); if (!isSameOrder(incomingOrderId, currentOrderId)) { return; } lastFetchedVehicleOrderIdRef.current = null; const requestedOrderId = String(currentOrderId || ''); queryVehicleByOrderId({ orderId: currentOrderId }) .then((res: any) => { const latestOrderId = String((depsRef.current.orderId || Taro.getStorageSync('orderId') || '')); if (!requestedOrderId || latestOrderId !== requestedOrderId) { // 确保连接可用并完成监听绑定 const ensureSocketListeners = (host: string) => { if (wsManager.isConnectedStatus()) { bindSocketListeners(); return; } currentDeps.setVehicleInfo(res); }) .catch(() => { }); } if (socketOpenHandlerRef.current) { wsManager.connect(host); return; } const handleOpen = () => { bindSocketListeners(); }; socketOpenHandlerRef.current = handleOpen; wsManager.on('open', handleOpen); wsManager.connect(host); }; orderStatusHandlerRef.current = (payload) => { handleOrderStatusPayload(payload, depsRef.current, lastFetchedVehicleOrderIdRef); }; orderRouteHandlerRef.current = (payload) => { handleOrderRoutePayload(payload, depsRef.current); }; if (wsManager.isConnectedStatus()) { bindSocketListeners(); } return () => { if (authSuccessBound.current && authSuccessHandlerRef.current) { wsManager.off('auth_success', authSuccessHandlerRef.current); authSuccessBound.current = false; } if (messageBound.current && messageHandlerRef.current) { wsManager.off('message', messageHandlerRef.current); messageBound.current = false; } if (orderStatusBound.current && orderStatusHandlerRef.current) { wsManager.off('order_status', orderStatusHandlerRef.current); orderStatusBound.current = false; } if (orderRouteBound.current && orderRouteHandlerRef.current) { wsManager.off('order_route', orderRouteHandlerRef.current); orderRouteBound.current = false; } if (openTrunkBound.current && openTrunkHandlerRef.current) { wsManager.off('open_trunk', openTrunkHandlerRef.current); openTrunkBound.current = false; } if (orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) { wsManager.off('order_update_price', orderUpdatePriceHandlerRef.current); orderUpdatePriceBound.current = false; } if (orderReassignBound.current && orderReassignHandlerRef.current) { wsManager.off('order_reassign', orderReassignHandlerRef.current); orderReassignBound.current = false; } if (socketOpenHandlerRef.current) { wsManager.off('open', socketOpenHandlerRef.current); socketOpenHandlerRef.current = null; } }; }, []); const ensureSocketListeners = (host: string) => { if (wsManager.isConnectedStatus()) { bindSocketListeners(); return; } if (socketOpenHandlerRef.current) { wsManager.connect(host); return; } const handleOpen = () => { bindSocketListeners(); return { ensureSocketListeners, bindSocketListeners }; socketOpenHandlerRef.current = handleOpen; wsManager.on('open', handleOpen); wsManager.connect(host); }; return { ensureSocketListeners, bindSocketListeners }; } src/robotaxi/pages/begin-route/index.less
@@ -96,75 +96,6 @@ flex-direction: column; } .site-text-callout { transform: translateY(48px); } .site-text-callout { position: relative; } .site-text-callout-top { transform: translateY(-48px); } .site-text-callout-bottom { transform: translateY(48px); } .site-text-bubble { display: flex; align-items: center; justify-content: center; max-width: 8em; border-radius: 16px; background: rgba(255, 255, 255, 0.96); box-shadow: 0px 4px 16px 0px rgba(45, 48, 64, 0.2); padding: 8px 20px; } .site-text-bubble-content { display: flex; align-items: center; justify-content: center; gap: 8px; } .site-text-bubble-arrow { align-self: center; width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; } .site-text-bubble-arrow-top { margin-bottom: -2px; border-bottom: 12px solid rgba(255, 255, 255, 0.96); } .site-text-bubble-arrow-bottom { margin-top: -2px; border-top: 12px solid rgba(255, 255, 255, 0.96); } .site-text-label { max-width: 8em; font-size: 24px; font-weight: 500; color: rgba(0, 0, 0, 0.9); line-height: 34px; text-align: center; white-space: normal; word-break: break-all; } .site-text-action-icon { width: 35px; height: 35px; flex-shrink: 0; } .pd-6-10 { padding: 8px 20px !important; } @@ -340,12 +271,14 @@ .waiting-callout-txt { z-index: 200; border-radius: 16px; color: rgba(0, 0, 0, 0.9); font-size: 24px; font-weight: 500; line-height: 36px; font-weight: 400; background: rgba(255, 255, 255, 1); padding: 8px 20px; line-height: 48px; display: flex; text-shadow: 0px 2px 8px rgba(255, 255, 255, 0.9); } .pr-6 { src/robotaxi/pages/begin-route/index.tsx
@@ -15,8 +15,6 @@ import useMapMarkers from './hooks/use-map-markers'; // 车辆追踪状态管理 import useVehicleTracking from './hooks/use-vehicle-tracking'; // 行程平滑运动编排 import useSmoothRouteMotion from './hooks/use-smooth-route-motion'; // Android 覆盖物状态管理 import useAndroidOverlays from './hooks/use-android-overlays'; // 地图视口状态管理 @@ -32,8 +30,6 @@ import useTripRuntime from './hooks/use-trip-runtime'; // 订单动作编排 import useOrderActions from './hooks/use-order-actions'; // socket 掉线兜底轮询编排 import useSocketFallbackPolling from './hooks/use-socket-fallback-polling'; // 视图委托渲染编排 import useViewDelegates from './hooks/use-view-delegates'; // 修改目的地流程编排 @@ -52,7 +48,6 @@ import originIcon from '../../../robotaxi/assets/origin-icon.png' import packUpIcon from '../../../robotaxi/assets/pack-up.png' import { _ROBOTAXI_WS_HOST } from '../../../constants/api'; import { queryOrderRoute, queryOrderTrajectory } from '../../api/begin-route'; const ROUTE_SUMMARY_MARKER_ID = 999001; type StatusRule = { @@ -74,13 +69,6 @@ const WAITING_TIMER_ELAPSED_KEY = 'waitingTimerElapsed'; // 等待计时已过秒数 const DEFAULT_QUEUE_POLLING_TIME = 10; // 排队位次默认轮询秒数 const DEFAULT_REQUOTE_EXPIRE_SECONDS = 10 * 60; // 重新报价默认有效期秒数 const requestOrderRouteApi = (targetOrderId: string, status: number) => { // 掉线兜底专用路线查询,直接返回接口结果供统一落地层消费 const routeType = status <= TripStatus.VehicleArrivedPickup ? 1 : 2; const requestRoute = [TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(status as TripStatus) ? queryOrderTrajectory : queryOrderRoute; return requestRoute({ orderId: targetOrderId, routeType }); }; const { showCustomerService, @@ -305,26 +293,11 @@ currentFullRouteRef, lastErasedIndexRef, applyRoutePoints: routeApplyRoutePoints, resetRoutePolylineCache, erasePassedRouteSegments: routeErasePassedRouteSegments, requestRouteAndFare: routeRequestRouteAndFare, requestOrderRoute: routeRequestOrderRoute, updateWalkingRouteForStart: routeUpdateWalkingRouteForStart } = routeState; // 路线状态解构 const { applyRouteSnapshot: applySmoothRouteSnapshot, resetSmoothRouteMotion } = useSmoothRouteMotion({ tripStatus, tripStatusRef, applyRoutePoints: routeApplyRoutePoints, resetRoutePolylineCache, setRouteDistance, setRouteDuration, setVehicleDirection, setVehicleMoveAlongPoints, updateVehicleLocationThrottled: vehicleState.updateVehicleLocationThrottled }); // 平滑路线运动编排 const displayPolyline = useMemo(() => { if (!(isModifyDestinationReviewVisible || hasLocalDestinationOverride) || !Array.isArray(previousRoutePoints) || previousRoutePoints.length === 0) { return polyline; @@ -551,31 +524,33 @@ setOrderId, setTripStatus, setVehicleInfo, vehicleInfo, setPriceUpdateVersion, setFinishCountdownEnabled, setShowAnnouncementsPopup, setRoutePoints, setPolyline, polylineKeyRef, lastPolylineUpdateAtRef, lastRoutePointRef, lastRouteUpdateAtRef, lastVehiclePointRef, lastVehicleUpdateAtRef, currentFullRouteRef, lastErasedIndexRef, orderRouteInitializedRef, setCancelType, setShowCancelPopup, checkAndShowPassengerNotice: tripRuntime.checkAndShowPassengerNotice, setVehicleDirection, setVehicleMoveAlongPoints, setRouteDistance, setRouteDuration, setBeforeUserEndSite, applySmoothRouteSnapshot, resetSmoothRouteMotion }); useSocketFallbackPolling({ tripStatus, tripStatusRef, orderId, setTripStatus, setOrderId, setVehicleInfo, setStartSite, setEndSite, setTrunkStatus, routeRequestOrderRouteApi: requestOrderRouteApi, applySmoothRouteSnapshot erasePassedRouteSegments: routeErasePassedRouteSegments, applyRoutePoints: routeApplyRoutePoints, updateVehicleLocationThrottled: vehicleState.updateVehicleLocationThrottled, onOrderUpdateLineRendered: autoRouteViewport.handleOrderUpdateLineRendered }); const orderActions = useOrderActions({