hejianhao
2026-03-17 70459d4ca44f8c7842a6b0d92e1b59a165a4f963
feat: 优化订单状态显示、地图标记和用户交互体验

- 在订单状态9和10之间新增“待评价”状态显示
- 优化订单详情页的地图展示和样式布局
- 改进取消订单弹窗,根据行程状态动态显示到达时间
- 修复车辆标记在特定状态下的显示问题
- 调整解锁操作的提示文案,提升用户体验
- 增强WebSocket消息日志记录,排除pong类型消息
- 优化价格明细页面的条件渲染逻辑
- 修复城市切换后地图中心点定位问题
12个文件已修改
318 ■■■■■ 已修改文件
src/pages/robotaxi/home/index.tsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/cancel-popup.tsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/passenger-notice.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/expense-detail/index.tsx 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail/index.less 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail/index.tsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/index.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/websocket.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.tsx
@@ -78,6 +78,7 @@
  const skipNextLocationCenterRef = useRef(false);
  const skipCityChangeEffectRef = useRef(false);
  const isCitySwitchingRef = useRef(false);
  const shouldMoveToSwitchedCityCenterRef = useRef(false);
  const lastSelectedCityCodeRef = useRef<string | undefined>(Taro.getStorageSync('selectedCity')?.code);
  const orderStatusBound = useRef(false);
