| src/constants/api.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/components/calling-car-modules/trip-action-bar.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/components/calling-car.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/hooks/use-order-distance-config.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/index.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/order/index.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/constants/api.ts
@@ -45,6 +45,10 @@ //@ts-ignore export const _AMAP_KEY = AMAP_KEY // 发票URL //@ts-ignore export const _INVOICE_URL = INVOICE_URL // pic CDN export const CDN = 'https://s.xiaopeng.com/xp-fe/mini' // 官渠通用 oss 资源前缀 src/robotaxi/pages/begin-route/components/calling-car-modules/trip-action-bar.tsx
@@ -9,13 +9,15 @@ handleCancelTrip?: () => void; handleModifyDestination?: () => void; handleShareTrip?: () => void; showModifyDestination?: boolean; }; export default function TripActionBar({ tripStatus, handleCancelTrip, handleModifyDestination, handleShareTrip handleShareTrip, showModifyDestination = true }: Props) { // 按行程状态控制按钮集合,避免在 JSX 中堆叠过多条件判断 const actionList = [ @@ -30,7 +32,7 @@ key: "destination", label: "修改目的地", icon: changeDestinationIcon, visible: [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(tripStatus), visible: [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(tripStatus) && showModifyDestination, onClick: handleModifyDestination }, { src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -44,6 +44,8 @@ handleCancelTrip?: () => void; /** 修改目的地 */ handleModifyDestination?: () => void; /** 是否展示修改目的地 */ showModifyDestination?: boolean; /** 分享行程 */ handleShareTrip?: () => void; /** 预估等待分钟数 */ @@ -90,6 +92,7 @@ handleOpenEvaluatePopup, handleCancelTrip, handleModifyDestination, showModifyDestination = true, handleShareTrip, estimatedWaitMinutes, routeDurationSeconds, @@ -208,6 +211,7 @@ tripStatus={tripStatus} handleCancelTrip={handleCancelTrip} handleModifyDestination={handleModifyDestination} showModifyDestination={showModifyDestination} handleShareTrip={handleShareTrip} /> </View> src/robotaxi/pages/begin-route/hooks/use-order-distance-config.ts
@@ -11,6 +11,11 @@ queuePollingTime?: number; walkingNavigationDistance?: number; maxStandingTiming?: number; xczSyDistance?: number; xczSyTime?: number; xczCaCount?: number; xczHmSpeedPerHour?: number; xczHmDuration?: number; }; type Deps = { @@ -43,7 +48,12 @@ adsorptionDistance: Number(res.adsorptionDistance), queuePollingTime: Number(res.queuePollingTime), walkingNavigationDistance: Number(res.walkingNavigationDistance), maxStandingTiming: Number(res.maxStandingTiming) maxStandingTiming: Number(res.maxStandingTiming), xczSyDistance: Number(res.xczSyDistance), xczSyTime: Number(res.xczSyTime), xczCaCount: Number(res.xczCaCount), xczHmSpeedPerHour: Number(res.xczHmSpeedPerHour), xczHmDuration: Number(res.xczHmDuration) }; setDistanceConfig(nextConfig); setDistanceConfigLoaded(true); src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -93,6 +93,8 @@ handleToggleDetailFade: (expanded: boolean, hasFare: boolean) => void; /** 修改目的地 */ handleModifyDestination: () => void; /** 是否展示修改目的地 */ showModifyDestination: boolean; /** 分享行程 */ handleShareTrip: () => void; /** 路线选择展开状态 */ @@ -332,6 +334,7 @@ handleOpenCancelPopup={(displayInfo) => deps.handleOpenCancelPopupByTripStatus(deps.tripStatus, displayInfo)} handleCancelTrip={() => deps.handleOpenCancelPopupByTripStatus(deps.tripStatus, true)} handleModifyDestination={deps.handleModifyDestination} showModifyDestination={deps.showModifyDestination} handleShareTrip={deps.handleShareTrip} handleCancelTimeout={() => deps.handleOpenCancelPopupWithType(4)} handleFinishTimeout={deps.handleFinishCountdownTimeout} src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts
@@ -34,6 +34,11 @@ setVehicleMoveAlongPoints: (points: any[]) => void; setRouteDistance: (value: any) => void; setRouteDuration: (value: number) => void; setOrderRouteMetrics?: (metrics: { distanceMeters: number | null; durationSeconds: number | null; speedKmh: number | null; }) => void; setBeforeUserEndSite: (site: { longitude: number; latitude: number } | null) => void; erasePassedRouteSegments: (points: any[], servedPoints?: any[]) => void; applyRoutePoints: (points: any[]) => boolean; @@ -190,14 +195,22 @@ deps.setVehicleDirection(normalizedDirection); } const route = safeParse(payload?.content?.route, {}); const distanceKm = normalizeDistanceKm(Number(route?.distance)); const distanceMeters = Number(route?.distance); const durationSecondsRaw = Number(route?.duration); const speedKmh = Number(route?.speed); const distanceKm = normalizeDistanceKm(distanceMeters); if (distanceKm !== null) { deps.setRouteDistance(distanceKm); } const durationSeconds = normalizeDurationSeconds(Number(route?.duration)); const durationSeconds = normalizeDurationSeconds(durationSecondsRaw); if (durationSeconds !== null) { deps.setRouteDuration(durationSeconds); } deps.setOrderRouteMetrics?.({ distanceMeters: Number.isFinite(distanceMeters) ? distanceMeters : null, durationSeconds: Number.isFinite(durationSecondsRaw) ? durationSecondsRaw : null, speedKmh: Number.isFinite(speedKmh) ? speedKmh : null }); }; const resolveVehiclePoint = (points: any[], servedPoints: any[] = []) => { src/robotaxi/pages/begin-route/index.tsx
@@ -64,6 +64,12 @@ latitude: number; }; type OrderRouteMetrics = { distanceMeters: number | null; durationSeconds: number | null; speedKmh: number | null; }; export default function BeginRoute() { const WAITING_TIMER_START_KEY = 'waitingTimerStartAt'; // 等待计时开始时间 const WAITING_TIMER_ELAPSED_KEY = 'waitingTimerElapsed'; // 等待计时已过秒数 @@ -129,6 +135,14 @@ const [evaluateSubmitVersion, setEvaluateSubmitVersion] = useState(0); // 评价提交版本号 const [evaluateCloseVersion, setEvaluateCloseVersion] = useState(0); // 评价关闭版本号 const [showCouponSelectPopup, setShowCouponSelectPopup] = useState(false); // 优惠券选择弹窗展示状态 const [orderRouteMetrics, setOrderRouteMetrics] = useState<OrderRouteMetrics>({ distanceMeters: null, durationSeconds: null, speedKmh: null }); const [modifyDestinationUsedCount, setModifyDestinationUsedCount] = useState(0); // 本次行程已成功修改目的地次数 const [lowSpeedDurationMs, setLowSpeedDurationMs] = useState(0); // 连续低速持续时长(毫秒) const lowSpeedStartAtRef = useRef<number | null>(null); // 连续低速开始时间戳 const tripStatusRef = useRef(tripStatus); // 行程状态引用 const lastTripStatusRef = useRef(tripStatus); // 上一次行程状态,用于检测状态变化 @@ -138,6 +152,16 @@ tripStatusForMapSyncRef.current = tripStatus; Taro.setStorageSync('tripStatus', tripStatus); }, [tripStatus]); useEffect(() => { setModifyDestinationUsedCount(0); setOrderRouteMetrics({ distanceMeters: null, durationSeconds: null, speedKmh: null }); lowSpeedStartAtRef.current = null; setLowSpeedDurationMs(0); }, [orderId]); // 行程状态显示规则配置:showPickup-显示上车点,showDropoff-显示下车点,showCar-显示车辆,showRoute-显示路线,showWalking-显示步行路线,viewport-视口类型,translateCar-车辆是否平移 const STATUS_RULES: Record<TripStatus, StatusRule> = { [TripStatus.PublishRoute]: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: true, viewport: 'route_walking', translateCar: false }, @@ -198,6 +222,7 @@ setEndSite, toSelectSite, onModifySuccess: () => { setModifyDestinationUsedCount((prev) => prev + 1); // 成功修改后触发路线重新获取 // 下一次渲染时 isModifyDestinationReviewVisible 变为 false // 我们需要强制让选路状态退出,同时获取后端的新行程路线 @@ -428,6 +453,81 @@ const requoteExpireSeconds = Number.isFinite(Number(distanceConfig?.maxStandingTiming)) && Number(distanceConfig?.maxStandingTiming) > 0 ? Number(distanceConfig?.maxStandingTiming) : DEFAULT_REQUOTE_EXPIRE_SECONDS; // 重新报价有效期(秒) const modifyDistanceLimitMeters = Number(distanceConfig?.xczSyDistance); // 修改目的地剩余里程限制(米) const modifyDurationLimitMinutes = Number(distanceConfig?.xczSyTime); // 修改目的地剩余时间限制(分钟) const modifyCountLimit = Number(distanceConfig?.xczCaCount); // 单次行程允许修改目的地次数上限 const lowSpeedLimitKmh = Number(distanceConfig?.xczHmSpeedPerHour); // 低速豁免阈值(km/h) const lowSpeedLimitMinutes = Number(distanceConfig?.xczHmDuration); // 低速豁免持续时间(分钟) const isTripStatusAllowModifyEntry = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(tripStatus); useEffect(() => { if (!isTripStatusAllowModifyEntry) { lowSpeedStartAtRef.current = null; setLowSpeedDurationMs(0); return; } if (!Number.isFinite(lowSpeedLimitKmh) || lowSpeedLimitKmh <= 0) { lowSpeedStartAtRef.current = null; setLowSpeedDurationMs(0); return; } const currentSpeed = Number(orderRouteMetrics?.speedKmh); if (!Number.isFinite(currentSpeed) || currentSpeed >= lowSpeedLimitKmh) { lowSpeedStartAtRef.current = null; setLowSpeedDurationMs(0); return; } const now = Date.now(); if (!lowSpeedStartAtRef.current) { lowSpeedStartAtRef.current = now; setLowSpeedDurationMs(0); return; } setLowSpeedDurationMs(now - lowSpeedStartAtRef.current); }, [isTripStatusAllowModifyEntry, orderRouteMetrics?.speedKmh, lowSpeedLimitKmh]); const showModifyDestination = useMemo(() => { if (!isTripStatusAllowModifyEntry) { return false; } const hasDistanceLimit = Number.isFinite(modifyDistanceLimitMeters) && modifyDistanceLimitMeters > 0; const hasDurationLimit = Number.isFinite(modifyDurationLimitMinutes) && modifyDurationLimitMinutes > 0; const hasCountLimit = Number.isFinite(modifyCountLimit) && modifyCountLimit > 0; const hasLowSpeedLimit = Number.isFinite(lowSpeedLimitKmh) && lowSpeedLimitKmh > 0; const hasLowSpeedDurationLimit = Number.isFinite(lowSpeedLimitMinutes) && lowSpeedLimitMinutes > 0; const currentDistance = Number(orderRouteMetrics?.distanceMeters); const currentDurationSeconds = Number(orderRouteMetrics?.durationSeconds); const currentSpeed = Number(orderRouteMetrics?.speedKmh); const hitDistanceLimit = hasDistanceLimit && Number.isFinite(currentDistance) && currentDistance < modifyDistanceLimitMeters; const hitDurationLimit = hasDurationLimit && Number.isFinite(currentDurationSeconds) && (currentDurationSeconds / 60) < modifyDurationLimitMinutes; const hitCountLimit = hasCountLimit && modifyDestinationUsedCount >= modifyCountLimit; const hitAnyLimit = hitDistanceLimit || hitDurationLimit || hitCountLimit; const hitLowSpeedExemption = hasLowSpeedLimit && hasLowSpeedDurationLimit && Number.isFinite(currentSpeed) && currentSpeed < lowSpeedLimitKmh && lowSpeedDurationMs >= lowSpeedLimitMinutes * 60 * 1000; if (hitLowSpeedExemption) { return true; } return !hitAnyLimit; }, [ isTripStatusAllowModifyEntry, modifyDistanceLimitMeters, modifyDurationLimitMinutes, modifyCountLimit, lowSpeedLimitKmh, lowSpeedLimitMinutes, orderRouteMetrics?.distanceMeters, orderRouteMetrics?.durationSeconds, orderRouteMetrics?.speedKmh, modifyDestinationUsedCount, lowSpeedDurationMs ]); const { handleDidShow } = usePageRestore({ @@ -546,6 +646,7 @@ setVehicleMoveAlongPoints, setRouteDistance, setRouteDuration, setOrderRouteMetrics, setBeforeUserEndSite, erasePassedRouteSegments: routeErasePassedRouteSegments, applyRoutePoints: routeApplyRoutePoints, @@ -842,6 +943,7 @@ handleOpenEvaluatePopup, handleToggleDetailFade, handleModifyDestination, showModifyDestination, handleShareTrip, trunkStatus, routeOptions, src/robotaxi/pages/order/index.tsx
@@ -3,7 +3,7 @@ import { View, Image } from '@tarojs/components'; import './index.less'; import { getOrderHistoryList, OrderHistoryGroup } from '../../../robotaxi/api/order'; import { INVOICE_URL } from '../../../constants/api'; import { _INVOICE_URL } from '../../../constants/api'; type OrderFilterKey = 'all' | 'pendingPay' | 'completed' | 'cancelled'; @@ -107,7 +107,7 @@ const toInvoicePage = () => { Taro.navigateTo({ url: `/pages/webpage/index?pageUrl=${encodeURIComponent(INVOICE_URL)}` url: `/pages/webpage/index?pageUrl=${encodeURIComponent(_INVOICE_URL)}` }) }