卢春龙
2026-08-14 8f6c52a3f68ea2622c53dc110d530d2a37df7722
Merge branch 'feature_v2.6.0_fuqj' into 'feature_2.6.0'

抽离站点名称组件、首页和站点选择页,指针雷达tooltip三者的衔接动效逻辑 、其他UI改动

See merge request robotaxi1/xp-robotaxi-miniprogram!263
2个文件已添加
31个文件已修改
1110 ■■■■■ 已修改文件
src/pages/robotaxi/home/components/map-pointer/index.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/constants.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-region-change.ts 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-site-state.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.less 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/components/site-route-card/index.less 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/components/site-route-card/index.tsx 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/door-trunk-control.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/trip-status-header.tsx 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/vehicle-info-card.tsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/cancel-popup.tsx 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/modify-destination-review.less 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/release-be-being.less 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/release-route.less 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/route-selection-panel.less 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/service-route-change.less 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/modify-destination-review.tsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/route-preference-popup/index.less 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-map-markers.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail/index.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/components/select-map-site-view.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/index.less 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/share-trip/components/share-trip-view.tsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/share-trip/hooks/use-map-markers.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/share-trip/hooks/use-view-delegates.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/share-trip/index.less 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/style/ui-variables.less 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/components/map-pointer/index.tsx
@@ -37,8 +37,9 @@
  return (
    <View className="pointer-box">
      {/* 雷达只在真正命中吸附、指针跳动到具体站点时才展示(由 bounce 驱动),不随真实请求态 nearbyLoading 提前出现 */}
      {
        props.nearbyLoading && props.isAndroid && (
        props.bounce && props.isAndroid && (
          <Image className="waiting-radar-img" src={props.waitingRadarSrc} webp />
        )
      }
@@ -64,7 +65,7 @@
        )
      }
      {
        props.nearbyLoading && !props.isAndroid && (
        props.bounce && !props.isAndroid && (
          <Image className="waiting-radar" src={props.waitingRadarSrc} />
        )
      }
src/pages/robotaxi/home/constants.ts
@@ -33,8 +33,8 @@
/** 首页订单状态文案映射。 */
export const HOME_ORDER_STATUS: Partial<Record<TripStatus, string>> = {
  [TripStatus.VehicleApproaching]: '车辆正在赶来',
  [TripStatus.VehicleArrivedPickup]: '车辆已到达上车点,请尽快前往',
  [TripStatus.VehicleArrivedPickup]: '车辆已到达上车点',
  [TripStatus.InService]: '正在前往目的地',
  [TripStatus.ArrivedDestination]: '车辆已到达目的地,请尽快结束行程',
  [TripStatus.ArrivedDestination]: '车辆已到达目的地',
  [TripStatus.TripFinished]: '行程已结束,请尽快支付'
};
src/pages/robotaxi/home/hooks/use-region-change.ts
@@ -5,6 +5,9 @@
  writeHomeStartSiteDisplayState
} from '../../../../robotaxi/hooks/use-home-start-site-display';
/** 指针跳动到附近站点的动效总时长:指针跳动(600ms)+ 雷达扫描确认(320ms),需与 index.less 中对应动画时长保持一致。 */
const POINTER_BOUNCE_SEQUENCE_DURATION_MS = 920;
type Deps = {
  mapState: any;
  siteState: any;
@@ -56,7 +59,6 @@
   */
  const queryPointerByCenter = async (lat: number, lng: number, options?: { allowAdsorb?: boolean }) => {
    const allowAdsorb = options?.allowAdsorb !== false;
    siteState.setBounce(true);
    siteState.setShowToolTip(false);
    try {
      const { isInside, newMarkers, sites } = await mapState.updateMapData(lat, lng, undefined, { commitMarkers: false });
@@ -68,38 +70,43 @@
        if (allowAdsorb) {
          siteState.setShowToolTip(true); // 运营区外恢复气泡提示
        }
        siteState.setBounce(false);
        return;
      }
      const nearestCandidates = sites.length > 0 ? sites : newMarkers;
      const { adsorbed, marker } = allowAdsorb
        ? siteState.tryAdsorb(lat, lng, nearestCandidates)
        ? siteState.tryAdsorb(lat, lng, nearestCandidates, { deferShowToolTip: true })
        : { adsorbed: false, marker: null }; // 仅浏览运营区域时不吸附最近点位
      commitQueriedMarkers(newMarkers, true, adsorbed);
      const finalLat = adsorbed && marker ? marker.latitude : lat; // 最终纬度
      const finalLng = adsorbed && marker ? marker.longitude : lng; // 最终经度
      if (allowAdsorb && adsorbed) {
        // 地图中心点(硬切换)与指针跳动动画在同一时刻触发,避免二者时序脱钩导致的突兀感
        siteState.setBounce(true);
      }
      if (allowAdsorb) {
        // 仅浏览运营区域时不回写地图中心,避免 Map 组件按旧 scale 重绘导致刚展开的运营区视野被收回
        mapState.setLatitude(finalLat);
        mapState.setLongitude(finalLng);
      }
      settleRegionResult({ lat: finalLat, lon: finalLng, isInside: true });
      setTimeout(async () => {
        siteState.setBounce(false);
        if (!adsorbed) {
          await siteState.resolveAddress(lat, lng);
        }
      if (adsorbed) {
        setTimeout(() => {
          siteState.setBounce(false);
          // 跳动 + 雷达动效播放完毕后才展示 tooltip,避免与前两段动效叠加显得突兀
          siteState.setShowToolTip(true);
        }, POINTER_BOUNCE_SEQUENCE_DURATION_MS);
      } else {
        await siteState.resolveAddress(lat, lng);
        if (!allowAdsorb) {
          siteState.setShowToolTip(false); // 查看运营区域期间隐藏气泡,避免遮挡围栏,拖动地图后自动恢复
        }
      }, 600);
      }
    } catch (error) {
      siteState.setBounce(false);
    }
  };
  const handleRegionEnd = (causedBy: string, wasReturningToOrigin: boolean) => {
    siteState.setBounce(true);
    const mapCtx = Taro.createMapContext('map');
    mapCtx.getScale({
      success: (res) => {
@@ -148,7 +155,7 @@
            return;
          }
          const nearestCandidates = sites.length > 0 ? sites : newMarkers;
          const { adsorbed, marker } = siteState.tryAdsorb(lat, lng, nearestCandidates);
          const { adsorbed, marker } = siteState.tryAdsorb(lat, lng, nearestCandidates, { deferShowToolTip: true });
          commitQueriedMarkers(newMarkers, true, adsorbed);
          const finalLat = adsorbed && marker ? marker.latitude : lat; // 最终纬度
          const finalLng = adsorbed && marker ? marker.longitude : lng; // 最终经度
@@ -156,20 +163,29 @@
            syncManualSelectedStartSite(marker);
          }
          settleRegionResult({ lat: finalLat, lon: finalLng, isInside: true });
          if ((causedBy === 'gesture' || causedBy === 'drag' || wasReturningToOrigin) && marker) {
          const isGestureSettle = causedBy === 'gesture' || causedBy === 'drag' || wasReturningToOrigin;
          const willPlayBounceSequence = isGestureSettle && adsorbed && !!marker;
          if (willPlayBounceSequence) {
            // 命中吸附且为手势/拖拽结算:地图中心点(硬切换)与指针跳动动画在同一时刻触发,避免二者时序脱钩导致的突兀感
            siteState.setBounce(true);
            mapCtx.moveToLocation({
              latitude: marker.latitude,
              longitude: marker.longitude
            });
            mapState.setLatitude(marker.latitude);
            mapState.setLongitude(marker.longitude);
            setTimeout(() => {
              siteState.setBounce(false);
              // 跳动 + 雷达动效播放完毕后才展示 tooltip,避免与前两段动效叠加显得突兀
              siteState.setShowToolTip(true);
            }, POINTER_BOUNCE_SEQUENCE_DURATION_MS);
          } else if (adsorbed) {
            // 未触发跳动动效(如吸附命中但非手势导致的静默刷新)时,立即展示 tooltip
            siteState.setShowToolTip(true);
          } else {
            // 未命中吸附:不播放跳动/雷达动效,立即回填地址,避免无意义的等待
            await siteState.resolveAddress(lat, lng);
          }
          setTimeout(async () => {
            siteState.setBounce(false);
            if (!adsorbed) {
              await siteState.resolveAddress(lat, lng);
            }
          }, 600);
        } catch (error) {
          siteState.setBounce(false);
        }
src/pages/robotaxi/home/hooks/use-site-state.ts
@@ -28,14 +28,17 @@
  /**
   * 统一吸附判断,命中阈值则更新吸附站点并显示提示。
   * @param options.deferShowToolTip 为 true 时不立即展示 tooltip,由调用方在合适的时机(如指针跳动动效结束后)自行调用 setShowToolTip(true)。
   */
  const tryAdsorb = (lat: number, lng: number, candidates: any[]) => {
  const tryAdsorb = (lat: number, lng: number, candidates: any[], options?: { deferShowToolTip?: boolean }) => {
    const threshold = adsorptionDistance || 1000;
    const { targetMarker, minDistance } = findNearestMarker(lat, lng, candidates);
    if (minDistance < threshold && targetMarker) {
      setIsAdsorbed(true);
      setCurrentSite(targetMarker);
      setShowToolTip(true);
      if (!options?.deferShowToolTip) {
        setShowToolTip(true);
      }
      Taro.setStorageSync('startSite', targetMarker);
      writeConfirmRideSiteSourceType('start', RECOMMENDED_CONFIRM_RIDE_SOURCE_TYPE);
      return { adsorbed: true, marker: targetMarker };
src/pages/robotaxi/home/index.less
@@ -123,7 +123,7 @@
  .order-status {
    position: relative;
    border-radius: 24px;
    background-color: #141E27;
    background-color: @black-text-main;
    background-image: url("https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/order-card-homepage-20260724.png");
    background-size: cover;
    box-sizing: border-box;
@@ -214,9 +214,11 @@
      font-size: 24px;
      font-weight: 500;
      letter-spacing: normal;
      color: rgba(0, 0, 0, 0.9);
      color: @black-text-main;
      border-radius: 200px;
      background: #ECF0F5;
      flex-shrink: 0;
      flex-grow: 0;
    }
  }
}
@@ -348,8 +350,8 @@
.coupon-message-toast {
  position: fixed;
  top: 370px;
  right: 144px;
  top: 393px;
  right: 124px;
  z-index: 1000012;
  max-width: 560px;
  min-height: 64px;
@@ -390,6 +392,8 @@
  .toolTip {
    position: relative;
    z-index: 10;
    animation: tooltip-fade-in 240ms ease-out;
    will-change: transform, opacity;
    .toolTip-bg {
      background-image: url("https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/tooltip-bg-20260715.png");
@@ -471,6 +475,8 @@
    width: 64px;
    height: 86px;
    margin-top: -54px;
    opacity: 1;
    transition: opacity 240ms ease-out;
    &.bounce {
      animation: pointer-bounce 600ms ease-in-out;
@@ -480,28 +486,32 @@
      opacity: 0;
    }
  }
}
.waiting-radar {
  position: fixed;
  width: 700px;
  height: 350px;
  top: 48%;
  left: 50%;
  transform: translate(-50%, -48%);
  z-index: 1;
  pointer-events: none;
}
  // 雷达以站点位置(.pointer-box 底边,即 translate(-50%,-100%) 后与地图中心点对齐的边)为圆心展开,
  // 用绝对定位直接锚定该边,不受内部 flex 子元素排列影响
  .waiting-radar {
    position: absolute;
    width: 700px;
    height: 350px;
    bottom: 0;
    left: 50%;
    transform: translate(-50%, 50%);
    z-index: 1;
    pointer-events: none;
    animation: radar-fade-in 200ms ease-out;
  }
.waiting-radar-img {
  position: fixed;
  width: 700px;
  height: 350px;
  top: 48%;
  left: 50%;
  transform: translate(-50%, -48%);
  z-index: 1;
  pointer-events: none;
  .waiting-radar-img {
    position: absolute;
    width: 700px;
    height: 350px;
    bottom: 0;
    left: 50%;
    transform: translate(-50%, 50%);
    z-index: 1;
    pointer-events: none;
    animation: radar-fade-in 200ms ease-out;
  }
}
@keyframes pointer-bounce {
@@ -522,6 +532,28 @@
  }
}
@keyframes tooltip-fade-in {
  0% {
    opacity: 0;
    transform: translateY(6px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes radar-fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
.home-container {
  display: flex;
  flex-direction: column;
src/robotaxi/components/site-route-card/index.less
New file
@@ -0,0 +1,86 @@
// SiteRouteCard 公共样式:抽离自 cancel-site-box / calling-car-site-box / share-site-box /
// modify-destination-review-select-site,与 release-be-being 的 route-card 结构对齐。
// 各业务方通过外层 className(如 cancel-site-box)叠加背景/内边距等容器级样式,
// 组件自身只负责 item/marker/dot/check/line/divider/text 的结构样式。
@import '../../style/ui-variables.less';
.site-route-card {
  .site-route-card-item {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 16px;
    &.site-route-card-item-first {
      padding-bottom: 80px;
    }
    // 虚线连接线:以 item 盒子为基准,用 top/bottom 锚定首尾 marker 中心,
    // 文字换行导致 item 变高时自动拉伸,无需固定高度。
    .site-route-card-item-line {
      position: absolute;
      left: 14px;
      top: 40px;
      bottom: -8px;
      transform: translateX(-50%);
      width: 0;
      border-left: 2px dashed #ccd0d7;
    }
    // 两项之间的水平分割线:与文字列左对齐,居中于行间距内。
    .site-route-card-item-divider {
      position: absolute;
      left: 48px;
      right: 0;
      top: 88px;
      height: 0;
      border-top: 1px solid rgba(204, 208, 215, 0.25);
    }
    .site-route-card-item-marker {
      position: relative;
      flex-shrink: 0;
      width: 28px;
      height: 44px;
      display: flex;
      align-items: center;
      justify-content: center;
      .site-route-card-item-dot {
        width: 16px;
        height: 16px;
        border-radius: 50%;
        background: @black-text-main;
        &.site-route-card-item-dot-first {
          background: @gold-light;
        }
      }
      .site-route-card-item-check {
        width: 28px;
        height: 28px;
      }
    }
    .site-route-card-item-row {
      flex: 1;
      min-width: 0;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .site-route-card-item-text {
      flex: 1;
      min-width: 0;
      font-size: 32px;
      font-weight: 500;
      line-height: 48px;
      letter-spacing: normal;
      color: @black-text-main;
      word-break: break-all;
    }
  }
}
src/robotaxi/components/site-route-card/index.tsx
New file
@@ -0,0 +1,71 @@
import { View, Image } from '@tarojs/components';
import './index.less';
export type SiteRouteCardItem = {
  /** 站点名称 */
  name: React.ReactNode;
  /** 名称文案的额外 className,用于置灰等场景 */
  nameClassName?: string;
  /** 右侧扩展内容(如时间文案),仅在需要时传入 */
  extra?: React.ReactNode;
  /** 该项点击事件(如终点可点击编辑) */
  onClick?: () => void;
};
type Props = {
  /** 站点列表,通常为 [起点, 终点] 两项 */
  sites: SiteRouteCardItem[];
  /** marker 类型:dot 为纯圆点,check 为图标(需配合 checkIconUrls 传入图标地址) */
  markerType?: 'dot' | 'check';
  /** markerType 为 check 时,按 sites 顺序传入对应图标地址 */
  checkIconUrls?: string[];
  /** 最外层 className,供各业务方沿用自身既有命名做样式覆盖 */
  className?: string;
};
/**
 * 站点路线卡片:统一的起终点两项展示结构(marker + 虚线连接线 + 分割线 + 文本)。
 * 抽离自 cancel-site-box / calling-car-site-box / share-site-box / modify-destination-review-select-site
 * 与 release-be-being 的 route-card 结构对齐。
 */
export default function SiteRouteCard({
  sites,
  markerType = 'dot',
  checkIconUrls = [],
  className = ''
}: Props) {
  return (
    <View className={`site-route-card ${className}`.trim()}>
      {sites.map((site, index) => {
        const isFirst = index === 0;
        return (
          <View
            key={index}
            className={`site-route-card-item${isFirst ? ' site-route-card-item-first' : ''}`}
            onClick={site.onClick}
          >
            {isFirst && <View className='site-route-card-item-line'></View>}
            {isFirst && <View className='site-route-card-item-divider'></View>}
            <View className='site-route-card-item-marker'>
              {markerType === 'check' ? (
                <Image className='site-route-card-item-check' src={checkIconUrls[index]} />
              ) : (
                <View
                  className={`site-route-card-item-dot${
                    isFirst ? ' site-route-card-item-dot-first' : ''
                  }`}
                ></View>
              )}
            </View>
            <View className='site-route-card-item-row'>
              <View className={`site-route-card-item-text ${site.nameClassName || ''}`.trim()}>
                {site.name}
              </View>
              {site.extra}
            </View>
          </View>
        );
      })}
    </View>
  );
}
src/robotaxi/pages/begin-route/components/calling-car-modules/door-trunk-control.tsx
@@ -306,7 +306,7 @@
                </View>
                <View className={`car-card-arrive-item ${lockStatus && 'close-btn-item'} ${loadingControlType ? 'trip-action-bar-item-disabled' : ''}`.trim()} onClick={() => handleUnlock('door', onDoorUnlockSuccess)}>
                    <View className="car-card-arrive-item-text">车锁</View>
                    <View className="car-card-arrive-item-status">{lockStatus ? '车门已解锁' : '点击打开'}</View>
                    <View className="car-card-arrive-item-status">{lockStatus ? '车门已解锁' : '点击解锁'}</View>
                    {renderControlIcon('door', lockStatus ? LOCK_OPEN_ICON_URL : LOCK_CLOSE_ICON_URL, 'car-card-arrive-item-img')}
                </View>
            </View>
src/robotaxi/pages/begin-route/components/calling-car-modules/trip-status-header.tsx
@@ -3,6 +3,7 @@
import { useEffect, useState } from 'react';
import { queryOrderQueueStatus } from '../../../../api/begin-route';
import { TripStatus } from '../../../../constants/trip-status';
import SiteRouteCard from '../../../../components/site-route-card';
type QueueStatus = {
  currentPosition: number;
@@ -141,9 +142,9 @@
  const endSiteName = cachedEndSite?.name || cachedEndSite?.address || '-';
  // 状态栏右侧的距离/时间文案
  const distanceText = routeDistance ? `${routeDistance.value}${routeDistance.unit === 'm' ? 'M' : 'KM'}` : '';
  const approachingTimeText = `${normalizedDurationDisplay.value}MIN`;
  const inServiceTimeText = `${normalizedDurationDisplay.value}MIN`;
  const distanceText = routeDistance ? `${routeDistance.value}${routeDistance.unit === 'm' ? '米' : '公里'}` : '';
  const approachingTimeText = `${normalizedDurationDisplay.value}分钟`;
  const inServiceTimeText = `${normalizedDurationDisplay.value}分钟`;
  return (
    <>
@@ -174,18 +175,18 @@
              取消行程
            </View>
          </View>
          <View className='calling-car-site-box'>
            <View className='calling-car-site-box-icon'>
              <Image className='calling-car-site-check'src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/route-state-icon-golden-20260702.png' ></Image>
              <Image className='dotted-line' src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/dotted-line.png' />
              <Image className='calling-car-site-check' src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/route-state-icon-black-20260702.png'></Image>
            </View>
            <View className='calling-car-site-box-content'>
              <View className='calling-car-site-box-content-name'>{startSiteName}</View>
              <View className='calling-car-site-box-divider'></View>
              <View className='calling-car-site-box-content-name'>{endSiteName}</View>
            </View>
          </View>
          <SiteRouteCard
            className='calling-car-site-box'
            markerType='check'
            checkIconUrls={[
              'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/route-state-icon-golden-20260702.png',
              'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/route-state-icon-black-20260702.png'
            ]}
            sites={[
              { name: startSiteName },
              { name: endSiteName }
            ]}
          />
        </>
      )}
@@ -204,7 +205,9 @@
      {/* 车辆到达上车点:直接展示图片 */}
      {tripStatus === TripStatus.VehicleArrivedPickup && (
        <View className='calling-car-status-box calling-car-arrived-img'>
          <View className='calling-car-status-box-left'>车辆已到达上车点,倒计时结束将自动取消订单</View>
          <View className='calling-car-status-box-left'>
            {doorUnlockedInCurrentPage ? '车门已开启,请尽快上车开启行程' : '车辆已到达上车点,倒计时结束将自动取消订单'}
          </View>
        </View>
      )}
src/robotaxi/pages/begin-route/components/calling-car-modules/vehicle-info-card.tsx
@@ -1,6 +1,9 @@
import { View } from "@tarojs/components";
import Taro from "@tarojs/taro";
import { View, Image } from "@tarojs/components";
import { TripStatus } from "../../../../constants/trip-status";
type Props = {
    tripStatus?: TripStatus;
    vehicleInfo?: {
        vehiclePictures?: string;
        vehicleCode?: string;
@@ -9,20 +12,33 @@
    };
};
export default function VehicleInfoCard({ vehicleInfo }: Props) {
export default function VehicleInfoCard({ tripStatus, vehicleInfo }: Props) {
    // 行程中(前往目的地):车牌信息下方展示目的地图标 + 站点名称
    const showDestination = tripStatus === TripStatus.InService;
    const cachedEndSite = showDestination ? (Taro.getStorageSync('endSite') || {}) : {};
    const endSiteName = cachedEndSite?.name || cachedEndSite?.address || '-';
    // 展示车辆基础信息:左侧车牌·车身颜色,右侧车型
    return (
        <View className="car-card-detail">
            <View className="car-card-detail-plate">
                <View className="car-card-detail-num">{vehicleInfo?.plateNumber}</View>
                {vehicleInfo?.vehicleColor && (
                    <>
                        <View className="car-card-detail-dot"></View>
                        <View className="car-card-detail-color-text">{vehicleInfo?.vehicleColor}</View>
                    </>
                )}
        <View className="car-card-detail-wrap">
            <View className="car-card-detail">
                <View className="car-card-detail-plate">
                    <View className="car-card-detail-num">{vehicleInfo?.plateNumber}</View>
                    {vehicleInfo?.vehicleColor && (
                        <>
                            <View className="car-card-detail-dot"></View>
                            <View className="car-card-detail-color-text">{vehicleInfo?.vehicleColor}</View>
                        </>
                    )}
                </View>
                <View className="car-card-detail-model">小鹏GX</View>
            </View>
            <View className="car-card-detail-model">小鹏GX</View>
            {showDestination && (
                <View className='car-card-detail-destination'>
                    <Image className='car-card-detail-destination-icon' src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/destination-icon-20260812.png' />
                    <View className='car-card-detail-destination-name'>{endSiteName}</View>
                </View>
            )}
        </View>
    );
}
src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -337,7 +337,7 @@
            }
            {
              [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService, TripStatus.PaymentSuccess].includes(tripStatus) &&
              <VehicleInfoCard vehicleInfo={vehicleInfo} />
              <VehicleInfoCard tripStatus={tripStatus} vehicleInfo={vehicleInfo} />
            }
            <TripStatusHeader
              orderId={orderId}
src/robotaxi/pages/begin-route/components/cancel-popup.tsx
@@ -2,6 +2,7 @@
import { View, Text, Image } from '@tarojs/components';
import { useEffect, useState } from 'react';
import './css-folder/cancel-popup.less'
import SiteRouteCard from '../../../components/site-route-card';
import { formatRemainingDurationDisplay } from '../../../utils/format';
import {
    clearOrderTripStorage
@@ -117,33 +118,28 @@
                    }
                </View>
                {
                    [CancelType.OrderCanceled, CancelType.NoAvailableVehicle].includes(cancelType) ?
                        <View>
                            <View className='cancel-site-box'>
                                <View className='cancel-site-box-icon'>
                                    <View className='cancel-site-box-icon-start'></View>
                                    <Image src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/dotted-line.png" />
                                    <View className='cancel-site-box-icon-end'></View>
                                </View>
                                <View className='cancel-site-box-content'>
                                    <View className='cancel-site-box-content-name'>{startSiteName}</View>
                                    <View className='cancel-site-box-divider'></View>
                                    <View className='cancel-site-box-content-name'>{endSiteName}</View>
                                </View>
                            </View>
                            <View className='cancel-btn-box'>
                                <View className='cancel-btn-again' onClick={onConfirm}>再来一单</View>
                            </View>
                  [CancelType.OrderCanceled, CancelType.NoAvailableVehicle].includes(cancelType) ?
                    <View>
                      <SiteRouteCard
                        className='cancel-site-box'
                        sites={[
                          { name: startSiteName },
                          { name: endSiteName }
                        ]}
                      />
                      <View className='cancel-btn-box'>
                          <View className='cancel-btn-again' onClick={onConfirm}>再来一单</View>
                      </View>
                    </View>
                    :
                    <>
                        {/* 占位动画:优先 webp,加载失败回退 png */}
                        <Image className='cancel-animation' src={animationSrc} onError={handleAnimationError} />
                        <View className='cancel-btn-box'>
                            <View  className=' cancel-btn-cancel' onClick={onConfirm}>确认取消</View>
                            <View className='cancel-btn-again wait-btn' onClick={onClose}>再等等</View>
                        </View>
                        :
                        <>
                            {/* 占位动画:优先 webp,加载失败回退 png */}
                            <Image className='cancel-animation' src={animationSrc} onError={handleAnimationError} />
                            <View className='cancel-btn-box'>
                                <View  className=' cancel-btn-cancel' onClick={onConfirm}>确认取消</View>
                                <View className='cancel-btn-again wait-btn' onClick={onClose}>再等等</View>
                            </View>
                        </>
                    </>
                }
            </View>
        </View>
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
@@ -34,7 +34,7 @@
  }
  .calling-car-content-box {
    position: relative;
    padding: 48px 48px 82px 48px;
    padding: 48px 48px 66px 48px;
    background: @gary-theme;
    border-radius: 60px 60px 0 0;
  }
@@ -59,7 +59,7 @@
  .calling-car-vehicle-pic {
    position: absolute;
    top: -4px;
    right: 116px;
    right: 80px;
    width: 342px;
    height: 294px;
    z-index: 1;
@@ -323,11 +323,30 @@
  .fare-box-card {
    padding-bottom: 0;
  }
  .car-card-detail-wrap {
    margin-bottom: 32px;
  }
  .car-card-detail {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 32px;
  }
  .car-card-detail-destination {
    display: flex;
    align-items: center;
    margin-top: 12px;
    .car-card-detail-destination-icon {
      width: 28px;
      height: 28px;
      flex-shrink: 0;
    }
    .car-card-detail-destination-name {
      margin-left: 8px;
      font-size: 24px;
      color: @black-text-main;
      max-width: 620px;
      word-break: break-all;
    }
  }
  .car-card-detail-plate {
    display: flex;
@@ -752,8 +771,7 @@
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 8px;
    padding: 32px 8px;
    padding: 22px 8px 0 8px;
  }
  .trip-action-bar-item {
    display: flex;
@@ -769,6 +787,7 @@
    width: 28px;
    height: 28px;
    margin-right: 8px;
    margin-top: 2px;
  }
  .trip-action-bar-item-destination {
    .trip-action-bar-item-icon {
@@ -779,7 +798,7 @@
  .trip-action-bar-item-text {
    flex-shrink: 0;
    font-size: 24px;
    line-height: 24px;
    line-height: 56px;
    color: @black-text-main;
  }
  .trip-action-bar-share-btn {
@@ -1064,9 +1083,6 @@
      margin: 0 12px;
      color: rgba(20, 30, 39, 0.2);
    }
    .calling-car-site-box-content-name {
      font-weight: 500;
    }
  }
  .calling-car-cancel-btn {
    flex-shrink: 0;
@@ -1086,36 +1102,6 @@
    border-radius: 40px;
    background: rgba(204, 208, 215, 0.25);
    padding: 32px;
    display: flex;
    gap: 16px;
    height: fit-content;
    .calling-car-site-box-icon {
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      .calling-car-site-check {
        width: 32px;
        height: 32px;
      }
      .dotted-line {
        flex: 1;
        width: 2px;
        margin: 6px 0;
        height: fit-content;
      }
    }
    .calling-car-site-box-content {
      flex: 1;
      font-size: 28px;
      line-height: 44px;
      letter-spacing: normal;
      color: @black-text-main;
      .calling-car-site-box-divider {
        margin: 40px 24px 40px 0px;
        border-top: 1px solid rgba(20, 30, 39, 0.08);
      }
    }
  }
  .calling-car-status-box {
    position: relative;
src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less
@@ -63,57 +63,9 @@
        .cancel-site-box {
            margin-top: 48px;
            border-radius: 40px;
            padding: 48px 40px;
            background: rgba(204, 208, 215, 0.25);
            padding: 48px 32px 42px 40px;
            display: flex;
            gap: 16px;
            height: fit-content;
            .cancel-site-box-icon {
                padding: 16px 0;
                flex-shrink: 0;
                display: flex;
                flex-direction: column;
                align-items: center;
                .cancel-site-box-icon-start {
                    width: 16px;
                    height: 16px;
                    background: #DFB369;
                    border-radius: 50%;
                }
                .cancel-site-box-icon-end {
                    width: 16px;
                    height: 16px;
                    background: #141E27;
                    border-radius: 50%;
                }
                image {
                    flex: 1;
                    width: 2px;
                    margin: 7px 0;
                    height: fit-content;
                }
            }
            .cancel-site-box-content {
                flex: 1;
                font-size: 32px;
                line-height: 48px;
                letter-spacing: normal;
                color: #141E27;
                font-weight: 500;
                .cancel-site-box-divider {
                  margin: 34px 32px 40px 0px;
                  border-top: 1px solid rgba(204, 208, 215, 0.25);
                }
                .cancel-site-box-content-name {
                  font-weight: 500;
                }
            }
            border-radius: 40px;
        }
        .cancel-animation {
src/robotaxi/pages/begin-route/components/css-folder/modify-destination-review.less
@@ -59,17 +59,22 @@
    margin-bottom: 32px;
  }
  // 站点样式参考 cancel-popup 的 cancel-site-box:灰底盒子 + 左侧起终点圆点虚线列
  // 站点样式参考 cancel-popup 的 cancel-site-box(route-card 风格),已抽离为 SiteRouteCard 公共组件,
  // 此处只保留容器级样式(背景/圆角/内边距)
  .modify-destination-review-select-site {
    position: relative;
    z-index: 1;
    display: flex;
    gap: 16px;
    padding: 40px 32px 40px 48px;
    border-radius: 40px;
    background: rgba(204, 208, 215, 0.25);
    box-sizing: border-box;
    height: fit-content;
    .site-route-card-item-first {
      .site-route-card-item-row {
        .site-route-card-item-text {
          color: @black-text-04;
        }
      }
    }
  }
  &-trip-card {
@@ -80,59 +85,6 @@
    box-sizing: border-box;
    border: 2px solid #ffffff;
    box-shadow: 0 20px 40px 0 rgba(87, 99, 147, 0.08);
  }
  .modify-destination-review-select-site-start {
    color: @black-text-04;
  }
  .modify-destination-review-select-site-icon {
    padding: 16px 0;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .modify-destination-review-select-site-icon-start {
    width: 16px;
    height: 16px;
    background: #DFB369;
    border-radius: 50%;
  }
  .modify-destination-review-select-site-icon-end {
    width: 16px;
    height: 16px;
    background: @black-text-main;
    border-radius: 50%;
  }
  .modify-destination-review-select-site-icon image {
    flex: 1;
    width: 2px;
    margin: 7px 0;
    height: fit-content;
  }
  .modify-destination-review-select-site-content {
    flex: 1;
    min-width: 0;
    font-size: 32px;
    line-height: 48px;
    letter-spacing: normal;
    color: @black-text-main;
  }
  .modify-destination-review-select-site-start,
  .modify-destination-review-select-site-end {
    font-weight: 500;
    word-break: break-all;
  }
  .modify-destination-review-select-site-divider {
    margin: 34px 32px 40px 0px;
    border-top: 1px solid rgba(204, 208, 215, 0.25);
  }
  &-footer {
src/robotaxi/pages/begin-route/components/css-folder/release-be-being.less
@@ -5,7 +5,7 @@
  background: @gary-theme;
  border-radius: 60px 60px 0px 0px;
  overflow: hidden;
  padding-top: 84px;
  padding-top: 94px;
  .release-be-being-content {
    position: relative;
    z-index: 1;
src/robotaxi/pages/begin-route/components/css-folder/release-route.less
@@ -70,9 +70,9 @@
            .route-select-content-item {
                flex: 1;
                padding: 16px 0 24px 24px;
                border-radius: 16px;
                background: #F5F6FA;
                border: 1px solid #F2F3F7;
                border-radius: 20px;
                background: @gary-theme;
                .route-select-content-item-title {
                    font-size: 20px;
@@ -115,28 +115,11 @@
            }
            .active-item {
                background: linear-gradient(97deg, rgba(255, 209, 129, 0.2) 0%, rgba(223, 179, 105, 0.2) 103%) !important;
                box-sizing: border-box;
                background: rgba(223, 179, 105, 0.2);
                position: relative;
                .route-select-content-item-title {
                    color: @gold-text;
                }
                &::after {
                    content: "";
                    position: absolute;
                    top: 0;
                    left: 0;
                    right: 0;
                    bottom: 0;
                    border-radius: 16px;
                    padding: 1px;
                    background: linear-gradient(180deg, @gold-light -10%, rgba(223, 179, 105, 0.0469) 108%, rgba(0, 0, 0, 0) 108%);
                    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
                    -webkit-mask-composite: xor;
                    mask-composite: exclude;
                    pointer-events: none;
                }
            }
        }
@@ -362,6 +345,7 @@
            .discount-amount-desc {
                margin-left: 8px;
                font-size: 24px;
                font-weight: 500;
                line-height: 40px;
                letter-spacing: normal;
                color: @gold-text;
src/robotaxi/pages/begin-route/components/css-folder/route-selection-panel.less
@@ -48,14 +48,13 @@
    .route-select-content-item {
      flex: 1;
      padding: 16px 0 24px 24px;
      border-radius: 16px;
      border-radius: 20px;
      background: #F5F6FA;
      border: 1px solid #F2F3F7;
      .route-select-content-item-title {
        font-size: 20px;
        line-height: 20px;
        color: rgba(0, 0, 0, 0.9);
        color: @black-text-main;
      }
      .route-select-content-item-time {
@@ -63,7 +62,7 @@
        font-size: 28px;
        font-weight: 500;
        line-height: 40px;
        color: rgba(0, 0, 0, 0.9);
        color: @black-text-main;
      }
      .route-select-content-item-distance {
@@ -90,27 +89,11 @@
    }
    .active-item {
      background: linear-gradient(97deg, rgba(255, 209, 129, 0.2) 0%, rgba(223, 179, 105, 0.2) 103%) !important;
      background: rgba(223, 179, 105, 0.2);
      box-sizing: border-box;
      position: relative;
      .route-select-content-item-title {
        color: @gold-text;
      }
      &::after {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        border-radius: 16px;
        padding: 1px;
        background: linear-gradient(180deg, @gold-light -10%, rgba(223, 179, 105, 0.0469) 108%, rgba(0, 0, 0, 0) 108%);
        -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
        -webkit-mask-composite: xor;
        mask-composite: exclude;
        pointer-events: none;
      }
    }
  }
src/robotaxi/pages/begin-route/components/css-folder/service-route-change.less
@@ -1,8 +1,8 @@
@import '../../../../style/ui-variables.less';
.service-route-change-container {
    background: #FFFFFF;
    border-radius: 40px 40px 0 0;
    padding: 40px 49px 68px 49px;
    background: @gary-theme;
    border-radius: 60px 60px 0 0;
    padding: 40px 40px 80px 40px;
    .service-route-select-container {
        display: flex;
@@ -11,8 +11,8 @@
        .service-route-select-title {
            font-size: 28px;
            font-weight: 500;
            line-height: 40px;
            color: rgba(0, 0, 0, 0.9);
            line-height: 52px;
            color: @black-text-main;
        }
        .service-route-select-preference {
@@ -20,11 +20,10 @@
            align-items: center;
            .service-route-select-preference-text {
                font-size: 24px;
                font-size: 28px;
                font-weight: 500;
                line-height: 40px;
                color: @gold-text;
                font-weight: 500;
                line-height: 52px;
                color: @dark-gold-text;
            }
            image {
@@ -41,28 +40,27 @@
    .service-route-select-content {
        display: flex;
        justify-content: space-between;
        gap: 10px;
        margin-top: 16px;
        gap: 14px;
        margin-top: 34px;
        .service-route-select-content-item {
            flex: 1;
            padding: 16px 24px 30px 24px;
            border-radius: 16px;
            border-radius: 20px;
            background: #F5F6FA;
            border: 1px solid #F2F3F7;
            .service-route-select-content-item-title {
                font-size: 20px;
                line-height: 20px;
                color: rgba(0, 0, 0, 0.9);
                color: @black-text-04;
            }
            .service-route-select-content-item-time {
                margin-top: 10px;
                margin-top: 18px;
                font-size: 28px;
                font-weight: 500;
                line-height: 40px;
                color: rgba(0, 0, 0, 0.9);
                color: @black-text-main;
            }
            .service-route-select-content-item-distance {
@@ -72,7 +70,7 @@
                margin-top: 12px;
                font-size: 20px;
                line-height: 20px;
                color: rgba(0, 0, 0, 0.9);
                color: @black-text-main;
                .service-traffic-signal-item {
                    display: flex;
@@ -88,36 +86,20 @@
            }
            .service-route-select-content-item-estimate {
                margin-top: 34px;
                margin-top: 38px;
                font-size: 20px;
                line-height: 20px;
                color: #F58900;
                color: @gold-text;
            }
        }
        .active {
            background: linear-gradient(97deg, rgba(255, 209, 129, 0.2) 0%, rgba(223, 179, 105, 0.2) 103%) !important;
            background: rgba(223, 179, 105, 0.2);
            box-sizing: border-box;
            position: relative;
            .service-route-select-content-item-title {
                color: @gold-text;
            }
            &::after {
                content: "";
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                border-radius: 16px;
                padding: 1px;
                background: linear-gradient(180deg, @gold-light -10%, rgba(223, 179, 105, 0.0469) 108%, rgba(0, 0, 0, 0) 108%);
                -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
                -webkit-mask-composite: xor;
                mask-composite: exclude;
                pointer-events: none;
            }
        }
    }
@@ -126,30 +108,36 @@
        display: flex;
        justify-content: space-between;
        gap: 22px;
        margin-top: 48px;
        margin-top: 58px;
        font-size: 32px;
        font-weight: 500;
        text-align: center;
        .service-route-select-footer-item-cancel {
            flex: 1;
            color: #141E27;
            border-radius: 1000px;
            box-sizing: border-box;
            border: 4px solid #141E27;
            line-height: 104px;
            height: 104px;
          display: flex;
          align-items: center;
          justify-content: center;
          flex: 1;
          color: @black-text-main;
          border-radius: 1000px;
          box-sizing: border-box;
          border: 4px solid @black-text-main;
          line-height: 104px;
          height: 102px;
        }
        .service-route-select-footer-item-confirm {
            flex: 1;
            color: #FFFFFF;
            border-radius: 1000px;
            line-height: 104px;
            border: 4px solid #141E27;
            box-sizing: border-box;
            height: 104px;
            background: #141E27;
          display: flex;
          align-items: center;
          justify-content: center;
          flex: 1;
          color: #FFFFFF;
          border-radius: 1000px;
          line-height: 104px;
          border: 4px solid @black-text-main;
          box-sizing: border-box;
          height: 102px;
          background: @black-text-main;
        }
    }
}
}
src/robotaxi/pages/begin-route/components/modify-destination-review.tsx
@@ -5,6 +5,7 @@
import { getUpdateAddressNum } from "../../../api/begin-route";
import "./css-folder/modify-destination-review.less";
import RouteSelectionPanel from "./route-selection-panel";
import SiteRouteCard from "../../../components/site-route-card";
type Props = {
  orderId?: string;
@@ -361,25 +362,16 @@
                {modifyDestinationTipText}
              </View>
            ) : null}
            <View className="modify-destination-review-select-site">
              <View className="modify-destination-review-select-site-icon">
                <View className="modify-destination-review-select-site-icon-start"></View>
                <Image src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/dotted-line.png" />
                <View className="modify-destination-review-select-site-icon-end"></View>
              </View>
              <View className="modify-destination-review-select-site-content">
                <View className="modify-destination-review-select-site-start">
                  {startSite?.name || "-"}
                </View>
                <View className="modify-destination-review-select-site-divider"></View>
                <View
                  className="modify-destination-review-select-site-end"
                  onClick={() => onEditDestination?.()}
                >
                  {nextEndSite?.name || "-"}
                </View>
              </View>
            </View>
            <SiteRouteCard
              className="modify-destination-review-select-site"
              sites={[
                { name: startSite?.name || "-" },
                {
                  name: nextEndSite?.name || "-",
                  onClick: () => onEditDestination?.()
                }
              ]}
            />
            <View className="modify-destination-review-footer">
              <View className="modify-destination-review-fare">
                {isOperateFree ? (
src/robotaxi/pages/begin-route/components/route-preference-popup/index.less
@@ -28,12 +28,13 @@
    .popup-content {
        position: relative;
        z-index: 103;
        background: #FFFFFF;
        background: @gary-theme;
        border-radius: 40px 40px 0 0;
        width: 100%;
        display: flex;
        flex-direction: column;
        transform: translateY(100%);
        min-height: 672px;
    }
    .popup-header {
@@ -48,7 +49,7 @@
            font-size: 32px;
            font-weight: 500;
            line-height: 32px;
            color: rgba(0, 0, 0, 0.9);
            color: @black-text-main;
        }
        .close-icon {
src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
@@ -11,7 +11,7 @@
  "https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/change-site-up.png"; // 行程中修改目的地时的上车点图标
// 接驾态(前往上车点)上车点三角图标
const PICKUP_APPROACHING_ICON_URL =
  "https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/up-triangle-20260722.png";
  "https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/up-triangle-20260812.png";
// 送驾态(前往目的地)下车点三角图标
const DROPOFF_INSERVICE_ICON_URL =
  "https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/down-triangle-20260722.png";
@@ -218,8 +218,11 @@
      !shouldUseChangePickupIcon;
    // 修改目的地预览:起点(上车点)统一用三角图标 up-triangle + 名称显示在图标下方
    const isPickupModifyPreview = deps.isModifyDestinationReviewVisible;
    // 发布路线态:上车点同样展示三角图标
    const isPickupPublishRoute =
      deps.tripStatus === TripStatus.PublishRoute && !shouldUseChangePickupIcon;
    // 接驾态路线切换卡片展示中:地图状态被切换为 PublishRoute 展示态,但上车点名称仍需按三角图标态展示
    const isPickupTriangle = isPickupApproaching || isPickupArrived || isPickupSettled || isPickupModifyPreview || deps.showTrip1StyleEndpoint;
    const isPickupTriangle = isPickupApproaching || isPickupArrived || isPickupSettled || isPickupModifyPreview || isPickupPublishRoute || deps.showTrip1StyleEndpoint;
    // 名称在图标下方:接驾态 11px,到达/待支付/完成态 22px
    const pickupNameLabelConfig = isPickupApproaching
      ? SITE_NAME_LABEL_BASE
@@ -312,12 +315,14 @@
      : dropoffSite.name || "目的地";
    // 送驾态(前往目的地):下车点改用三角图标,名称移到图标下方,浮窗展示 end-site 图片;
    // 待支付/完成态:下车点同样三角图标 + end-site 浮窗,名称移到图标左侧。
    // 发布路线态:下车点同样展示三角图标。
    const isDropoffInService = deps.tripStatus === TripStatus.InService;
    const isDropoffSettled = [
      TripStatus.TripFinished,
      TripStatus.PaymentSuccess
    ].includes(deps.tripStatus);
    const isDropoffTriangle = isDropoffInService || isDropoffSettled;
    const isDropoffPublishRoute = deps.tripStatus === TripStatus.PublishRoute;
    const isDropoffTriangle = isDropoffInService || isDropoffSettled || isDropoffPublishRoute;
    const useDropoffRichCallout = isDropoffTriangle; // cover-view 富浮窗
    const getCustomCalloutAnchorY = () => {
      if (!deps.isAndroid && isDropoffSettled) {
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -263,9 +263,7 @@
              {routeDistance?.value ?? 0}
            </View>
            <View style={{ flexShrink: 0 }}>
              {isPickup
                ? routeDistance?.unit === "m" ? "M" : "KM"
                : routeDistance?.unit === "m" ? "米" : "公里"}
              {routeDistance?.unit === "m" ? "米" : "公里"}
            </View>
            {!isPickup && (
              <>
@@ -405,11 +403,19 @@
        .join("|"),
    [deps.markers]
  );
  /**
   * 触发一次 customCallout 回流重绘:翻转 borderRadius 兜底原生地图对该 cover-view 的重绘。
   */
  const toggleCalloutRepaint = useCallback(() => {
    setCalloutRepaintStyle((prev) => ({ borderRadius: prev.borderRadius === '0px' ? '0.5px' : '0px' }));
  }, []);
  useEffect(() => {
    // 仅 Android 保留一次性重绘兜底;iOS 完全不做 borderRadius 重绘(避免闪动)。
    // 依赖里额外加入 isModifyDestinationReviewVisible / isPickupRouteChangeVisible:
    // 这两个状态切换时 customCallout 内容会切成目的地黑色浮窗(endpoint-summary),
    // 但 marker 的 id/name(markersRepaintSignature)和 tripStatus 通常不变,
    // 若不监听会导致本次内容切换错过重绘时机,出现"气泡背景已渲染但内部文字未绘制"。
    if (!deps.isAndroid) {
      return undefined;
    }
@@ -426,7 +432,14 @@
        calloutRepaintTimerRef.current = null;
      }
    };
  }, [markersRepaintSignature, deps.tripStatus, deps.isAndroid, toggleCalloutRepaint]);
  }, [
    markersRepaintSignature,
    deps.tripStatus,
    deps.isAndroid,
    deps.isModifyDestinationReviewVisible,
    deps.isPickupRouteChangeVisible,
    toggleCalloutRepaint
  ]);
  /**
@@ -448,6 +461,27 @@
    toggleCalloutRepaint();
    return undefined;
  }, [deps.elapsedSeconds, deps.tripStatus, deps.isAndroid, toggleCalloutRepaint]);
  /**
   * Android 接驾到达态(VehicleArrivedPickup)上车点倒计时气泡(site-countdown-callout)
   * 同样存在偶现不绘制、倒计时文案不刷新的问题:pickupCountdownSeconds 按秒递减更新,
   * 但此前只有 Publishing/CallingCar 的 elapsedSeconds 变化会触发每秒重绘兜底,
   * VehicleArrivedPickup 未被覆盖,导致该气泡文案长时间卡住不变,直到其他依赖
   * (markers/tripStatus 等)触发的单次重绘才一次性跳变。这里单独为该状态按秒重绘兜底。
   */
  useEffect(() => {
    if (!deps.isAndroid) {
      return undefined;
    }
    if (deps.tripStatus !== TripStatus.VehicleArrivedPickup) {
      return undefined;
    }
    if (pickupCountdownSeconds === null || pickupCountdownSeconds === undefined) {
      return undefined;
    }
    toggleCalloutRepaint();
    return undefined;
  }, [pickupCountdownSeconds, deps.tripStatus, deps.isAndroid, toggleCalloutRepaint]);
@@ -730,6 +764,7 @@
            {item.id === 1 &&
              deps.tripStatus === TripStatus.VehicleApproaching &&
              !deps.isModifyDestinationReviewVisible &&
              !deps.isPickupRouteChangeVisible &&
              !item.hideNameCallout && (
                <>
                <CoverView
@@ -741,7 +776,7 @@
                    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
                    限停{deps.startSite?.time}分钟
                  </CoverView>
                </CoverView>
                <CoverImage
@@ -753,6 +788,7 @@
            {item.id === 1 &&
              deps.tripStatus === TripStatus.VehicleArrivedPickup &&
              !deps.isModifyDestinationReviewVisible &&
              !deps.doorUnlockedInCurrentPage &&
              !item.hideNameCallout &&
              Number(pickupCountdownSeconds) > 0 && (
                <CoverView
@@ -771,7 +807,8 @@
            {item.id === 1 &&
              ([TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(
                deps.tripStatus
              ) || deps.isModifyDestinationReviewVisible) &&
              ) || deps.isModifyDestinationReviewVisible
                || (deps.tripStatus === TripStatus.VehicleApproaching && deps.isPickupRouteChangeVisible)) &&
              !item.hideNameCallout && (
                <CoverImage
                  className="site-status-end-img"
@@ -788,7 +825,7 @@
              !item.hideNameCallout && (
                <CoverImage
                  className="site-status-end-img"
                  src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-20260722.png"
                  src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-20260812.png"
                />
              )}
            {item.name === "车辆位置" &&
@@ -818,7 +855,7 @@
                        {deps.routeDistance?.value ?? 0}
                      </CoverView>
                      <CoverView className="car-before-order-tip-unit">
                        {deps.routeDistance?.unit === "m" ? "M" : "KM"}
                        {deps.routeDistance?.unit === "m" ? "米" : "公里"}
                      </CoverView>
                      <CoverView className="car-before-order-tip-value">
                        {vehicleMetricDurationDisplay.value}
@@ -833,7 +870,7 @@
                        {deps.routeDistance?.value ?? 0}
                      </CoverView>
                      <CoverView>
                        {deps.routeDistance?.unit === "m" ? "M" : "KM"}
                        {deps.routeDistance?.unit === "m" ? "米" : "公里"}
                      </CoverView>
                    </CoverView>
                  )
@@ -853,6 +890,7 @@
    [
      deps.arrivedTooltipReady,
      deps.customCalloutIconSrc,
      deps.doorUnlockedInCurrentPage,
      deps.hasBeforeUserEndSite,
      deps.isAndroid,
      deps.isModifyDestinationReviewVisible,
src/robotaxi/pages/begin-route/index.tsx
@@ -2689,8 +2689,8 @@
        setShowCancelPopup={setShowCancelPopup}
        setShowModifyDestinationFailPopup={setShowModifyDestinationFailPopup}
        setModifyDestinationFailMessage={setModifyDestinationFailMessage}
      />
      */}
      />*/}
    </>
  )
}
src/robotaxi/pages/order/detail/index.tsx
@@ -149,10 +149,10 @@
        id: 1,
        longitude: displayStartPoint.longitude,
        latitude: displayStartPoint.latitude,
        width: 34,
        height: 46,
        width: 36,
        height: 56,
        anchor: { x: 0.7, y: 0.8 },
        iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/start-site-order-20260804-v2.png',
        iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/start-site-order-20260812.png',
        viewportLongitude: startPoint?.longitude,
        viewportLatitude: startPoint?.latitude,
      } : null,
@@ -160,10 +160,10 @@
        id: 2,
        longitude: endPoint.longitude,
        latitude: endPoint.latitude,
        width: 34,
        height: 46,
        width: 36,
        height: 56,
        anchor: { x: 0.7, y: 0.8 },
        iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-order-20260804-v2.png',
        iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-order-20260812.png',
      } : null
    ].filter(Boolean) as any[];
    setMarkers(markerList);
src/robotaxi/pages/select-map-site/components/select-map-site-view.tsx
@@ -97,13 +97,13 @@
            </View>
          </View>
        )}
        {/* 地图浮层入口 - 选点页不展示 */}
        {deps.nearbyLoading && deps.isAndroid && (
          <Image className="waiting-radar-img" src={deps.waitingRadarSrc} />
        )}
        {/* 指针 */}
        <View className="pointer-box">
          {deps.nearbyLoading && !deps.isAndroid && (
          {/* 雷达只在真正命中吸附、指针跳动到具体站点时才展示(由 bounce 驱动),与首页保持一致 */}
          {deps.bounce && deps.isAndroid && (
            <Image className="waiting-radar-img" src={deps.waitingRadarSrc} />
          )}
          {deps.bounce && !deps.isAndroid && (
            <Image className="waiting-radar" src={deps.waitingRadarSrc} />
          )}
          {((deps.showToolTip && shouldShowSelectedSiteEcho) || showRangeWarning) && (
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts
@@ -59,6 +59,8 @@
const AUTO_SCALE_MAX = 17; // 自动缩放最大等级
const AUTO_SCALE_MIN = 11; // 自动缩放最小等级
const INCLUDE_POINTS_PADDING = [100, 100, 100, 100] as [number, number, number, number]; // 地图回视图内边距
/** 指针跳动到附近站点的动效总时长:指针跳动(600ms)+ 雷达扫描确认(320ms),需与 index.less 中对应动画时长保持一致,与首页保持一致。 */
const POINTER_BOUNCE_SEQUENCE_DURATION_MS = 920;
const CROSS_OPERATING_AREA_FILL_COLOR = '#0000001a'; // 非可选运营区围栏填充色
const CROSS_OPERATING_AREA_STROKE_COLOR = '#DFB369'; // 非可选运营区围栏描边色
const CROSS_OPERATING_AREA_STROKE_WIDTH = 1.5; // 非可选运营区围栏描边宽度
@@ -517,7 +519,6 @@
      return;
    }
    if (type !== 'end') return;
    state.setBounce(true);
    const mapCtx = Taro.createMapContext('select-site-map');
    // 获取真实的缩放等级并通过回调传递出去,彻底阻断与 mapScale 状态的绑定,避免触发原生组件抖动
@@ -543,6 +544,8 @@
          const selectableMarkers = getSelectableMarkers(newMarkers, sites);
          const { targetMarker, minDistance } = findNearestMarker(lat, lng, selectableMarkers);
          if (targetMarker && minDistance < state.distanceConfig.adsorptionDistance) {
            // 命中吸附:指针跳动 + 雷达确认动效(与地图硬切同一时刻触发),920ms 播完后才展示 tooltip,与首页保持一致
            state.setBounce(true);
            mapCtx.moveToLocation({
              latitude: targetMarker.latitude,
              longitude: targetMarker.longitude,
@@ -552,8 +555,13 @@
            state.setLatitude(activeSite?.latitude || targetMarker.latitude);
            state.setLongitude(activeSite?.longitude || targetMarker.longitude);
            syncPinLocation(activeSite?.latitude || targetMarker.latitude, activeSite?.longitude || targetMarker.longitude);
            state.setShowToolTip(true);
            setTimeout(() => {
              if (currentRegionSeq !== state.regionSeqRef.current) return;
              state.setBounce(false);
              state.setShowToolTip(true);
            }, POINTER_BOUNCE_SEQUENCE_DURATION_MS);
          } else {
            // 未命中吸附:不播放跳动/雷达动效,立即展示提示,避免无意义等待
            resetStationSelection();
            state.setSelectedSite({});
            state.setRangeStatus(
@@ -574,10 +582,6 @@
        }
      });
    }
    setTimeout(() => {
      state.setBounce(false);
      state.setShowToolTip(true);
    }, 600);
  };
  /**
@@ -591,6 +595,7 @@
    const targetSite = findSiteByMarker(targetMarker, state.nearbySites) || targetMarker;
    state.pickupConfirmTypeRef.current = 'click';
    state.setShowToolTip(false);
    // 点击 marker 是明确的跳转动作,指针跳动 + 雷达确认动效播完(920ms)后才展示 tooltip,与首页保持一致
    state.setBounce(true);
    state.setLatitude(targetMarker.latitude);
    state.setLongitude(targetMarker.longitude);
@@ -610,7 +615,7 @@
      state.setLongitude(activeSite?.longitude || targetMarker.longitude);
      syncPinLocation(activeSite?.latitude || targetMarker.latitude, activeSite?.longitude || targetMarker.longitude);
      state.setShowToolTip(true);
    }, 600);
    }, POINTER_BOUNCE_SEQUENCE_DURATION_MS);
  };
  /**
src/robotaxi/pages/select-map-site/index.less
@@ -126,6 +126,8 @@
    .toolTip {
        position: relative;
        z-index: 10;
        animation: tooltip-fade-in 240ms ease-out;
        will-change: transform, opacity;
        .toolTip-bg {
            position: relative;
@@ -215,6 +217,8 @@
        width: 64px;
        height: 86px;
        margin-top: -54px;
        opacity: 1;
        transition: opacity 240ms ease-out;
        &.bounce {
            animation: pointer-bounce 600ms ease-in-out;
@@ -224,28 +228,32 @@
          opacity: 0;
        }
    }
}
.waiting-radar {
    position: fixed;
    width: 700px;
    height: 350px;
    top: 48%;
    left: 50%;
    transform: translate(-50%, -48%);
    pointer-events: none;
    z-index: 1;
}
    // 雷达以站点位置(.pointer-box 底边,即 translate(-50%,-100%) 后与地图中心点对齐的边)为圆心展开,
    // 用绝对定位直接锚定该边,不受内部 flex 子元素排列影响,与首页保持一致
    .waiting-radar {
        position: absolute;
        width: 700px;
        height: 350px;
        bottom: 0;
        left: 50%;
        transform: translate(-50%, 50%);
        pointer-events: none;
        z-index: 1;
        animation: radar-fade-in 200ms ease-out;
    }
.waiting-radar-img {
    position: fixed;
    width: 700px;
    height: 350px;
    top: 48%;
    left: 50%;
    transform: translate(-50%, -48%);
    pointer-events: none;
    z-index: 1;
    .waiting-radar-img {
        position: absolute;
        width: 700px;
        height: 350px;
        bottom: 0;
        left: 50%;
        transform: translate(-50%, 50%);
        pointer-events: none;
        z-index: 1;
        animation: radar-fade-in 200ms ease-out;
    }
}
@keyframes pointer-bounce {
@@ -266,6 +274,28 @@
    }
}
@keyframes tooltip-fade-in {
    0% {
        opacity: 0;
        transform: translateY(6px);
    }
    100% {
        opacity: 1;
        transform: translateY(0);
    }
}
@keyframes radar-fade-in {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}
.home-container {
    display: flex;
src/robotaxi/pages/share-trip/components/share-trip-view.tsx
@@ -2,6 +2,7 @@
import { safeParse } from '../../../../utils/Robotaxi-public-method';
import { formatRemainingDurationDisplay } from '../../../utils/format';
import { TripStatus } from '../../../constants/trip-status';
import SiteRouteCard from '../../../components/site-route-card';
type Props = {
  isExpired: boolean;
@@ -167,32 +168,25 @@
                  </View>
                </View>
                {/* 站点信息卡:参考 cancel-site-box 布局与背景卡片样式 */}
                <View className='share-site-box'>
                  <View className='share-site-box-icon'>
                    <View className='share-site-box-icon-start'></View>
                    <Image src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/dotted-line.png' />
                    <View className='share-site-box-icon-end'></View>
                  </View>
                  <View className='share-site-box-content'>
                    <View className='share-site-row'>
                      <View className={`share-site-name ${isStartSiteGray ? 'gray-text' : ''}`}>
                        {tripData?.startPointName}
                      </View>
                      {showBoardingTime && (
                <SiteRouteCard
                  className='share-site-box'
                  sites={[
                    {
                      name: tripData?.startPointName,
                      nameClassName: isStartSiteGray ? 'gray-text' : '',
                      extra: showBoardingTime && (
                        <View className='share-site-time'>{tripData.boardingTime} 上车</View>
                      )}
                    </View>
                    <View className='share-site-box-divider'></View>
                    <View className='share-site-row'>
                      <View className={`share-site-name ${isEndSiteGray ? 'gray-text' : ''}`}>
                        {tripData?.endPointName}
                      </View>
                      {showGetoffTime && (
                      )
                    },
                    {
                      name: tripData?.endPointName,
                      nameClassName: isEndSiteGray ? 'gray-text' : '',
                      extra: showGetoffTime && (
                        <View className='share-site-time'>{tripData.getoffTime} 到达</View>
                      )}
                    </View>
                  </View>
                </View>
                      )
                    }
                  ]}
                />
              </View>
            </View>
          </View>
src/robotaxi/pages/share-trip/hooks/use-map-markers.ts
@@ -4,7 +4,7 @@
// 接驾态(前往上车点)/到达终点态 上车点三角图标(与 begin-route 对齐)
const PICKUP_APPROACHING_ICON_URL =
    'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/up-triangle-20260722.png';
    'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/up-triangle-20260812.png';
// 送驾态(前往目的地)/到达终点态 下车点三角图标(与 begin-route 对齐)
const DROPOFF_INSERVICE_ICON_URL =
    'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/down-triangle-20260722.png';
src/robotaxi/pages/share-trip/hooks/use-view-delegates.tsx
@@ -48,9 +48,7 @@
      <View className='car-time' style={{ alignItems: 'baseline', paddingBottom: 0 }}>
        <View className='car-time-num'>{routeDistance?.value ?? 0}</View>
        <View style={{ flexShrink: 0 }}>
          {isPickup
            ? (routeDistance?.unit === 'm' ? 'M' : 'KM')
            : (routeDistance?.unit === 'm' ? '米' : '公里')}
          {routeDistance?.unit === 'm' ? '米' : '公里'}
        </View>
        {!isPickup && (
          <>
@@ -151,7 +149,7 @@
          >
            <CoverView className='car-time'>
              <CoverView className='car-time-num'>{deps.routeDistance?.value ?? 0}</CoverView>
              <CoverView>{deps.routeDistance?.unit === 'm' ? 'M' : 'KM'}</CoverView>
              <CoverView>{deps.routeDistance?.unit === 'm' ? '米' : '公里'}</CoverView>
            </CoverView>
          </CoverView>
        )}
@@ -162,7 +160,7 @@
                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 className='site-status-time'>限停{deps.startSite?.time}分钟</CoverView>
            </CoverView>
            <CoverImage
              className='callout-diamond-icon'
@@ -179,7 +177,7 @@
        {(item.id === 2 && [TripStatus.InService, TripStatus.ArrivedDestination, TripStatus.TripFinished, TripStatus.CancelOnlyPickup].includes(deps.tripStatus) && !item.hideNameCallout) && (
          <CoverImage
            className='site-status-end-img'
            src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-20260722.png'
            src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/end-site-20260812.png'
          />
        )}
          </>
src/robotaxi/pages/share-trip/index.less
@@ -227,7 +227,7 @@
  .calling-car-vehicle-pic {
    position: absolute;
    top: -4px;
    right: 116px;
    right: 80px;
    width: 342px;
    height: 294px;
    z-index: 10;
@@ -352,77 +352,26 @@
    }
  }
  // 站点信息卡:参考 cancel-site-box 的布局与背景卡片样式,独立于车辆信息卡
  // 站点信息卡:参考 cancel-site-box 的布局与背景卡片样式,独立于车辆信息卡;
  // 结构已抽离为 SiteRouteCard 公共组件,此处只保留容器级样式与业务特有的置灰/时间文案样式
  .share-site-box {
    margin-top: 32px;
    display: flex;
    gap: 16px;
    border-radius: 40px;
    background: rgba(204, 208, 215, 0.25);
    padding: 48px 32px 42px 32px;
    .share-site-box-icon {
      padding: 16px 0;
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      .share-site-box-icon-start {
        width: 16px;
        height: 16px;
        background: @gold-light;
        border-radius: 50%;
      }
      .share-site-box-icon-end {
        width: 16px;
        height: 16px;
        background: @black-text-main;
        border-radius: 50%;
      }
      image {
        flex: 1;
        width: 2px;
        margin: 7px 0;
        height: fit-content;
      }
    .site-route-card-item-text.gray-text {
      color: @black-text-04;
    }
    .share-site-box-content {
      flex: 1;
      .share-site-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
      }
      .share-site-name {
        font-size: 32px;
        line-height: 48px;
        color: @black-text-main;
        &.gray-text {
          color: @black-text-04;
        }
      }
      .share-site-time {
        flex-shrink: 0;
        min-width: 120px;
        margin-left: 12px;
        text-align: right;
        font-size: 24px;
        line-height: 40px;
        color: @black-text-04;
      }
      .share-site-box-divider {
        margin: 34px 32px 40px 0;
        border-top: 1px solid rgba(204, 208, 215, 0.4);
      }
    .share-site-time {
      flex-shrink: 0;
      min-width: 120px;
      margin-left: 12px;
      text-align: right;
      font-size: 24px;
      line-height: 40px;
      color: @black-text-04;
    }
  }
}
src/robotaxi/style/ui-variables.less
@@ -20,6 +20,7 @@
@gold-text: #A68042;  // 文字金
@gold-light: #DFB369; // 主题金
@dark-gold-text: #8F754A; // 黑金
@black-text-main: #141E27; // 重要文字
@black-text-04: rgba(20, 30, 39, 0.4);  // 次要文字
@gary-theme: #F1F5FA; // 浅色面板