import Taro, { useDidHide } from '@tarojs/taro';
|
import { useEffect } from 'react';
|
import { getOrderDetail } from '../../../api/order';
|
import { parseLocationCoordinateString } from '../../../../utils/Robotaxi-public-method';
|
import { TripStatus, CancelType } from '../../../constants/trip-status';
|
|
type Deps = {
|
/** 等待计时开始 key */
|
waitingTimerStartKey: string;
|
/** 等待计时累计 key */
|
waitingTimerElapsedKey: string;
|
/** 重新报价过期秒数 */
|
requoteExpireSeconds: number;
|
/** 行程状态 */
|
tripStatus: TripStatus;
|
/** 行程状态引用 */
|
tripStatusRef: React.MutableRefObject<TripStatus>;
|
/** 上一行程状态引用 */
|
lastTripStatusRef: React.MutableRefObject<TripStatus>;
|
/** 行程同步引用 */
|
tripStatusForMapSyncRef: React.MutableRefObject<TripStatus>;
|
/** 重新报价触发引用 */
|
requoteTriggeredRef: React.MutableRefObject<boolean>;
|
/** 路线是否初始化 */
|
orderRouteInitializedRef: React.MutableRefObject<boolean>;
|
/** 视口锁定引用 */
|
tripStatus6ViewportLockedRef: React.MutableRefObject<boolean>;
|
/** marker 刷新引用 */
|
tripStatus6MarkerRefreshedRef: React.MutableRefObject<boolean>;
|
/** 完整路线引用 */
|
currentFullRouteRef: React.MutableRefObject<any[]>;
|
/** 订单详情请求序号 */
|
orderDetailRouteRequestIdRef: React.MutableRefObject<number>;
|
/** 当前路线点 */
|
routePoints: any[];
|
/** 起点 */
|
startSite: any;
|
/** 结束点 */
|
endSite: any;
|
/** 当前经度 */
|
longitude: number | null;
|
/** 当前纬度 */
|
latitude: number | null;
|
/** 等待秒数 */
|
elapsedSeconds: number;
|
/** 等待秒数设置 */
|
setElapsedSeconds: (value: number) => void;
|
/** 设置步行点 */
|
setWalkingPoints: (value: any[]) => void;
|
/** 设置步行时长 */
|
setWalkingTime: (value: number) => void;
|
/** 设置路线点 */
|
setRoutePoints: (value: any[]) => void;
|
/** 设置 polyline */
|
setPolyline: (value: any[]) => void;
|
/** 行程状态设置 */
|
setTripStatus: (value: TripStatus) => void;
|
/** 订单状态重置版本 */
|
setResetDetailVersion: (updater: (v: number) => number) => void;
|
/** 是否提交中 */
|
setCallSubmitting: (value: boolean) => void;
|
/** 完成倒计时开关 */
|
setFinishCountdownEnabled: (value: boolean) => void;
|
/** 价格更新版本 */
|
setPriceUpdateVersion: (value: number | ((v: number) => number)) => void;
|
/** 打开公告弹窗 */
|
setShowAnnouncementsPopup: (value: boolean) => void;
|
/** 设置路线距离 */
|
setRouteDistance: (value: any) => void;
|
/** 设置路线时长 */
|
setRouteDuration: (value: any) => void;
|
/** 设置取消类型 */
|
setCancelType: (value: CancelType) => void;
|
/** 设置取消弹窗 */
|
setShowCancelPopup: (value: boolean) => void;
|
/** 显示乘客须知 */
|
setShowPassengerNotice: (value: boolean) => void;
|
/** 车辆方向设置 */
|
setVehicleDirection: (value: number) => void;
|
/** 车辆位置引用 */
|
lastVehiclePointRef: React.MutableRefObject<{ latitude: number; longitude: number } | null>;
|
/** 车辆更新时间引用 */
|
lastVehicleUpdateAtRef: React.MutableRefObject<number>;
|
/** 路线签名引用 */
|
polylineKeyRef: React.MutableRefObject<string>;
|
/** 路线更新时间引用 */
|
lastPolylineUpdateAtRef: React.MutableRefObject<number>;
|
/** 路线首点引用 */
|
lastRoutePointRef: React.MutableRefObject<{ latitude: number; longitude: number } | null>;
|
/** 路线更新时间引用 */
|
lastRouteUpdateAtRef: React.MutableRefObject<number>;
|
/** 最后擦除索引引用 */
|
lastErasedIndexRef: React.MutableRefObject<number>;
|
/** 是否展示客服弹窗 */
|
setShowCustomerService: (value: boolean) => void;
|
/** 车辆信息 */
|
vehicleInfo: any;
|
/** 费用信息 */
|
fareEstimate: any;
|
/** 请求预估路线 */
|
routeRequestRouteAndFare: (
|
originLat: number,
|
originLng: number,
|
destLat: number,
|
destLng: number,
|
startPointId: string | number,
|
force?: boolean
|
) => void;
|
/** 更新步行路线 */
|
routeUpdateWalkingRouteForStart: (startSite: any) => void;
|
/** 应用路线点 */
|
routeApplyRoutePoints: (points: any[]) => boolean;
|
/** 擦除已行驶路段 */
|
routeErasePassedRouteSegments: (newPoints: any[]) => void;
|
/** 更新车辆位置 */
|
updateVehicleLocationThrottled: (lat: number, lng: number, force?: boolean) => void;
|
/** 视口 padding 更新 */
|
updateMapViewportPadding: () => void;
|
};
|
|
/**
|
* 行程运行时编排:计时器、状态副作用、资源预载、页面隐藏持久化。
|
*/
|
export default function useTripRuntime(deps: Deps) {
|
/**
|
* 清理等待计时缓存。
|
*/
|
const clearWaitingTimerStorage = () => {
|
Taro.removeStorageSync(deps.waitingTimerStartKey);
|
Taro.removeStorageSync(deps.waitingTimerElapsedKey);
|
};
|
|
/**
|
* 检查并展示乘客须知。
|
*/
|
const checkAndShowPassengerNotice = () => {
|
const checkedTimeRaw = Taro.getStorageSync('passengerNoticeCheckedTime');
|
if (!checkedTimeRaw) {
|
deps.setShowPassengerNotice(true);
|
return;
|
}
|
const checkedTime = Number(checkedTimeRaw);
|
const threeMonthMs = 3 * 30 * 24 * 60 * 60 * 1000;
|
if (Number.isFinite(checkedTime) && Date.now() - checkedTime > threeMonthMs) {
|
deps.setShowPassengerNotice(true);
|
Taro.removeStorageSync('passengerNoticeCheckedTime');
|
}
|
};
|
|
/**
|
* 启动重新报价计时。
|
*/
|
const startRequoteTimer = () => {
|
deps.requoteTriggeredRef.current = false;
|
const startAt = Date.now();
|
const updateRequoteElapsed = () => {
|
const nextElapsed = Math.max(0, Math.floor((Date.now() - startAt) / 1000));
|
if (nextElapsed < deps.requoteExpireSeconds || deps.requoteTriggeredRef.current) return;
|
deps.requoteTriggeredRef.current = true;
|
const s = Taro.getStorageSync('startSite') || {};
|
const e = Taro.getStorageSync('endSite') || {};
|
if (s?.latitude && s?.longitude && e?.latitude && e?.longitude) {
|
deps.routeRequestRouteAndFare(s.latitude, s.longitude, e.latitude, e.longitude, s?.id, true);
|
}
|
};
|
updateRequoteElapsed();
|
return setInterval(updateRequoteElapsed, 1000);
|
};
|
|
/**
|
* 启动等待计时。
|
*/
|
const startWaitingTimer = () => {
|
const cachedStartAt = Number(Taro.getStorageSync(deps.waitingTimerStartKey));
|
const cachedElapsed = Number(Taro.getStorageSync(deps.waitingTimerElapsedKey));
|
let startAt = cachedStartAt;
|
if (!Number.isFinite(startAt) || startAt <= 0) {
|
startAt = Number.isFinite(cachedElapsed) && cachedElapsed >= 0
|
? Date.now() - cachedElapsed * 1000
|
: Date.now();
|
Taro.setStorageSync(deps.waitingTimerStartKey, startAt);
|
}
|
const updateElapsed = () => {
|
const nextElapsed = Math.max(0, Math.floor((Date.now() - startAt) / 1000));
|
deps.setElapsedSeconds(nextElapsed);
|
};
|
updateElapsed();
|
return setInterval(updateElapsed, 1000);
|
};
|
|
/**
|
* 刷新完成态轨迹。
|
*/
|
const refreshOrderDetailRoute = (prevTripStatus: TripStatus) => {
|
const shouldSkipOrderDetailRefresh = prevTripStatus === TripStatus.TripFinished && deps.tripStatus === TripStatus.PaymentSuccess;
|
if (shouldSkipOrderDetailRefresh) return;
|
deps.orderDetailRouteRequestIdRef.current += 1;
|
const currentRequestId = deps.orderDetailRouteRequestIdRef.current;
|
getOrderDetail(Taro.getStorageSync('orderId')).then((res: any) => {
|
if (currentRequestId !== deps.orderDetailRouteRequestIdRef.current) return;
|
if (![TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(deps.tripStatusRef.current)) return;
|
const mapCtx = Taro.createMapContext('map');
|
if (!res.trajectories) {
|
mapCtx.moveToLocation({ latitude: 39.916527, longitude: 116.397128 });
|
return;
|
}
|
const points = res.trajectories
|
.map((item: any) => parseLocationCoordinateString(item.location))
|
.filter(Boolean);
|
deps.routeApplyRoutePoints(points);
|
});
|
};
|
|
useEffect(() => {
|
const wasHold = [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.lastTripStatusRef.current);
|
const isHold = [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus);
|
if (deps.tripStatus !== TripStatus.VehicleArrivedPickup) Taro.removeStorageSync('pickupEndTime');
|
if (deps.tripStatus !== TripStatus.ArrivedDestination) Taro.removeStorageSync('finishEndTime');
|
if (!wasHold && isHold) {
|
deps.orderRouteInitializedRef.current = false;
|
deps.tripStatus6ViewportLockedRef.current = false;
|
deps.tripStatus6MarkerRefreshedRef.current = false;
|
deps.currentFullRouteRef.current = [];
|
}
|
if (!isHold) {
|
deps.tripStatus6ViewportLockedRef.current = false;
|
deps.tripStatus6MarkerRefreshedRef.current = false;
|
}
|
deps.lastTripStatusRef.current = deps.tripStatus;
|
}, [deps.tripStatus, deps.requoteExpireSeconds]);
|
|
useEffect(() => {
|
deps.setFinishCountdownEnabled(false);
|
}, [deps.vehicleInfo?.id]);
|
|
useEffect(() => {
|
if (deps.tripStatus === TripStatus.PublishRoute) checkAndShowPassengerNotice();
|
}, [deps.tripStatus]);
|
|
useEffect(() => {
|
Taro.setStorageSync('tripStatus', deps.tripStatus);
|
deps.tripStatusRef.current = deps.tripStatus;
|
const prevTripStatus = deps.tripStatusForMapSyncRef.current;
|
let timer: any = null;
|
let requoteTimer: any = null;
|
if (deps.tripStatus === TripStatus.PublishRoute) {
|
deps.setCallSubmitting(false);
|
requoteTimer = startRequoteTimer();
|
} else if (deps.tripStatus === TripStatus.Publishing || deps.tripStatus === TripStatus.CallingCar) {
|
deps.requoteTriggeredRef.current = false;
|
deps.setWalkingPoints([]);
|
deps.setWalkingTime(0);
|
timer = startWaitingTimer();
|
} else if ([TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(deps.tripStatus)) {
|
deps.requoteTriggeredRef.current = false;
|
const cachedStartSite = Taro.getStorageSync('startSite') || {};
|
if (cachedStartSite?.latitude && cachedStartSite?.longitude) {
|
deps.routeUpdateWalkingRouteForStart(cachedStartSite);
|
} else {
|
deps.setWalkingPoints([]);
|
deps.setWalkingTime(0);
|
}
|
deps.setElapsedSeconds(0);
|
clearWaitingTimerStorage();
|
} else if (deps.tripStatus === TripStatus.ArrivedDestination) {
|
deps.requoteTriggeredRef.current = false;
|
deps.setWalkingPoints([]);
|
deps.setWalkingTime(0);
|
deps.setElapsedSeconds(0);
|
clearWaitingTimerStorage();
|
} else if ([TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(deps.tripStatus)) {
|
deps.requoteTriggeredRef.current = false;
|
deps.setWalkingPoints([]);
|
deps.setWalkingTime(0);
|
refreshOrderDetailRoute(prevTripStatus);
|
deps.setElapsedSeconds(0);
|
clearWaitingTimerStorage();
|
} else {
|
deps.requoteTriggeredRef.current = false;
|
deps.setWalkingPoints([]);
|
deps.setWalkingTime(0);
|
deps.setElapsedSeconds(0);
|
clearWaitingTimerStorage();
|
}
|
deps.tripStatusForMapSyncRef.current = deps.tripStatus;
|
return () => {
|
if (timer) clearInterval(timer);
|
if (requoteTimer) clearInterval(requoteTimer);
|
};
|
}, [deps.tripStatus]);
|
|
useDidHide(() => {
|
deps.setResetDetailVersion((v: number) => v + 1);
|
const currentTripStatus = deps.tripStatusRef.current;
|
if (currentTripStatus === TripStatus.Publishing || currentTripStatus === TripStatus.CallingCar) {
|
const startAt = Number(Taro.getStorageSync(deps.waitingTimerStartKey));
|
if (Number.isFinite(startAt) && startAt > 0) {
|
const currentElapsed = Math.max(0, Math.floor((Date.now() - startAt) / 1000));
|
Taro.setStorageSync(deps.waitingTimerElapsedKey, currentElapsed);
|
} else {
|
Taro.setStorageSync(deps.waitingTimerElapsedKey, deps.elapsedSeconds);
|
}
|
} else {
|
Taro.removeStorageSync(deps.waitingTimerStartKey);
|
Taro.removeStorageSync(deps.waitingTimerElapsedKey);
|
}
|
});
|
|
useEffect(() => {
|
const originLng = deps.startSite?.longitude || deps.longitude;
|
const originLat = deps.startSite?.latitude || deps.latitude;
|
if (deps.tripStatus === TripStatus.PublishRoute && originLng && originLat && deps.endSite?.longitude && deps.endSite?.latitude) {
|
deps.routeRequestRouteAndFare(originLat, originLng, deps.endSite.latitude, deps.endSite.longitude, deps.startSite?.id, false);
|
}
|
if (deps.tripStatus === TripStatus.PublishRoute && deps.startSite?.latitude && deps.startSite?.longitude) {
|
deps.routeUpdateWalkingRouteForStart(deps.startSite);
|
}
|
}, [deps.startSite?.longitude, deps.startSite?.latitude, deps.endSite?.longitude, deps.endSite?.latitude, deps.longitude, deps.latitude]);
|
|
useEffect(() => {
|
deps.updateMapViewportPadding();
|
}, [deps.tripStatus]);
|
|
useEffect(() => {
|
const timer = setTimeout(() => {
|
deps.updateMapViewportPadding();
|
}, 300); // 延迟 300ms,避免 Taro.nextTick 过早触发,导致地图视口 padding 计算不准确
|
return () => clearTimeout(timer);
|
}, [
|
deps.tripStatus,
|
deps.startSite?.name,
|
deps.endSite?.name,
|
deps.fareEstimate?.waitingTime,
|
deps.fareEstimate?.finalFareStr,
|
deps.fareEstimate?.originalFareStr,
|
deps.fareEstimate?.discountAmount,
|
]);
|
|
return {
|
checkAndShowPassengerNotice,
|
clearWaitingTimerStorage
|
};
|
}
|