@@ -126,7 +127,7 @@
  const mapActiveStatusToTripStatus = (status: number) => {
    if (status === 0) return 1;
    if (status === 1) return 2;
    if (status === 1) return 3;
    if (status === 3 || status === 2) return 4;
    if (status === 4) return 5;
    if (status === 5) return 6;
@@ -150,6 +151,7 @@
      const nextCode = selectedCity.code;
      if (lastSelectedCityCodeRef.current && nextCode && lastSelectedCityCodeRef.current !== nextCode) {
        isCitySwitchingRef.current = true;
        shouldMoveToSwitchedCityCenterRef.current = true;
        Taro.removeStorageSync('startSite');
        Taro.removeStorageSync('endSite');
        setCurrentSite({});
@@ -238,7 +240,11 @@
          if (startSite && startSite.latitude && startSite.longitude) {
            if (isCitySwitchingRef.current) {
              isCitySwitchingRef.current = false;
              return;
              const shouldMoveToCityCenter = shouldMoveToSwitchedCityCenterRef.current;
              shouldMoveToSwitchedCityCenterRef.current = false;
              if (shouldMoveToCityCenter) {
                return;
              }
            }
            setLatitude(startSite.latitude);
            setLongitude(startSite.longitude);
@@ -258,7 +264,42 @@
          } else {
            if (isCitySwitchingRef.current) {
              isCitySwitchingRef.current = false;
              skipNextLocationCenterRef.current = true;
              const shouldMoveToCityCenter = shouldMoveToSwitchedCityCenterRef.current;
              shouldMoveToSwitchedCityCenterRef.current = false;
              if (!shouldMoveToCityCenter) {
                const shouldUpdateCenter = !skipNextLocationCenterRef.current;
                skipNextLocationCenterRef.current = false;
                const hasEntered = Taro.getStorageSync(FIRST_ENTER_KEY) === 'true';
                if (!hasEntered) {
                  skipFenceIncludePointsRef.current = true;
                  getLocationInfo(true);
                  Taro.setStorageSync(FIRST_ENTER_KEY, 'true');
                  return;
                }
                getLocationInfo(shouldUpdateCenter);
                return;
              }
              const switchedCity = Taro.getStorageSync('selectedCity');
              if (switchedCity?.name) {
                getCityLocation(switchedCity.name).then((loc) => {
                  setLatitude(loc.latitude);
                  setLongitude(loc.longitude);
                  shouldIgnoreRegionChange.current = true;
                  skipNextLocationCenterRef.current = true;
                  const mapCtx = Taro.createMapContext('map');
                  mapCtx.moveToLocation({
                    latitude: loc.latitude,
                    longitude: loc.longitude,
                  });
                  if (switchedCity?.code) {
                    updateMapData(loc.latitude, loc.longitude, switchedCity.code);
                  }
                }).catch(() => {
                  skipNextLocationCenterRef.current = true;
                });
              } else {
                skipNextLocationCenterRef.current = true;
              }
              return;
            }
            const shouldUpdateCenter = !skipNextLocationCenterRef.current;
@@ -617,6 +658,14 @@
  const toOrigin = async () => {
    const ok = await ensureNetwork();
    if (!ok) return;
    const systemSetting = await Promise.resolve((Taro as any).getSystemSetting?.());
    if (systemSetting?.locationEnabled === false) {
      Taro.showToast({
        title: '需开启设备GPS/定位服务开关',
        icon: 'none'
      });
      return;
    }
    if (isLocationAuthDenied) return;
    shouldIgnoreRegionChange.current = false;
    isReturningToOrigin.current = true;
@@ -1211,10 +1260,10 @@
            </View>
            {/* 订单状态卡片 */}
            {
              [2, 3, 4, 5, 6, 7, 8].includes(tripStatus) &&
              [3, 4, 5, 6, 7, 8].includes(tripStatus) &&
              <View className="order-status">
                {
                  [2].includes(tripStatus) &&
                  [3].includes(tripStatus) &&
                  <View className="order-status-left">
                    <View className="order-status-left-item">
                      <Image className="order-status-left-item-icon" src={require('../../../robotaxi/assets/pointer.png')} />
@@ -1225,7 +1274,7 @@
                  </View>
                }
                {
                  [3, 4, 5, 6, 7, 8].includes(tripStatus) &&
                  [4, 5, 6, 7, 8].includes(tripStatus) &&
                  <View className="order-status-left">
                    {
                      orderData.vehiclePictures ?
src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -463,20 +463,20 @@
            const isFailure = res?.success === false || (typeof res?.code === 'number' && res.code !== 0);
            if (isFailure) {
                Taro.showToast({
                    title: res?.msg || '网络连接不稳定,解锁失败,请重试',
                    title: res?.msg || '解锁失败,请重试或联系客服',
                    icon: 'none'
                });
                return;
            }
            if (doorType === 'trunk') {
                Taro.showToast({
                    title: '后备箱已解锁',
                    title: '打开成功',
                    icon: 'none'
                });
            }
            if (doorType === 'door') {
                Taro.showToast({
                    title: '车门已解锁',
                    title: '右侧车门已解锁',
                    icon: 'none'
                });
                if (!hasShownAnnouncementsRef.current) {
@@ -486,7 +486,7 @@
            }
        }).catch(() => {
            Taro.showToast({
                title: res?.msg || '网络连接不稳定,解锁失败,请重试',
                title: res?.msg || '解锁失败,请重试或联系客服',
                icon: 'none'
            });
        });
src/robotaxi/pages/begin-route/components/cancel-popup.tsx
@@ -3,7 +3,8 @@
import { useEffect, useState } from 'react';
import './css-folder/cancel-popup.less'
import {
    clearOrderTripStorage
    clearOrderTripStorage,
    formatDurationDisplay
} from '../../../../utils/Robotaxi-public-method'
@@ -13,12 +14,14 @@
    startSite?: any;
    endSite?: any;
    waitMinutes?: number;
    tripStatus?: number;
    routeDurationSeconds?: number | null;
    discountAmount?: number;
    onClose?: () => void;
    onConfirm?: () => void;
}
export default function CancelPopup({ show = false, cancelType = 1, startSite, endSite, waitMinutes, discountAmount, onClose, onConfirm }: Props) {
export default function CancelPopup({ show = false, cancelType = 1, startSite, endSite, waitMinutes, tripStatus, routeDurationSeconds, discountAmount, onClose, onConfirm }: Props) {
    const [visible, setVisible] = useState(false);
    const [animationData, setAnimationData] = useState<any>();
    const cachedStartSite = startSite || Taro.getStorageSync('startSite') || {};
@@ -26,6 +29,7 @@
    const startSiteName = cachedStartSite?.name || cachedStartSite?.address || '-';
    const endSiteName = cachedEndSite?.name || cachedEndSite?.address || '-';
    const waitMinutesValue = typeof waitMinutes === 'number' && !Number.isNaN(waitMinutes) ? waitMinutes : 2;
    const durationDisplay = formatDurationDisplay(routeDurationSeconds);
    const discountAmountValue = typeof discountAmount === 'number' && !Number.isNaN(discountAmount) ? discountAmount : 0;
    const STATUS_MAP = {
@@ -34,7 +38,9 @@
            tip: `本单限时优惠${discountAmountValue}元,重新叫车可能等待更久`,
        },
        2: {
            title: `车辆还有${waitMinutesValue}分钟到达,确定取消吗`,
            title: tripStatus === 4
                ? `车辆还有${durationDisplay.value}${durationDisplay.unit}到达,确定取消吗`
                : `车辆还有${waitMinutesValue}分钟到达,确定取消吗`,
            tip: `本单限时优惠${discountAmountValue}元,重新叫车可能等待更久`,
        },
        3: {
@@ -79,6 +85,11 @@
    }, [show]);
    const closePopup = () => {
        if (cancelType == 5) {
            onConfirm?.();
            onClose?.();
            return
        }
        if (cancelType == 4) {
            Taro.reLaunch({
                url: '/pages/robotaxi/home/index',
src/robotaxi/pages/begin-route/components/passenger-notice.tsx
@@ -12,7 +12,7 @@
export default function PassengerNotice({ show = false, onClose }: Props) {
    const [visible, setVisible] = useState(false);
    const [animationData, setAnimationData] = useState<any>();
    const [checked, setChecked] = useState(false);
    const [checked, setChecked] = useState(true);
    const [noticeContent, setNoticeContent] = useState('');
    useEffect(() => {
src/robotaxi/pages/begin-route/index.tsx
@@ -61,7 +61,7 @@
  const [polyline, setPolyline] = useState<any[]>([]); // 路线
  const [startSite, setStartSite] = useState<any>(null); // 上车点
  const [endSite, setEndSite] = useState<any>({}); // 下车点
  const [tripStatus, setTripStatus] = useState<number>(Number(Taro.getStorageSync('tripStatus')) || 1); // 行程状态 1:发布行程 2:发布中 3:呼叫车中 4:车辆即将到达 5:车辆到达上车点 6:车辆行驶中 7:到达目的地 8:行程已结束 9:支付成功
  const [tripStatus, setTripStatus] = useState<number>(Number(Taro.getStorageSync('tripStatus')) || 1); // 行程状态 1:发布行程 2:发布中 3:呼叫车中 4:车辆即将到达 5:车辆到达上车点 6:车辆行驶中 7:到达目的地 8:行程已结束 9:支付成功 10:取消订单时页面只展示一个上车点
  const [elapsedSeconds, setElapsedSeconds] = useState(0); // 等待计时秒数
  const requoteTriggeredRef = useRef(false);
  const [showCancelPopup, setShowCancelPopup] = useState(false); // 取消弹窗是否显示
@@ -125,6 +125,7 @@
    7: { showPickup: false, showDropoff: true, showCar: false, showRoute: false, showWalking: false, viewport: 'dropoff', translateCar: false },
    8: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: false, viewport: 'order_detail', translateCar: false },
    9: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: false, viewport: 'order_detail', translateCar: false },
    10: { showPickup: true, showDropoff: false, showCar: false, showRoute: false, showWalking: false, viewport: 'pickup', translateCar: false },
  };
  const statusRule = useMemo(() => STATUS_RULES[tripStatus] || STATUS_RULES[1], [tripStatus]);
  const shouldShowRoute = statusRule.showRoute;
@@ -154,6 +155,8 @@
  const lastRoutePointRef = useRef<{ latitude: number; longitude: number } | null>(null);
  const lastRouteUpdateAtRef = useRef<number>(0);
  const lastPolylineUpdateAtRef = useRef<number>(0);
  const lastRouteAndFareRequestAtRef = useRef<number>(0);
  const markersSignatureRef = useRef<string>('');
  const isAndroid = useMemo(() => {
    try {
      const info = Taro.getSystemInfoSync();
@@ -164,11 +167,13 @@
    }
  }, []);
  const shouldShowWaitingRadar = isAndroid && [2, 3].includes(tripStatus);
  const vehicleMarkerId = !isAndroid && tripStatus === 6 ? 30 : 3;
  const isCheckedEnter = useRef(false);
  const router = Taro.useRouter();
  useEffect(() => {
    const wasHold = [6].includes(lastTripStatusRef.current);
    const isHold = [6].includes(tripStatus);
    const wasHold = [4, 6].includes(lastTripStatusRef.current);
    const isHold = [4, 6].includes(tripStatus);
    if (!wasHold && isHold) {
      orderRouteInitializedRef.current = false;
      tripStatus6ViewportLockedRef.current = false;
@@ -281,10 +286,7 @@
  };
  const updateCarCalloutPosition = () => {
    if (!isAndroid) {
      return;
    }
    if (!vehicleLocation || ![4, 6].includes(tripStatus)) {
    if (!isAndroid || !vehicleLocation || ![4, 6].includes(tripStatus)) {
      setCarCalloutVisible(false);
      return;
    }
@@ -561,6 +563,11 @@
  // 请求路线和车费
  const requestRouteAndFare = async (originLat: number, originLng: number, destLat: number, destLng: number, force = false) => {
    const now = Date.now();
    if (now - lastRouteAndFareRequestAtRef.current < 1000) {
      return;
    }
    lastRouteAndFareRequestAtRef.current = now;
    const requestKey = `${originLng},${originLat}-${destLng},${destLat}`;
    if (!force && requestKey === routeRequestKeyRef.current) {
      return;
@@ -763,7 +770,9 @@
        lastRouteUpdateAtRef.current = 0;
      }
      if (payload?.showCancelPopup) {
        handleOpenCancelPopupWithType(5);
        setTripStatus(10);
        setCancelType(5);
        setShowCancelPopup(true);
      }
      if (payload?.showPassengerNotice) {
        checkAndShowPassengerNotice();
@@ -1001,6 +1010,7 @@
  useEffect(() => {
    const pickupSite = startSite?.latitude && startSite?.longitude ? startSite : Taro.getStorageSync('startSite');
    const dropoffSite = endSite?.latitude && endSite?.longitude ? endSite : Taro.getStorageSync('endSite');
    const useAndroidNativeTrip1Callout = isAndroid && tripStatus === 1;
    const pickupMarker = pickupSite?.latitude && pickupSite?.longitude ? {
      id: 1,
      latitude: pickupSite.latitude,
@@ -1011,11 +1021,26 @@
      anchor: { x: 0.5, y: 1 },
      zIndex: 1,
      name: pickupSite?.name || '上车点',
      customCallout: {
        anchorY: [2, 3].includes(tripStatus) ? 150 : -5,
        anchorX: 0,
        display: 'ALWAYS'
      }
      ...(useAndroidNativeTrip1Callout
        ? {
          callout: {
            content: `${pickupSite?.name || '上车点'} >`,
            color: '#000000',
            fontSize: 12,
            bgColor: '#FFFFFF',
            borderRadius: 8,
            padding: 8,
            display: 'ALWAYS',
            textAlign: 'center',
          },
        }
        : {
          customCallout: {
            anchorY: [2, 3].includes(tripStatus) ? 150 : -5,
            anchorX: 0,
            display: 'ALWAYS'
          },
        })
    } : null;
    const dropoffMarker = dropoffSite?.latitude && dropoffSite?.longitude ? {
      id: 2,
@@ -1027,14 +1052,29 @@
      anchor: { x: 0.5, y: 1 },
      zIndex: 1,
      name: [7, 9].includes(tripStatus) ? '已到达' : (dropoffSite.name || '目的地'),
      customCallout: {
        anchorY: -5,
        anchorX: 0,
        display: 'ALWAYS'
      }
      ...(useAndroidNativeTrip1Callout
        ? {
          callout: {
            content: `${[7, 9].includes(tripStatus) ? '已到达' : (dropoffSite.name || '目的地')} >`,
            color: '#000000',
            fontSize: 12,
            bgColor: '#FFFFFF',
            borderRadius: 8,
            padding: 8,
            display: 'ALWAYS',
            textAlign: 'center',
          },
        }
        : {
          customCallout: {
            anchorY: -5,
            anchorX: 0,
            display: 'ALWAYS'
          },
        })
    } : null;
    const carMarker = vehicleLocation ? {
      id: 3,
      id: vehicleMarkerId,
      latitude: vehicleLocation.latitude,
      longitude: vehicleLocation.longitude,
      iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e51e92e6-700d-4e83-b3e0-14e6f18e0b9a.png',
@@ -1044,25 +1084,38 @@
      zIndex: 1,
      rotate: typeof vehicleDirection === 'number' ? vehicleDirection : 0,
      name: '车辆位置',
      customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }
      customCallout: { anchorY: 70, anchorX: 0, display: 'ALWAYS' }
    } : null;
    const nextMarkers: any[] = [];
    if (statusRule.showPickup && pickupMarker) nextMarkers.push(pickupMarker);
    if (statusRule.showCar && carMarker) nextMarkers.push(carMarker);
    if (statusRule.showDropoff && dropoffMarker) nextMarkers.push(dropoffMarker);
    const nextMarkersSignature = nextMarkers
      .map((m: any) => [
        m.id,
        Number(m.latitude).toFixed(6),
        Number(m.longitude).toFixed(6),
        m.name || '',
        typeof m.rotate === 'number' ? m.rotate : '',
        m?.customCallout?.anchorY ?? '',
      ].join('|'))
      .join(';');
    const hasDropoffMarker = markersRef.current.some((m: any) => m.id === 2);
    const shouldHoldMarkerByTranslate = tripStatus === 6 && carMarkerInitialized.current && statusRule.showCar && (!statusRule.showDropoff || hasDropoffMarker);
    const shouldHoldMarkerByTranslate = [4, 6].includes(tripStatus) && carMarkerInitialized.current && statusRule.showCar && (!statusRule.showDropoff || hasDropoffMarker);
    const shouldSkipUpdate = shouldHoldMarkerByTranslate && (isAndroid || tripStatus6MarkerRefreshedRef.current);
    if (shouldSkipUpdate) {
    } else {
      setMarkers(nextMarkers);
      if (!isAndroid && tripStatus === 6 && statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current) {
      if (markersSignatureRef.current !== nextMarkersSignature) {
        markersSignatureRef.current = nextMarkersSignature;
        setMarkers(nextMarkers);
      }
      if (!isAndroid && [4, 6].includes(tripStatus) && statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current) {
        tripStatus6MarkerRefreshedRef.current = true;
      }
    }
    carMarkerInitialized.current = statusRule.showCar && !!carMarker;
  }, [tripStatus, startSite?.name, startSite?.latitude, startSite?.longitude, endSite.name, endSite?.latitude, endSite?.longitude, vehicleLocation, vehicleDirection]);
  }, [tripStatus, startSite?.name, startSite?.latitude, startSite?.longitude, endSite.name, endSite?.latitude, endSite?.longitude, vehicleLocation, vehicleDirection, vehicleMarkerId]);
  // 绘制路线与视野适配
  useEffect(() => {
@@ -1205,7 +1258,7 @@
    const mapCtx = Taro.createMapContext('map');
    Taro.nextTick(() => {
      mapCtx.translateMarker({
        markerId: 3,
        markerId: vehicleMarkerId,
        destination: {
          latitude: vehicleLocation.latitude,
          longitude: vehicleLocation.longitude,
@@ -1219,7 +1272,7 @@
        },
      });
    });
  }, [tripStatus, vehicleLocation, vehicleDirection]);
  }, [tripStatus, vehicleLocation, vehicleDirection, vehicleMarkerId]);
  const bindSocketListeners = () => {
    if (!orderStatusBound.current && orderStatusHandlerRef.current) {
@@ -1265,6 +1318,13 @@
  // 页面初始化
  useDidShow(() => {
    const { skipPageFlag } = router.params;
    if (skipPageFlag && Taro.getStorageSync('orderId')) {
      Taro.showToast({
        title: '您已有进行中的订单',
        icon: 'none',
      })
    }
    const s = Taro.getStorageSync('startSite');
    const e = Taro.getStorageSync('endSite');
    const cachedOrderId = Taro.getStorageSync('orderId');
@@ -1538,6 +1598,10 @@
  // 确认取消
  const handleConfirmCancel = async () => {
    if (cancelType === 5) {
      handleOpenCancelPopupWithType(5);
      return;
    }
    const targetOrderId = orderId || Taro.getStorageSync('orderId');
    if (targetOrderId) {
      try {
@@ -1551,6 +1615,7 @@
        Taro.removeStorageSync('pickupEndTime');
        Taro.removeStorageSync('waitingTimerStartAt');
        Taro.removeStorageSync('waitingTimerElapsed');
        setTripStatus(10);
        setCancelType(4);
        return
      } catch (error) {
@@ -1774,12 +1839,13 @@
      >
        {/* 自定义callout */}
        {
          markers.map((item, index) => {
          !(isAndroid && tripStatus === 1) && markers.map((item, index) => {
            const calloutKey = `${tripStatus}-${item.id}`;
            const isWaiting = [2, 3].includes(tripStatus) && index === 0;
            if (isWaiting) {
              return (
                <CoverView slot="callout" marker-id={item.id} key={`${tripStatus}-${item.id}`} className="waiting-callout">
                <CoverView slot="callout" marker-id={item.id} key={calloutKey} className="waiting-callout">
                  <CoverView className="waiting-callout-content">
                    {/* <CoverView className="waiting-callout-bg">
                      <CoverImage className="waiting-callout-bg-img" src={waitingCalloutBgSrc} />
@@ -1802,10 +1868,10 @@
              return null;
            }
            return (
              <CoverView slot="callout" marker-id={item.id} key={`${tripStatus}-${item.id}`} className="custom-callout">
              <CoverView slot="callout" marker-id={item.id} key={calloutKey} className="custom-callout">
                {
                  (item.name !== '车辆位置' && !(item.id === 2 && [7, 9].includes(tripStatus))) &&
                  <CoverView className={`custom-callout-content pr-6 ${isAndroid && 'txt-center '}`}>
                  <CoverView className={`custom-callout-content ${isAndroid && 'txt-center '}`}>
                    <CoverView>{item.name}</CoverView>
                    {
                      isAndroid &&
@@ -1817,8 +1883,8 @@
                  </CoverView>
                }
                {
                  (item.name === '车辆位置' && [4, 6].includes(tripStatus)) &&
                  <CoverView className="custom-callout-content" style={{ padding: '6px 10px' }}>
                  (item.name === '车辆位置' && [4, 6].includes(tripStatus) && !isAndroid) &&
                  <CoverView className="custom-callout-content" style={{ padding: '6px 10px', minWidth: '132px' }}>
                    <CoverView className="car-time">
                      <CoverView className="car-time-num">{routeDistance?.value ?? 0}</CoverView>
                      <CoverView>{routeDistance?.unit === 'm' ? '米' : '公里'}</CoverView>
@@ -1904,7 +1970,7 @@
              }
              {/* 呼叫车辆弹窗 */}
              {
                tripStatus >= 3 &&
                tripStatus >= 3 && tripStatus !== 10 &&
                <CallingCar
                  key={tripStatus}
                  orderId={orderId}
@@ -1944,6 +2010,8 @@
        startSite={startSite}
        endSite={endSite}
        waitMinutes={fareEstimate?.waitingTime}
        tripStatus={tripStatus}
        routeDurationSeconds={routeDuration}
        discountAmount={fareEstimate?.discountAmount}
        onClose={handleCloseCancelPopup}
        onConfirm={handleConfirmCancel}
src/robotaxi/pages/expense-detail/index.tsx
@@ -59,22 +59,32 @@
          <View className='price-notice-label'>{pricingCard.time}({formatTripDistance(pricingCard.distance)},{formatTripDurationFromSeconds(pricingCard.duration)})</View>
          <View className='price-notice-value'>{pricingCard.startPriceStr}元</View>
        </View>
        <View className='flex-between-card'>
          <View className='price-label'>里程费</View>
          <View className='price-value'>{pricingCard.distancePriceStr}元</View>
        </View>
        <View className='flex-between-card mb-16'>
          <View className='price-notice-label'>普通时段({formatTripDistance(pricingCard.distancePriceNormal)})</View>
          <View className='price-notice-value'>{pricingCard.distancePriceStr}元</View>
        </View>
        <View className='flex-between-card'>
          <View className='price-label'>时长费({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View>
          <View className='price-value'>{pricingCard.durationPriceStr}元</View>
        </View>
        <View className='flex-between-card mb-16'>
          <View className='price-notice-label'>普通时段({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View>
          <View className='price-notice-value'>{pricingCard.durationPriceStr}元</View>
        </View>
        {
          pricingCard.distancePriceStr > 0 &&
          <View>
            <View className='flex-between-card'>
              <View className='price-label'>里程费</View>
              <View className='price-value'>{pricingCard.distancePriceStr}元</View>
            </View>
            <View className='flex-between-card mb-16'>
              <View className='price-notice-label'>普通时段({formatTripDistance(pricingCard.distancePriceNormal)})</View>
              <View className='price-notice-value'>{pricingCard.distancePriceStr}元</View>
            </View>
          </View>
        }
        {
          pricingCard.durationPriceStr > 0 &&
          <View>
            <View className='flex-between-card'>
              <View className='price-label'>时长费({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View>
              <View className='price-value'>{pricingCard.durationPriceStr}元</View>
            </View>
            <View className='flex-between-card mb-16'>
              <View className='price-notice-label'>普通时段({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View>
              <View className='price-notice-value'>{pricingCard.durationPriceStr}元</View>
            </View>
          </View>
        }
        {pricingCard.coupon && (
          <View className='flex-between-card'>
            <View className='price-label'>优惠券</View>
@@ -85,7 +95,7 @@
      {/* 总价格 */}
      <View className='flex-card total'>
        <Text className='total-text'>总费用</Text>
        <Text className='num'>{pricingCard.totalPriceStr}</Text>
        <Text className='num'>{pricingCard.finalFareStr}</Text>
        <Text className='unit'>元</Text>
      </View>
      {/* 质疑事项 */}
src/robotaxi/pages/order/detail/index.less
@@ -1,12 +1,11 @@
.order-detail-page {
  background: #F2F3F7;
  min-height: 100vh;
  padding: 32px 0px;
  padding: 32px 24px;
  display: flex;
  flex-direction: column;
  .car-info {
    margin: 0 24px;
    padding: 18px 32px 32px 32px;
    border-radius: 24px;
    background: #FFFFFF;
@@ -14,7 +13,6 @@
    .info-top {
      display: flex;
      justify-content: space-between;
      margin-bottom: 36px;
      .car-name {
        font-family: PingFang SC;
@@ -78,6 +76,7 @@
    }
    .evaluate-card {
      margin-top: 36px;
      width: 638px;
      height: 248px;
      border-radius: 16px;
@@ -141,7 +140,7 @@
  }
  .trip-info {
    margin: 24px 38px;
    margin-top: 24px;
    border-radius: 20.29px;
    background: #FFFFFF;
    padding: 32px 24px;
@@ -150,13 +149,14 @@
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 32px;
      .price-card {
        display: flex;
        align-items: center;
        .price {
          font-size: 48px;
          line-height: 40px;
          letter-spacing: normal;
@@ -174,7 +174,7 @@
        }
        .old-price {
          font-size: 28px;
          font-weight: normal;
          line-height: 40px;
@@ -207,8 +207,8 @@
    }
    .trip-map {
      margin: 32px 0;
      width: 622px;
      margin-bottom: 32px;
      width: 100%;
      height: 270px;
      border-radius: 16px;
      background: #F2F3F7;
src/robotaxi/pages/order/detail/index.tsx
@@ -89,7 +89,10 @@
    if (status == 8) {
      return '待支付'
    }
    if (status >= 9) {
    if (status == 9) {
      return '待评价'
    }
    if (status == 10) {
      return '已完成'
    }
  }
@@ -151,7 +154,7 @@
    }
    if (status === 0) {
      setPolyline([]);
      setPolyline(routePath);
      return;
    }
    const polylineData = [{
@@ -166,11 +169,16 @@
  };
  useEffect(() => {
    if (!polyline.length) return
    let viewportPoints = polyline;
    if (polyline.length == 1) {
      viewportPoints = polyline[0].points;
    }
    const mapCtx = Taro.createMapContext('trip-map');
    Taro.nextTick(() => {
      mapCtx.includePoints({
        points: polyline,
        padding: [80, 80, 80, 80]
        points: viewportPoints,
        padding: [50, 30, 10, 30]
      });
    });
  }, [polyline, markers])
@@ -269,7 +277,7 @@
          orderData.status != 0 &&
          <View className='trip-top'>
            <View className='price-card'>
              <Text className='price'>{orderData.payMoney || '0.00'}元</Text>
              <Text className='price'>{orderData.payMoney || '0.00'}</Text>
              <Text className='unit'>元</Text>
              {orderData.orderMoney > orderData.payMoney && (
                <Text className='old-price'>{orderData.orderMoney || '-'}元</Text>
@@ -287,11 +295,10 @@
            id="trip-map"
            longitude={longitude}
            latitude={latitude}
            scale={14}
            markers={markers}
            polyline={polyline}
            showLocation={false}
            style={{ borderRadius: '16px' }}
            style={{ borderRadius: '16px', width: '100%' }}
          />
        </View>
        {/* 行程信息 */}
src/robotaxi/pages/order/index.tsx
@@ -93,7 +93,10 @@
    if (status == 8) {
      return '待支付'
    }
    if (status >= 9) {
    if (status == 9) {
      return '待评价'
    }
    if (status == 10) {
      return '已完成'
    }
  }
src/robotaxi/pages/select-map-site/index.tsx
@@ -341,7 +341,7 @@
      Taro.setStorageSync('endSite', selectedSite)
      Taro.reLaunch({
        url: '/robotaxi/pages/begin-route/index',
        url: `/robotaxi/pages/begin-route/index?skipPageFlag=1`,
      })
    }
  }
src/robotaxi/pages/site-search/index.tsx
@@ -280,7 +280,7 @@
      } catch { }
      Taro.setStorageSync('endSite', item)
      Taro.reLaunch({
        url: '/robotaxi/pages/begin-route/index',
        url: '/robotaxi/pages/begin-route/index?skipPageFlag=1',
      })
    }
  }
src/utils/websocket.ts
@@ -95,6 +95,9 @@
      this.socketTask.onMessage((res) => {
        try {
          const data = JSON.parse(res.data as string);
          if (data?.type !== 'pong') {
            console.log('收到 WebSocket 消息:', res.data);
          }
          const content = JSON.parse(data.content || '{}');
          this.handleIncomingMessage(data, content);
          if (data.type) {
@@ -143,6 +146,9 @@
    Taro.onSocketMessage((res) => {
      try {
        const data = JSON.parse(res.data as string);
        if (data?.type !== 'pong') {
          console.log('收到 WebSocket 消息:', res.data);
        }
        const content = JSON.parse(data.content || '{}');
        this.handleIncomingMessage(data, content);
        if (data.type) {