| | |
| | | import { TripStatus } from "../../../constants/trip-status"; |
| | | import { formatRemainingDurationDisplay } from "../../../utils/format"; |
| | | |
| | | /** 将秒数格式化为 MM:SS(不足两位补零)。 */ |
| | | const formatCountdownDisplay = (seconds: unknown) => { |
| | | const total = Math.max(0, Math.floor(Number(seconds) || 0)); |
| | | const minutes = Math.floor(total / 60); |
| | | const rest = total % 60; |
| | | return `${String(minutes).padStart(2, "0")}:${String(rest).padStart(2, "0")}`; |
| | | }; |
| | | |
| | | type Deps = { |
| | | /** 是否安卓 */ |
| | | isAndroid: boolean; |
| | |
| | | arriveCalloutVisible: boolean; |
| | | /** 到达气泡样式 */ |
| | | arriveCalloutStyle: any; |
| | | /** 路线摘要气泡可见 */ |
| | | routeSummaryCalloutVisible: boolean; |
| | | /** 路线摘要气泡样式 */ |
| | | routeSummaryCalloutStyle: any; |
| | | /** 到达态气泡渲染就绪(角度与缩放均就绪) */ |
| | | arrivedTooltipReady: boolean; |
| | | /** 路线距离 */ |
| | |
| | | hasPreviousOrder: boolean; |
| | | /** 自定义气泡图标 */ |
| | | customCalloutIconSrc: string; |
| | | /** 路线概览气泡背景图 */ |
| | | routeSummaryBgSrc: string; |
| | | /** 是否使用下单前端点气泡样式 */ |
| | | showTrip1StyleEndpoint: boolean; |
| | | /** 上车点 */ |
| | | startSite: any; |
| | | /** 呼单等待计时秒数(按秒递增,接单后重置为 0) */ |
| | | elapsedSeconds: number; |
| | | /** 下车点 */ |
| | | endSite: any; |
| | | /** 发单 */ |
| | |
| | | hasBeforeUserEndSite: boolean; |
| | | routeDistance: { value: number; unit: "km" | "m" } | null; |
| | | durationDisplay: DurationDisplay; |
| | | /** 是否接驾态(前往上车点):金色药丸样式,仅展示里程(KM/M),不展示分钟 */ |
| | | isPickup: boolean; |
| | | }; |
| | | |
| | | /** |
| | |
| | | */ |
| | | const AndroidCarCalloutContent = memo( |
| | | function AndroidCarCalloutContent(props: AndroidCarCalloutContentProps) { |
| | | const { hasBeforeUserEndSite, routeDistance, durationDisplay } = props; |
| | | const { hasBeforeUserEndSite, routeDistance, durationDisplay, isPickup } = props; |
| | | return ( |
| | | <View |
| | | className={`custom-callout-content ${hasBeforeUserEndSite |
| | | className={`custom-callout-content ${isPickup ? "car-metric-callout " : ""}${hasBeforeUserEndSite |
| | | ? "car-callout-with-before car-callout-double-line" |
| | | : "" |
| | | }`.trim()} |
| | | style={{ padding: "6px 10px" }} |
| | | style={isPickup ? undefined : { padding: "6px 10px" }} |
| | | > |
| | | {hasBeforeUserEndSite && ( |
| | | <View className="car-time car-before-order-tip">即将完成上一单</View> |
| | | )} |
| | | <View |
| | | className="car-time" |
| | | style={{ alignItems: "baseline", paddingBottom: 0 }} |
| | | > |
| | | <View className="car-time-num"> |
| | | {routeDistance?.value ?? 0} |
| | | {hasBeforeUserEndSite ? ( |
| | | <View className="car-before-order-tip-text"> |
| | | <View>全程</View> |
| | | <View className="car-before-order-tip-value"> |
| | | {routeDistance?.value ?? 0} |
| | | </View> |
| | | <View className="car-before-order-tip-unit"> |
| | | {routeDistance?.unit === "m" ? "米" : "公里"} |
| | | </View> |
| | | <View className="car-before-order-tip-value"> |
| | | {durationDisplay.value} |
| | | </View> |
| | | <View className="car-before-order-tip-unit"> |
| | | {durationDisplay.unit} |
| | | </View> |
| | | </View> |
| | | <View style={{ flexShrink: 0 }}> |
| | | {routeDistance?.unit === "m" ? "米" : "公里"} |
| | | </View> |
| | | ) : ( |
| | | <View |
| | | style={{ marginLeft: "2px" }} |
| | | className="car-time-num" |
| | | className="car-time" |
| | | style={{ alignItems: "baseline", paddingBottom: 0 }} |
| | | > |
| | | {durationDisplay.value} |
| | | <View className="car-time-num"> |
| | | {routeDistance?.value ?? 0} |
| | | </View> |
| | | <View style={{ flexShrink: 0 }}> |
| | | {isPickup |
| | | ? routeDistance?.unit === "m" ? "M" : "KM" |
| | | : routeDistance?.unit === "m" ? "米" : "公里"} |
| | | </View> |
| | | {!isPickup && ( |
| | | <> |
| | | <View |
| | | style={{ marginLeft: "2px" }} |
| | | className="car-time-num" |
| | | > |
| | | {durationDisplay.value} |
| | | </View> |
| | | <View style={{ flexShrink: 0 }}>{durationDisplay.unit}</View> |
| | | </> |
| | | )} |
| | | </View> |
| | | <View style={{ flexShrink: 0 }}>{durationDisplay.unit}</View> |
| | | </View> |
| | | )} |
| | | </View> |
| | | ); |
| | | }, |
| | |
| | | prevProps.routeDistance?.value === nextProps.routeDistance?.value && |
| | | prevProps.routeDistance?.unit === nextProps.routeDistance?.unit && |
| | | prevProps.durationDisplay.value === nextProps.durationDisplay.value && |
| | | prevProps.durationDisplay.unit === nextProps.durationDisplay.unit |
| | | prevProps.durationDisplay.unit === nextProps.durationDisplay.unit && |
| | | prevProps.isPickup === nextProps.isPickup |
| | | ); |
| | | |
| | | /** |
| | |
| | | /** 修改目的地预览态和接驾态切路线期间隐藏车辆公里/分钟气泡。 */ |
| | | const shouldShowVehicleMetricCallout = |
| | | !deps.isModifyDestinationReviewVisible && !deps.isPickupRouteChangeVisible; |
| | | const routeSummaryText = useMemo( |
| | | () => |
| | | deps.routeDistance && deps.routeDuration !== null |
| | | ? `${vehicleMetricDurationDisplay.value}${vehicleMetricDurationDisplay.unit |
| | | } ${deps.routeDistance.value}${deps.routeDistance.unit === "m" ? "米" : "公里" |
| | | }` |
| | | : "", |
| | | [deps.routeDistance, deps.routeDuration, vehicleMetricDurationDisplay] |
| | | ); |
| | | const shouldHideAndroidMarkerCallout = |
| | | deps.isAndroid && |
| | | ([ |
| | |
| | | hasBeforeUserEndSite={deps.hasBeforeUserEndSite} |
| | | routeDistance={deps.routeDistance} |
| | | durationDisplay={vehicleMetricDurationDisplay} |
| | | isPickup={[TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus)} |
| | | /> |
| | | ), |
| | | [ |
| | | deps.hasBeforeUserEndSite, |
| | | deps.routeDistance, |
| | | vehicleMetricDurationDisplay |
| | | vehicleMetricDurationDisplay, |
| | | deps.tripStatus |
| | | ] |
| | | ); |
| | | |
| | |
| | | slot="callout" |
| | | marker-id={item.id} |
| | | key={calloutKey} |
| | | className="waiting-callout" |
| | | className={`waiting-callout ${deps.tripStatus === TripStatus.Publishing ? 'waiting-callout-publishing' : ''}`} |
| | | > |
| | | <CoverView className="waiting-callout-content"> |
| | | {deps.tripStatus === TripStatus.CallingCar && ( |
| | | <CoverView className="waiting-callout-bg waiting-timedown-bg"> |
| | | <CoverImage |
| | | className="waiting-callout-bg-img" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/tooltip-bg-mini-20260715.png" |
| | | /> |
| | | <CoverView className="waiting-callout-bg-text"> |
| | | {formatCountdownDisplay(deps.elapsedSeconds)} |
| | | </CoverView> |
| | | </CoverView> |
| | | )} |
| | | <CoverView |
| | | className={`waiting-callout-txt ${!deps.isAndroid && "pr-6"}`} |
| | | className={`waiting-callout-txt ${!deps.isAndroid && "pr-6"}${ |
| | | deps.tripStatus === TripStatus.CallingCar |
| | | ? " waiting-callout-txt--overlap" |
| | | : "" |
| | | }`} |
| | | > |
| | | {item.name} |
| | | {!deps.isAndroid && ( |
| | | <CoverView style={{ color: "transparent" }}>占</CoverView> |
| | | )} |
| | | </CoverView> |
| | | <CoverImage |
| | | className="callout-diamond-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/diamond-icon-20260708.png" |
| | | /> |
| | | </CoverView> |
| | | <CoverImage className="waiting-radar" src={deps.waitingRadarSrc} /> |
| | | </CoverView> |
| | | ), |
| | | [deps.isAndroid, deps.waitingRadarSrc] |
| | | [deps.isAndroid, deps.waitingRadarSrc, deps.tripStatus, deps.elapsedSeconds] |
| | | ); |
| | | |
| | | /** |
| | |
| | | slot="callout" |
| | | marker-id={item.id} |
| | | key={calloutKey} |
| | | className="custom-callout" |
| | | className={` |
| | | custom-callout |
| | | ${ |
| | | [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus) |
| | | ? 'site-status-callout-box' |
| | | : '' |
| | | } |
| | | ${deps.tripStatus === TripStatus.VehicleArrivedPickup ? 'arrived-pickup-callout-box' : ''}` |
| | | } |
| | | style={item.id === 1 ? calloutRepaintStyle : undefined} |
| | | > |
| | | {item.isRouteSummaryBubble ? ( |
| | | routeSummaryText ? ( |
| | | // 路由摘要气泡 |
| | | <CoverView className="route-summary-callout"> |
| | | <CoverImage |
| | | className="route-summary-callout-bg" |
| | | src={deps.routeSummaryBgSrc} |
| | | /> |
| | | <CoverView className="route-summary-callout-text"> |
| | | {routeSummaryText} |
| | | {/* 占位 */} |
| | | <CoverView style={{ color: "transparent" }}>占</CoverView> |
| | | </CoverView> |
| | | </CoverView> |
| | | ) : null |
| | | ) : item.isBeforeUserSite ? ( |
| | | {item.isBeforeUserSite ? ( |
| | | <CoverView className="before-user-site-callout"> |
| | | <CoverView className="before-user-site-txt">上一站终点</CoverView> |
| | | </CoverView> |
| | |
| | | item.id === 2 && |
| | | [TripStatus.ArrivedDestination].includes(deps.tripStatus) |
| | | ) && |
| | | !( |
| | | item.id === 2 && |
| | | (deps.tripStatus === TripStatus.PublishRoute || deps.isPickupRouteChangeVisible) && |
| | | !!deps.routeDistance && |
| | | deps.routeDuration !== null |
| | | ) && |
| | | !( |
| | | item.id === 1 && |
| | | deps.tripStatus === TripStatus.PublishRoute && |
| | | !!deps.routeDistance && |
| | | deps.routeDuration !== null |
| | | ) && |
| | | !( |
| | | item.id === 1 && |
| | | [ |
| | | TripStatus.VehicleApproaching, |
| | | TripStatus.VehicleArrivedPickup, |
| | | TripStatus.TripFinished, |
| | | TripStatus.PaymentSuccess |
| | | ].includes(deps.tripStatus) |
| | | ) && |
| | | !( |
| | | item.id === 2 && |
| | | [ |
| | | TripStatus.InService, |
| | | TripStatus.TripFinished, |
| | | TripStatus.PaymentSuccess |
| | | ].includes(deps.tripStatus) |
| | | ) && |
| | | !( |
| | | (item.id === 1 || item.id === 2) && |
| | | deps.isModifyDestinationReviewVisible |
| | | ) && |
| | | !item.hideNameCallout && ( |
| | | <> |
| | | <CoverView |
| | | className={`custom-callout-content ${deps.isAndroid && |
| | | "txt-center "}`} |
| | |
| | | /> |
| | | )} |
| | | </CoverView> |
| | | <CoverImage |
| | | className="callout-diamond-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/diamond-icon-20260708.png" |
| | | /> |
| | | </> |
| | | )} |
| | | {item.id === 1 && |
| | | deps.tripStatus === TripStatus.PublishRoute && |
| | | !!deps.routeDistance && |
| | | deps.routeDuration !== null && |
| | | !item.hideNameCallout && ( |
| | | <> |
| | | {Number(deps.startSite?.time) > 0 && ( |
| | | <CoverView className="callout-parking-tip"> |
| | | <CoverImage |
| | | className="callout-parking-tip-bg" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/tooltip-bg-20260715.png" |
| | | /> |
| | | <CoverImage |
| | | className="callout-parking-tip-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/map-time-icon-20260708.png" |
| | | /> |
| | | <CoverView className="callout-parking-tip-text"> |
| | | 限停{deps.startSite.time}分钟 |
| | | {!deps.isAndroid && ( |
| | | <CoverView |
| | | style={{ color: "transparent", width: "4px" }} |
| | | > |
| | | 占 |
| | | </CoverView> |
| | | )} |
| | | </CoverView> |
| | | </CoverView> |
| | | )} |
| | | <CoverView |
| | | className={`custom-callout-content endpoint-summary-callout${ |
| | | Number(deps.startSite?.time) > 0 |
| | | ? " endpoint-summary-callout--overlap" |
| | | : "" |
| | | }`} |
| | | style={{ zIndex: 1 }} |
| | | > |
| | | <CoverView className="endpoint-summary-main"> |
| | | <CoverView className="endpoint-summary-name"> |
| | | {item.name} |
| | | {!deps.isAndroid && |
| | | /[^\u4e00-\u9fa5]/.test(item.name) && ( |
| | | <CoverView |
| | | style={{ color: "transparent", width: "3px" }} |
| | | > |
| | | 占 |
| | | </CoverView> |
| | | )} |
| | | </CoverView> |
| | | </CoverView> |
| | | <CoverImage |
| | | className="endpoint-summary-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/map-white-arrow-20260708.png" |
| | | /> |
| | | </CoverView> |
| | | <CoverImage |
| | | className="callout-diamond-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/diamond-icon-20260708.png" |
| | | /> |
| | | </> |
| | | )} |
| | | {item.id === 2 && |
| | | (deps.tripStatus === TripStatus.PublishRoute |
| | | || deps.isModifyDestinationReviewVisible |
| | | || deps.isPickupRouteChangeVisible |
| | | ) && |
| | | !!deps.routeDistance && |
| | | deps.routeDuration !== null && |
| | | !item.hideNameCallout && ( |
| | | <> |
| | | <CoverView |
| | | className="custom-callout-content endpoint-summary-callout" |
| | | style={{ zIndex: 1 }} |
| | | > |
| | | <CoverView className="endpoint-summary-main"> |
| | | <CoverView className="endpoint-summary-name"> |
| | | {item.name} |
| | | {!deps.isAndroid && |
| | | /[^\u4e00-\u9fa5]/.test(item.name) && ( |
| | | <CoverView |
| | | style={{ color: "transparent", width: "3px" }} |
| | | > |
| | | 占 |
| | | </CoverView> |
| | | )} |
| | | </CoverView> |
| | | <CoverView className="endpoint-summary-metric"> |
| | | <CoverView className="endpoint-summary-label"> |
| | | 全程 |
| | | </CoverView> |
| | | <CoverView className="endpoint-summary-golden-value"> |
| | | {deps.routeDistance.value} |
| | | </CoverView> |
| | | <CoverView className="endpoint-summary-value-unit"> |
| | | {deps.routeDistance.unit === "m" ? "米" : "公里"} |
| | | </CoverView> |
| | | <CoverView className="endpoint-summary-golden-value"> |
| | | {vehicleMetricDurationDisplay.value} |
| | | </CoverView> |
| | | <CoverView className="endpoint-summary-value-unit"> |
| | | {vehicleMetricDurationDisplay.unit} |
| | | </CoverView> |
| | | </CoverView> |
| | | </CoverView> |
| | | {!deps.isModifyDestinationReviewVisible && ( |
| | | <CoverImage |
| | | className="endpoint-summary-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/map-white-arrow-20260708.png" |
| | | /> |
| | | )} |
| | | </CoverView> |
| | | <CoverImage |
| | | className="callout-diamond-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/diamond-icon-20260708.png" |
| | | /> |
| | | </> |
| | | )} |
| | | {item.id === 1 && |
| | | deps.tripStatus === TripStatus.VehicleApproaching && |
| | | !deps.isModifyDestinationReviewVisible && |
| | | !item.hideNameCallout && ( |
| | | <> |
| | | <CoverView |
| | | className="custom-callout-content site-status-callout" |
| | | style={{ zIndex: 1 }} |
| | | > |
| | | <CoverImage |
| | | className="site-status-car-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/golden-car-icon-20260708.png" |
| | | /> |
| | | <CoverView className="site-status-time"> |
| | | 限停{deps.startSite?.time}MIN |
| | | </CoverView> |
| | | </CoverView> |
| | | <CoverImage |
| | | className="callout-diamond-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/diamond-icon-20260708.png" |
| | | /> |
| | | </> |
| | | )} |
| | | {item.id === 1 && |
| | | deps.tripStatus === TripStatus.VehicleArrivedPickup && |
| | | !deps.isModifyDestinationReviewVisible && |
| | | !item.hideNameCallout && |
| | | Number(pickupCountdownSeconds) > 0 && ( |
| | | <CoverView |
| | | className="custom-callout-content site-countdown-callout" |
| | | style={{ zIndex: 1 }} |
| | | > |
| | | <CoverImage |
| | | className="site-countdown-icon" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/golden-time-icon-20260709.png" |
| | | /> |
| | | <CoverView className="site-countdown-time"> |
| | | {formatCountdownDisplay(pickupCountdownSeconds)} |
| | | </CoverView> |
| | | </CoverView> |
| | | )} |
| | | {item.id === 1 && |
| | | ([TripStatus.TripFinished, TripStatus.PaymentSuccess].includes( |
| | | deps.tripStatus |
| | | ) || deps.isModifyDestinationReviewVisible) && |
| | | !item.hideNameCallout && ( |
| | | <CoverImage |
| | | className="site-status-end-img" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/start-site-20260722.png" |
| | | /> |
| | | )} |
| | | {item.id === 2 && |
| | | [ |
| | | TripStatus.InService, |
| | | TripStatus.TripFinished, |
| | | TripStatus.PaymentSuccess |
| | | ].includes(deps.tripStatus) && |
| | | !deps.isModifyDestinationReviewVisible && |
| | | !item.hideNameCallout && ( |
| | | <CoverImage |
| | | className="site-status-end-img" |
| | | src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-20260722.png" |
| | | /> |
| | | )} |
| | | {item.name === "车辆位置" && |
| | | [TripStatus.VehicleApproaching, TripStatus.InService].includes( |
| | |
| | | !deps.isAndroid && |
| | | shouldShowVehicleMetricCallout && ( |
| | | <CoverView |
| | | className={`custom-callout-content ${deps.hasBeforeUserEndSite |
| | | className={`custom-callout-content car-metric-callout ${deps.hasBeforeUserEndSite |
| | | ? "car-callout-with-before car-callout-double-line" |
| | | : "" |
| | | }`.trim()} |
| | | style={{ |
| | | padding: "6px 10px", |
| | | minWidth: "40px", |
| | | justifyContent: "center", |
| | | zIndex: 2 |
| | | }} |
| | | style={{ minWidth: "40px", justifyContent: "center", zIndex: 2 }} |
| | | > |
| | | {deps.hasBeforeUserEndSite && ( |
| | | {!deps.hasBeforeUserEndSite && ( |
| | | <CoverView className="car-time car-before-order-tip"> |
| | | 即将完成上一单 |
| | | </CoverView> |
| | | )} |
| | | <CoverView className="car-time"> |
| | | <CoverView className="car-time-num"> |
| | | {deps.routeDistance?.value ?? 0} |
| | | {!deps.hasBeforeUserEndSite ? ( |
| | | <CoverView className="car-before-order-tip-text"> |
| | | <CoverView> |
| | | 全程 |
| | | </CoverView> |
| | | <CoverView className="car-before-order-tip-value"> |
| | | {deps.routeDistance?.value ?? 0} |
| | | </CoverView> |
| | | <CoverView className="car-before-order-tip-unit"> |
| | | {deps.routeDistance?.unit === "m" ? "M" : "KM"} |
| | | </CoverView> |
| | | <CoverView className="car-before-order-tip-value"> |
| | | {vehicleMetricDurationDisplay.value} |
| | | </CoverView> |
| | | <CoverView className="car-before-order-tip-unit"> |
| | | {vehicleMetricDurationDisplay.unit} |
| | | </CoverView> |
| | | </CoverView> |
| | | <CoverView> |
| | | {deps.routeDistance?.unit === "m" ? "米" : "公里"} |
| | | ) : ( |
| | | <CoverView className="car-time"> |
| | | <CoverView className="car-time-num"> |
| | | {deps.routeDistance?.value ?? 0} |
| | | </CoverView> |
| | | <CoverView> |
| | | {deps.routeDistance?.unit === "m" ? "M" : "KM"} |
| | | </CoverView> |
| | | </CoverView> |
| | | <CoverView |
| | | style={{ marginLeft: "2px" }} |
| | | className="car-time-num" |
| | | > |
| | | {vehicleMetricDurationDisplay.value} |
| | | </CoverView> |
| | | <CoverView>{vehicleMetricDurationDisplay.unit}</CoverView> |
| | | </CoverView> |
| | | ) |
| | | } |
| | | </CoverView> |
| | | )} |
| | | {item.id === 2 && |
| | |
| | | deps.hasBeforeUserEndSite, |
| | | deps.isAndroid, |
| | | deps.isModifyDestinationReviewVisible, |
| | | deps.isPickupRouteChangeVisible, |
| | | deps.routeDistance, |
| | | deps.routeSummaryBgSrc, |
| | | deps.routeDuration, |
| | | deps.showTrip1StyleEndpoint, |
| | | deps.tripStatus, |
| | | routeSummaryText, |
| | | deps.startSite?.time, |
| | | pickupCountdownSeconds, |
| | | vehicleMetricDurationDisplay, |
| | | shouldShowVehicleMetricCallout, |
| | | calloutRepaintStyle |
| | |
| | | * 渲染单个 marker 气泡。 |
| | | */ |
| | | const renderMarkerCallout = useCallback((item: any, index: number) => { |
| | | const calloutKey = `callout-${item.id}-${item.name || ''}`; |
| | | // 路线摘要气泡需跨平台强制显示,避免被 Android 普通气泡隐藏规则误伤 |
| | | if (item.isRouteSummaryBubble) { |
| | | return deps.isAndroid ? null : renderDefaultCallout(item, calloutKey); |
| | | } |
| | | if (shouldHideAndroidMarkerCallout && !item.isBeforeUserSite) return null; |
| | | // iOS 上 CoverView 内多个文本节点(里程/时长数字)原地更新会偶发数字错乱, |
| | | // 故将会变化的里程/时长/限停时长纳入 key,数值变化时强制整体重新挂载而非原地 patch。 |
| | | const calloutKey = `callout-${item.id}-${item.name || ''}-${deps.routeDistance?.value ?? ''}-${deps.routeDistance?.unit ?? ''}-${deps.routeDuration ?? ''}-${deps.startSite?.time ?? ''}`; |
| | | // 待支付/完成态的上/下车点浮窗为富内容(end-site 图片),需走 cover-view,不能被 Android 常规隐藏规则误伤 |
| | | const isSettledSiteMarker = |
| | | (item.id === 1 || item.id === 2) && |
| | | [TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(deps.tripStatus); |
| | | // 到达上车点态的上车点倒计时浮窗(cover-view),同样需放开 |
| | | const isArrivedPickupMarker = |
| | | item.id === 1 && deps.tripStatus === TripStatus.VehicleArrivedPickup; |
| | | if (shouldHideAndroidMarkerCallout && !item.isBeforeUserSite && !isSettledSiteMarker && !isArrivedPickupMarker) return null; |
| | | const isWaiting = [TripStatus.Publishing, TripStatus.CallingCar].includes(deps.tripStatus) && index === 0; |
| | | if (isWaiting) return renderWaitingCallout(item, calloutKey); |
| | | // 车辆里程气泡:两端都不再用 marker customCallout 渲染。 |
| | | // Android 用 <View> 浮层;iOS 用「自由定位的 CoverView」浮层(见 renderMapOverlays), |
| | | // 均规避被 translateMarker 丢弃(customCallout 在 iOS translate 下接驾/送驾都会偶发丢失)。 |
| | | if (item.name === '车辆位置' && [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus)) return null; |
| | | // iOS 接驾态上车点已改用原生 callout(marker.callout),不再渲染 cover-view customCallout, |
| | | // 既避免与原生 callout 重复,也彻底规避被车辆 translateMarker 丢弃。 |
| | | if (!deps.isAndroid && item.id === 1 && deps.tripStatus === TripStatus.VehicleApproaching) return null; |
| | | return renderDefaultCallout(item, calloutKey); |
| | | }, [deps.isAndroid, deps.tripStatus, renderDefaultCallout, renderWaitingCallout, shouldHideAndroidMarkerCallout]); |
| | | |
| | |
| | | webp={true} |
| | | style={deps.waitingRadarStyle} |
| | | /> |
| | | )} |
| | | {deps.isAndroid && deps.routeSummaryCalloutVisible && routeSummaryText && ( |
| | | <View |
| | | className="route-summary-callout" |
| | | style={deps.routeSummaryCalloutStyle} |
| | | > |
| | | <Image |
| | | className="route-summary-callout-bg" |
| | | src={deps.routeSummaryBgSrc} |
| | | /> |
| | | <View className="route-summary-callout-text"> |
| | | {routeSummaryText} |
| | | </View> |
| | | </View> |
| | | )} |
| | | {deps.isAndroid && deps.carCalloutVisible && shouldShowVehicleMetricCallout && ( |
| | | <View className="car-callout-android" style={deps.carCalloutStyle}> |
| | |
| | | deps.isModifyDestinationReviewVisible, |
| | | deps.markers, |
| | | deps.routeDistance, |
| | | deps.routeSummaryBgSrc, |
| | | deps.routeSummaryCalloutStyle, |
| | | deps.routeSummaryCalloutVisible, |
| | | deps.shouldShowWaitingRadar, |
| | | deps.waitingRadarSrc, |
| | | deps.waitingRadarStyle, |
| | | deps.waitingRadarVisible, |
| | | renderMarkerCallout, |
| | | routeSummaryText, |
| | | shouldShowVehicleMetricCallout, |
| | | vehicleMetricDurationDisplay |
| | | ]); |
| | |
| | | onOpenCouponSelect={deps.handleOpenCouponSelect} |
| | | handleOpenEvaluatePopup={deps.handleOpenEvaluatePopup} |
| | | routeDurationSeconds={deps.routeDuration} |
| | | routeDistance={deps.routeDistance} |
| | | onToggleDetailFade={deps.handleToggleDetailFade} |
| | | orderTrunkStatus={deps.trunkStatus} |
| | | doorUnlockedInCurrentPage={deps.doorUnlockedInCurrentPage} |