| | |
| | | <View className='popup-content' animation={animationData}> |
| | | <Image className='popup-icon' src={'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/bluetooth-success-auth.png'} /> |
| | | <Text className='popup-title'>已授权蓝牙,无感自动开启车辆</Text> |
| | | <Text className='popup-desc'>授权并打开蓝牙连接,手机靠近车辆即可解锁并自动启车门</Text> |
| | | <Text className='popup-desc'>授权并打开蓝牙连接,手机靠近车辆即可解锁并自动开启车门</Text> |
| | | <View className='popup-confirm-btn' onClick={handleConfirm}> |
| | | 知道了 |
| | | </View> |
| | |
| | | /** 预估金额币种。 */ |
| | | export const CONFIRM_RIDE_FARE_CURRENCY = 'CNY'; |
| | | /** 靠边状态空字符串。 */ |
| | | export const CONFIRM_RIDE_EMPTY_CURB_STATUS = ''; |
| | | export const CONFIRM_RIDE_EMPTY_CURB_STATUS = false; |
| | | /** 偏移距离,单位米。 */ |
| | | export const CONFIRM_RIDE_DEVIATION_METERS = ''; |
| | | |
| | | /** 确认行程页站点埋点结构。 */ |
| | | export type ConfirmRideSitePayload = { |
| | |
| | | }; |
| | | |
| | | /** 等待接驾取消成功埋点结构。 */ |
| | | export type WaitingForPickupCancelReportPayload = WaitingForPickupReportPayload; |
| | | export type WaitingForPickupCancelReportPayload = WaitingForPickupReportPayload & { |
| | | /** 从下单成功到取消成功的等待时长,单位秒。 */ |
| | | wait_duration_sec: number; |
| | | /** 取消原因。 */ |
| | | cancel_reason: string; |
| | | }; |
| | | |
| | | /** 到达上车点后取消成功埋点结构。 */ |
| | | export type PickupArrivedCancelTripReportPayload = { |
| | |
| | | pickup: ConfirmRideSitePayload; |
| | | /** 下车点。 */ |
| | | dropoff: ConfirmRideSitePayload; |
| | | /** 车辆信息。 */ |
| | | vehicle_info: ConfirmRideVehicleInfoPayload; |
| | | /** 预估信息。 */ |
| | | estimates: ConfirmRideEstimatesPayload; |
| | | /** 停车信息。 */ |
| | | park_info: TripCompleteParkInfoPayload; |
| | | /** 取消原因。 */ |
| | | cancel_reason: string; |
| | | }; |
| | | |
| | | /** 行程完成停车信息结构。 */ |
| | | export type TripCompleteParkInfoPayload = { |
| | | /** 上车点是否靠边。 */ |
| | | pickup_close_to_curb: string; |
| | | pickup_close_to_curb: boolean; |
| | | /** 下车点是否靠边。 */ |
| | | dropoff_close_to_curb: string; |
| | | dropoff_close_to_curb: boolean; |
| | | /** 停车纬度。 */ |
| | | lat: number; |
| | | /** 停车经度。 */ |
| | | lng: number; |
| | | /** 上车点偏移距离,单位米。 */ |
| | | pickup_deviation_meters?: string; |
| | | /** 下车点偏移距离,单位米。 */ |
| | | dropoff_deviation_meters?: string; |
| | | }; |
| | | |
| | | /** 行程完成事实信息结构。 */ |
| | |
| | | vehicleInfo: any; |
| | | /** 车辆位置。 */ |
| | | vehicleLocation?: { latitude?: number | null; longitude?: number | null } | null; |
| | | }): WaitingForPickupCancelReportPayload => ( |
| | | createWaitingForPickupReportPayload(params) |
| | | ); |
| | | /** 等待时长,单位秒。 */ |
| | | waitDurationSeconds?: number | null; |
| | | /** 取消原因。 */ |
| | | cancelReason?: string; |
| | | }): WaitingForPickupCancelReportPayload => ({ |
| | | ...createWaitingForPickupReportPayload(params), |
| | | wait_duration_sec: normalizeConfirmRideNumber(params.waitDurationSeconds), |
| | | cancel_reason: String(params.cancelReason || '') |
| | | }); |
| | | |
| | | /** 生成到达上车点后取消成功埋点参数。 */ |
| | | export const createPickupArrivedCancelTripReportPayload = (params: { |
| | |
| | | startSite: any; |
| | | /** 下车点。 */ |
| | | endSite: any; |
| | | /** 车辆信息。 */ |
| | | vehicleInfo: any; |
| | | /** 车辆位置。 */ |
| | | vehicleLocation?: { latitude?: number | null; longitude?: number | null } | null; |
| | | /** 费用信息。 */ |
| | | fareEstimate: any; |
| | | /** 行程时长,单位秒。 */ |
| | | routeDurationSeconds?: number | null; |
| | | /** 行程距离,单位米。 */ |
| | | routeDistanceMeters?: number | null; |
| | | /** 取消原因。 */ |
| | | cancelReason?: string; |
| | | }): PickupArrivedCancelTripReportPayload => ({ |
| | | pickup: createConfirmRideSitePayload(params.startSite, 'start'), |
| | | dropoff: createConfirmRideSitePayload(params.endSite, 'end') |
| | | dropoff: createConfirmRideSitePayload(params.endSite, 'end'), |
| | | vehicle_info: createConfirmRideVehicleInfoPayload({ |
| | | vehicleInfo: params.vehicleInfo, |
| | | vehicleLocation: params.vehicleLocation |
| | | }), |
| | | estimates: createConfirmRideEstimatesPayload({ |
| | | fareEstimate: params.fareEstimate, |
| | | routeDurationSeconds: params.routeDurationSeconds, |
| | | routeDistanceMeters: params.routeDistanceMeters |
| | | }), |
| | | park_info: createTripCompleteParkInfoPayload({ |
| | | vehicleLocation: params.vehicleLocation, |
| | | endSite: params.endSite |
| | | }), |
| | | cancel_reason: String(params.cancelReason || '') |
| | | }); |
| | | |
| | | /** 生成行程完成停车信息。 */ |
| | |
| | | /** 下车点。 */ |
| | | endSite?: any; |
| | | }): TripCompleteParkInfoPayload => ({ |
| | | pickup_deviation_meters: CONFIRM_RIDE_DEVIATION_METERS, |
| | | dropoff_deviation_meters: CONFIRM_RIDE_DEVIATION_METERS, |
| | | pickup_close_to_curb: CONFIRM_RIDE_EMPTY_CURB_STATUS, |
| | | dropoff_close_to_curb: CONFIRM_RIDE_EMPTY_CURB_STATUS, |
| | | lat: normalizeConfirmRideNumber( |
| | |
| | | /** 等待接驾阶段取消成功埋点事件。 */ |
| | | export const WAITING_FOR_PICKUP_CANCEL_BUTTON_EVENT_ID = 'RT_xcx_Click_WaitingFoPickup_Cancel_Button'; |
| | | |
| | | /** 行程中页面浏览埋点事件。 */ |
| | | export const TRIP_IN_PROGRESS_PAGE_VIEW_EVENT_ID = 'RT_xcx_PageView_TripInProgress'; |
| | | |
| | | /** 车辆到达上车点页面浏览埋点事件。 */ |
| | | export const PICKUP_ARRIVED_PAGE_VIEW_EVENT_ID = 'RT_xcx_PageView_PickupArrived'; |
| | | |
| | | /** 到达上车点后取消成功埋点事件。 */ |
| | | export const PICKUP_ARRIVED_CANCEL_TRIP_BUTTON_EVENT_ID = 'RT_xcx_Click_PickupArrived_CancelTrip_Button'; |
| | | |
| | |
| | | routeDurationSeconds: deps.routeDuration, |
| | | routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters, |
| | | vehicleInfo: deps.vehicleInfo, |
| | | vehicleLocation: deps.vehicleLocation |
| | | vehicleLocation: deps.vehicleLocation, |
| | | waitDurationSeconds: getWaitingDurationSeconds(), |
| | | cancelReason: EMPTY_CANCEL_REASON |
| | | })); |
| | | }; |
| | | |
| | |
| | | const reportPickupArrivedCancelTripEvent = () => { |
| | | reportEvent(PICKUP_ARRIVED_CANCEL_TRIP_BUTTON_EVENT_ID, createPickupArrivedCancelTripReportPayload({ |
| | | startSite: deps.startSite, |
| | | endSite: deps.endSite |
| | | endSite: deps.endSite, |
| | | fareEstimate: deps.fareEstimate, |
| | | routeDurationSeconds: deps.routeDuration, |
| | | routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters, |
| | | vehicleInfo: deps.vehicleInfo, |
| | | vehicleLocation: deps.vehicleLocation, |
| | | cancelReason: EMPTY_CANCEL_REASON |
| | | })); |
| | | }; |
| | | |
| | |
| | | |
| | | export type UseWebSocketEventsDeps = { |
| | | tripStatusRef: React.MutableRefObject<TripStatus>; |
| | | pickupArrivedReportedOrderIdRef: React.MutableRefObject<string>; |
| | | tripInProgressReportedOrderIdRef: React.MutableRefObject<string>; |
| | | arrivedDestinationReportedOrderIdRef: React.MutableRefObject<string>; |
| | | pendingCancelContextRef: React.MutableRefObject<PendingCancelContext | null>; |
| | | modifyDestinationPickupSuccessAtRef: React.MutableRefObject<number>; |
| | |
| | | import { clearOrderTripStorage, reportEvent } from '../../../../../utils/Robotaxi-public-method'; |
| | | import { getOrderId } from '../../../../utils/order'; |
| | | import { TripStatus, toTripStatus } from '../../constants/trip-status'; |
| | | import { ARRIVED_DROPOFF_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_COMPLETE_TRIP_BUTTON_EVENT_ID } from '../../constants/report-events'; |
| | | import { ARRIVED_DROPOFF_PAGE_VIEW_EVENT_ID, PICKUP_ARRIVED_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_COMPLETE_TRIP_BUTTON_EVENT_ID } from '../../constants/report-events'; |
| | | import { createTripInProgressCompleteTripReportPayload } from '../../constants/confirm-ride-report'; |
| | | import { clearPassedRouteCache } from '../passed-route-cache'; |
| | | import { PENDING_CANCEL_CONTEXT_TTL_MS, type PendingCancelContext } from '../use-order-actions.shared'; |
| | |
| | | }; |
| | | |
| | | /** |
| | | * 行程中(状态为 6)上报页面浏览埋点。 |
| | | */ |
| | | const reportTripInProgressEventIfNeeded = ( |
| | | mappedTripStatus: TripStatus | null, |
| | | changeStatusOld: TripStatus, |
| | | effectiveOrderId: string, |
| | | deps: UseWebSocketEventsDeps |
| | | ) => { |
| | | const shouldReportTripInProgress = ( |
| | | mappedTripStatus === TripStatus.InService |
| | | && changeStatusOld !== TripStatus.InService |
| | | && Boolean(effectiveOrderId) |
| | | ); |
| | | if (!shouldReportTripInProgress) { |
| | | return; |
| | | } |
| | | if (deps.tripInProgressReportedOrderIdRef.current === effectiveOrderId) { |
| | | return; |
| | | } |
| | | deps.tripInProgressReportedOrderIdRef.current = effectiveOrderId; |
| | | getOrderDetail(effectiveOrderId) |
| | | .then((orderDetail: any) => { |
| | | const payload = createTripInProgressCompleteTripReportPayload({ |
| | | startSite: deps.startSite, |
| | | endSite: deps.endSite, |
| | | vehicleInfo: deps.vehicleInfo, |
| | | vehicleLocation: deps.vehicleLocation, |
| | | fareEstimate: deps.fareEstimate, |
| | | routeDurationSeconds: deps.routeDuration, |
| | | routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters, |
| | | orderDetail |
| | | }); |
| | | reportEvent(TRIP_IN_PROGRESS_PAGE_VIEW_EVENT_ID, payload, 'pageView'); |
| | | }) |
| | | .catch(() => { |
| | | deps.tripInProgressReportedOrderIdRef.current = ''; |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 车辆到达上车点后上报页面浏览埋点。 |
| | | */ |
| | | const reportPickupArrivedEventIfNeeded = ( |
| | | mappedTripStatus: TripStatus | null, |
| | | changeStatusOld: TripStatus, |
| | | effectiveOrderId: string, |
| | | deps: UseWebSocketEventsDeps |
| | | ) => { |
| | | const shouldReportPickupArrived = ( |
| | | mappedTripStatus === TripStatus.VehicleArrivedPickup |
| | | && changeStatusOld !== TripStatus.VehicleArrivedPickup |
| | | && Boolean(effectiveOrderId) |
| | | ); |
| | | if (!shouldReportPickupArrived) { |
| | | return; |
| | | } |
| | | if (deps.pickupArrivedReportedOrderIdRef.current === effectiveOrderId) { |
| | | return; |
| | | } |
| | | deps.pickupArrivedReportedOrderIdRef.current = effectiveOrderId; |
| | | getOrderDetail(effectiveOrderId) |
| | | .then((orderDetail: any) => { |
| | | const payload = createTripInProgressCompleteTripReportPayload({ |
| | | startSite: deps.startSite, |
| | | endSite: deps.endSite, |
| | | vehicleInfo: deps.vehicleInfo, |
| | | vehicleLocation: deps.vehicleLocation, |
| | | fareEstimate: deps.fareEstimate, |
| | | routeDurationSeconds: deps.routeDuration, |
| | | routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters, |
| | | orderDetail |
| | | }); |
| | | reportEvent(PICKUP_ARRIVED_PAGE_VIEW_EVENT_ID, payload, 'pageView'); |
| | | }) |
| | | .catch(() => { |
| | | deps.pickupArrivedReportedOrderIdRef.current = ''; |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 到达目的地后上报完成行程埋点。 |
| | | */ |
| | | const reportTripCompleteEventIfNeeded = ( |
| | |
| | | const mappedTripStatus = syncTripStatus(payload, currentDeps); |
| | | const effectiveOrderId = resolveEffectiveOrderId(payload, currentOrderId); |
| | | syncVehicleInfo(effectiveOrderId, currentDeps, lastFetchedVehicleOrderIdRef); |
| | | reportPickupArrivedEventIfNeeded(mappedTripStatus, changeStatusOld, effectiveOrderId, currentDeps); |
| | | reportTripInProgressEventIfNeeded(mappedTripStatus, changeStatusOld, effectiveOrderId, currentDeps); |
| | | reportTripCompleteEventIfNeeded(mappedTripStatus, changeStatusOld, effectiveOrderId, currentDeps); |
| | | handleStatusSideEffects(mappedTripStatus, changeStatusOld, currentDeps); |
| | | handleCancelPopupPush(payload, changeStatusOld, currentDeps, pendingCancelContext); |
| | |
| | | const lastSocketDirectionAtRef = useRef(0); // 最近一次由 socket 写入车辆朝向的时间戳 |
| | | const beforeBackRef = useRef<() => void>(() => { }); // 左上角返回前的兜底清理,避免预览路线残留到下次进页 |
| | | const pendingCancelContextRef = useRef<PendingCancelContext | null>(null); // 待确认取消上下文,避免本地状态重置污染后续 socket 取消弹窗类型判断 |
| | | const pickupArrivedReportedOrderIdRef = useRef(''); // 到达上车点页面浏览埋点去重 |
| | | const tripInProgressReportedOrderIdRef = useRef(''); // 行程中页面浏览埋点去重 |
| | | const arrivedDestinationReportedOrderIdRef = useRef(''); // 到达目的地完成行程埋点去重,避免同一订单重复 socket 上报 |
| | | const lastPreviewPageViewDidShowVersionRef = useRef(0); // 确认行程预览态页面浏览埋点最近一次上报的页面展示版本 |
| | | const lastWaitingForDispatchPageViewOrderIdRef = useRef(''); // 等待派单页页面浏览埋点最近一次上报的订单 ID |
| | |
| | | |
| | | const { bindSocketListeners, ensureSocketListeners } = useWebSocketEvents({ |
| | | tripStatusRef, |
| | | pickupArrivedReportedOrderIdRef, |
| | | tripInProgressReportedOrderIdRef, |
| | | arrivedDestinationReportedOrderIdRef, |
| | | pendingCancelContextRef, |
| | | startSite, |
| | |
| | | return; |
| | | } |
| | | reportEvent(eventId, { |
| | | has_nearby_stations_shown: state.nearbySites.length > 0 ? 1 : 0, |
| | | has_nearby_stations_shown: state.nearbySites.length > 0 ? true : false, |
| | | nearby_station_count: Number(state.nearbySites.length || 0), |
| | | confirmed_pickup: { |
| | | station: createConfirmedPickupStationPayload(), |
| | | type: state.pickupConfirmTypeRef.current |
| | | type: state.pickupConfirmTypeRef.current, |
| | | lat: Number(state.selectedSite?.latitude || state.selectedSite?.lat || 0), |
| | | lng: Number(state.selectedSite?.longitude || state.selectedSite?.lng || 0) |
| | | }, |
| | | map_zoom_level: Number(state.mapScale || 0), |
| | | pin_loc: { |
| | |
| | | const selectedCity = Taro.getStorageSync('selectedCity'); |
| | | reportEvent(eventId, { |
| | | search_selected_station: { |
| | | query_key: String(searchValue || '').trim(), |
| | | query_keyword: String(searchValue || '').trim(), |
| | | list_idx: Number(listIndex), |
| | | total_results: Number(totalResults), |
| | | station: createReportStationPayload(item, String(selectedCity?.name || '')) |
| | |
| | | GEOFENCE_TYPE_OPERATING, |
| | | GEOFENCE_TYPE_STATION |
| | | } from '../robotaxi/constants/geofence'; |
| | | import { APPID } from '../constants/app' |
| | | |
| | | /** |
| | | * 安全的 JSON 解析函数 |
| | |
| | | if (typeof str !== 'string') { |
| | | return str || defaultVal; |
| | | } |
| | | |
| | | |
| | | const trimmed = str.trim(); |
| | | // 简单的格式预判:JSON 对象或数组字符串通常以 { 或 [ 开头 |
| | | // 如果不是这两种开头,大概率不是我们需要解析的 JSON,直接返回原值或默认值,避免引发 JSON.parse 的异常和错误日志 |
| | |
| | | type, // 埋点操作类型 |
| | | user_id: uid || '-1', // 用户ID |
| | | masked_device_id: openId, // 设备ID |
| | | device_id: openId, // 设备ID |
| | | event_time: Date.now(), // 事件时间 |
| | | city: Taro.getStorageSync('selectedCity').name || '', // 城市 |
| | | orderId: Taro.getStorageSync('orderId') || '', // 订单ID |
| | | os: Taro.getSystemInfoSync().system || '', // 操作系统 |
| | | app_id: APPID, // 微信小程序appid |
| | | app_ver: appVersion, // 微信小程序版本 |
| | | session_id: '', // 会话ID |
| | | }, |