hejianhao
2026-05-07 af5817c068c116506ebdd19788ffe2c7d7504ecb
feat(robotaxi): 更新订单列表、路线偏好和温度显示

- 在订单列表页添加“去支付”按钮并优化状态重置逻辑
- 重构路线偏好弹窗,使用新的API数据格式并调整z-index
- 修复温度显示单位重复问题(移除多余的°符号)
- 优化修改目的地组件,修正起点站点显示
- 调整路线摘要气泡样式,使用图片背景并更新定位
- 修复优惠活动显示条件,仅当优惠金额大于0时展示
- 优化接驾阶段路线拉取逻辑,避免误渲染行程中路线
12个文件已修改
196 ■■■■■ 已修改文件
src/pages/robotaxi/home/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/fare-detail-card.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/temperature-control.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/modify-destination-review.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/route-preference-popup/index.less 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/route-preference-popup/index.tsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/route-selection-panel.tsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.less 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/index.less 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/index.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.tsx
@@ -850,7 +850,7 @@
          />
        )
      }
      {/* 是否登录 */}
      {/* 是否展示登录入口 */}
      {
        isLogin &&
        <View className={`login-bar ${isLogin ? '' : 'hide'}`}>
src/robotaxi/pages/begin-route/components/calling-car-modules/fare-detail-card.tsx
@@ -172,7 +172,7 @@
                    <Image src={require('../../../../assets/ccc-icon-right.png')} />
                </View>
                {
                    (fareBreakdown?.operateFree != 1 && [TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(tripStatus)) &&
                    (fareBreakdown?.operateFree != 1 && [TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(tripStatus)) && Number(fareBreakdown?.redPacketMoney + fareBreakdown?.couponMoney) > 0 &&
                    <View className="car-card-end-through">
                        <View className="car-card-end-through-label">优惠活动</View>
                        <View className="car-card-end-through-content" onClick={onOpenCouponSelect}>
src/robotaxi/pages/begin-route/components/calling-car-modules/temperature-control.tsx
@@ -252,7 +252,7 @@
                        }
                    </View>
                    <View className="car-card-temp-num-text">
                        <View className="car-card-temp-num-text-temp color-000-25">{typeof tempStatus === 'number' ? `${tempStatus.toFixed(1)}°℃` : '--'}</View>
                        <View className="car-card-temp-num-text-temp color-000-25">{typeof tempStatus === 'number' ? `${tempStatus.toFixed(1)}℃` : '--'}</View>
                        <View className="car-card-temp-num-text-desc color-000-25">设定温度</View>
                    </View>
                    <View className="car-card-temp-num-btn">
src/robotaxi/pages/begin-route/components/modify-destination-review.tsx
@@ -6,7 +6,7 @@
import tipDefaultIcon from '../../../assets/tip-default-icon.png';
type Props = {
  previousEndSite: any;
  startSite: any;
  nextEndSite: any;
  previousFareEstimate?: any;
  fareEstimate: any;
@@ -21,7 +21,7 @@
};
export default function ModifyDestinationReview({
  previousEndSite,
  startSite,
  nextEndSite,
  previousFareEstimate,
  fareEstimate,
@@ -151,7 +151,7 @@
            <View className='modify-destination-review-sites'>
              <View className='modify-destination-review-site-item'>
                <View className='modify-destination-review-site-dot modify-destination-review-site-dot-old' />
                <View className='modify-destination-review-site-text start-txt'>{previousEndSite?.name || '-'}</View>
                <View className='modify-destination-review-site-text start-txt'>{startSite?.name || '-'}</View>
              </View>
              <View
                className='modify-destination-review-site-item modify-destination-review-site-item-active'
src/robotaxi/pages/begin-route/components/route-preference-popup/index.less
@@ -21,12 +21,12 @@
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.4);
        z-index: 1;
        z-index: 102;
    }
    .popup-content {
        position: relative;
        z-index: 2;
        z-index: 103;
        background: #FFFFFF;
        border-radius: 40px 40px 0 0;
        width: 100%;
src/robotaxi/pages/begin-route/components/route-preference-popup/index.tsx
@@ -20,29 +20,15 @@
  onSelect: (item: RoutePreferenceOption) => void;
};
const DEFAULT_OPTIONS: RoutePreferenceOption[] = [
  { label: '智能推荐', value: '智能推荐', code: '智能推荐' },
  { label: '时间优先', value: '时间优先', code: '时间优先' },
  { label: '低价优先', value: '低价优先', code: '低价优先' }
];
const DEFAULT_OPTIONS: RoutePreferenceOption[] = [];
const normalizeOptions = (res: any): RoutePreferenceOption[] => {
  const list = Array.isArray(res)
    ? res
    : Array.isArray(res?.list)
      ? res.list
      : Array.isArray(res?.data)
        ? res.data
        : Array.isArray(res?.routePreferences)
          ? res.routePreferences
          : Array.isArray(res?.preferences)
            ? res.preferences
            : [];
  const list = Array.isArray(res) ? res : [];
  const normalized = list.map((item: any) => {
    const label = item?.label || item?.name || item?.displayName || item?.preferenceName || item?.value;
    const value = item?.value ?? item?.code ?? item?.preferenceCode ?? label;
    const code = item?.code ?? item?.preferenceCode ?? item?.value ?? label;
    const label = item?.name;
    const value = item?.amapStrategy;
    const code = item?.code;
    if (!label || value === undefined || value === null) {
      return null;
    }
@@ -112,26 +98,26 @@
          <Image className='close-icon' src={closeIcon} onClick={onClose} />
        </View>
        <View className='popup-list-wrap'>
            <View className='popup-list-inner'>
              {options.map((item, index) => {
                const isSelected = item.value === value;
                return (
                  <View
                    key={item.value || index}
                    className={`popup-item ${isSelected ? 'active' : ''}`}
                    onClick={() => {
                      onSelect(item);
                      onClose();
                    }}
                  >
                    <View className='popup-name'>{item.label}</View>
                    {isSelected && (
                      <Image className='select-icon' src={selectTypeLineIcon} />
                    )}
                  </View>
                );
              })}
            </View>
          <View className='popup-list-inner'>
            {options.map((item, index) => {
              const isSelected = item.value === value;
              return (
                <View
                  key={item.value || index}
                  className={`popup-item ${isSelected ? 'active' : ''}`}
                  onClick={() => {
                    onSelect(item);
                    onClose();
                  }}
                >
                  <View className='popup-name'>{item.label}</View>
                  {isSelected && (
                    <Image className='select-icon' src={selectTypeLineIcon} />
                  )}
                </View>
              );
            })}
          </View>
        </View>
      </View>
    </View>
src/robotaxi/pages/begin-route/components/route-selection-panel.tsx
@@ -19,12 +19,9 @@
  selectedRouteIndex = 0,
  handleSelectRoute
}: Props) {
  const DEFAULT_PREFERENCE: RoutePreferenceOption = { label: '智能推荐', value: 'default', code: 'default' }; // 路线偏好默认值,避免初始渲染空指针
  const [showPreferencePopup, setShowPreferencePopup] = useState(false);
  const [preference, setPreference] = useState<RoutePreferenceOption>({
    label: '智能推荐',
    value: '智能推荐',
    code: '智能推荐'
  });
  const [preference, setPreference] = useState<RoutePreferenceOption>(DEFAULT_PREFERENCE);
  const [preferenceOptions, setPreferenceOptions] = useState<RoutePreferenceOption[]>([]);
  const openCityId = Number(Taro.getStorageSync('selectedCity')?.id || 0);
@@ -34,9 +31,9 @@
      if (cancelled) return;
      const list = Array.isArray(res) ? res : (Array.isArray(res?.list) ? res.list : (Array.isArray(res?.data) ? res.data : []));
      const normalized = list.map((item: any) => {
        const label = item?.label || item?.name || item?.displayName || item?.preferenceName || item?.value;
        const value = item?.value ?? item?.code ?? item?.preferenceCode ?? label;
        const code = item?.code ?? item?.preferenceCode ?? item?.value ?? label;
        const label = item?.name;
        const value = item?.amapStrategy;
        const code = item?.code;
        if (!label || value === undefined || value === null) return null;
        return { label: String(label), value: String(value), code: String(code) };
      }).filter(Boolean) as RoutePreferenceOption[];
@@ -69,7 +66,7 @@
    }
    setPreference(prev => {
      if (prev.label !== mappedLabel) {
      if (prev?.label !== mappedLabel) {
        const newCode = currentRoute.preference !== undefined && currentRoute.preference !== null ? String(currentRoute.preference) : mappedLabel;
        return {
          label: mappedLabel,
@@ -109,7 +106,6 @@
    try {
      await saveRoutePreference({ code: item.code });
    } catch (error) {
      // Keep local state even if persistence fails.
    }
  };
@@ -118,7 +114,7 @@
      <View className='route-select-type'>
        <View className='route-select-title'>路线选择:</View>
        <View className='route-select-preference' onClick={() => setShowPreferencePopup(true)}>
          <View className='route-select-preference-text'>{preference.label}</View>
          <View className='route-select-preference-text'>{preference?.label || '智能推荐'}</View>
          <Image src={selectLineIcon} />
        </View>
      </View>
@@ -135,7 +131,7 @@
              key={index}
              onClick={() => handleSelectRoute && handleSelectRoute(index)}
            >
              <View className='route-select-content-item-title'>{item.label || (index === 0 ? '智能推荐' : `路线${index + 1}`)}</View>
              <View className='route-select-content-item-title'>{item.route?.name}</View>
              <View className='route-select-content-item-time'>{time.value}{time.unit}</View>
              <View className='route-select-content-item-distance'>
                <View>{dist}</View>
@@ -153,7 +149,7 @@
      <RoutePreferencePopup
        show={showPreferencePopup}
        value={preference.value}
        value={preference?.value}
        openCityId={openCityId}
        onClose={() => setShowPreferencePopup(false)}
        onSelect={handleSelectPreference}
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -6,6 +6,7 @@
import ModifyDestinationReview from '../components/modify-destination-review';
import { TripStatus } from '../constants/trip-status';
import { formatRemainingDurationDisplay } from '../../../utils/format';
import summaryIcon from '../../../assets/summary-icon.png';
type Deps = {
  /** 是否安卓 */
@@ -178,10 +179,10 @@
      {item.isRouteSummaryBubble ? (
        routeSummaryText ? (
          <CoverView className='route-summary-callout'>
            <CoverView className='route-summary-callout-bubble'>
            <CoverImage className='route-summary-callout-bg' src={summaryIcon} />
            <CoverView className='route-summary-callout-text'>
              {routeSummaryText}
            </CoverView>
            <CoverView className='route-summary-callout-arrow' />
          </CoverView>
        ) : null
      ) : item.isBeforeUserSite ? (
@@ -294,7 +295,7 @@
    if (deps.isModifyDestinationReviewVisible) {
      return (
        <ModifyDestinationReview
          previousEndSite={deps.previousEndSite}
          startSite={deps.startSite}
          nextEndSite={deps.previewEndSite}
          previousFareEstimate={deps.previousFareEstimate}
          fareEstimate={deps.fareEstimate}
src/robotaxi/pages/begin-route/index.less
@@ -63,10 +63,12 @@
        .origin-box {
            position: absolute;
            bottom: 100%; /* 相对于父级(底部卡片容器)顶部 */
            margin-bottom: 24px; /* 距离底部卡片24px */
            bottom: 100%;
            /* 相对于父级(底部卡片容器)顶部 */
            margin-bottom: 24px;
            /* 距离底部卡片24px */
            right: 24px;
            z-index: 101;
            z-index: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
@@ -125,33 +127,30 @@
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-bottom: 12rpx;
    min-width: 169px;
    height: 45px;
    padding-left: 15px;
    padding-right: 8px;
    box-sizing: border-box;
}
.route-summary-callout-bubble {
.route-summary-callout-bg {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}
.route-summary-callout-text {
    position: relative;
    z-index: 2;
    white-space: nowrap;
    border-radius: 10rpx;
    background: rgba(0, 0, 0, 0.6);
    font-size: 20rpx;
    font-size: 20px;
    font-weight: 500;
    line-height: 39px;
    color: #FFFFFF;
    line-height: 47rpx;
    padding: 0 12rpx 0 14rpx;
}
.route-summary-callout-arrow {
    position: absolute;
    left: 50%;
    bottom: 6rpx;
    width: 12rpx;
    height: 12rpx;
    margin-left: -6rpx;
    background: rgba(0, 0, 0, 0.6);
    transform: rotate(45deg);
    transform-origin: center;
    z-index: 1;
    white-space: nowrap;
    text-align: center;
}
.txt-center {
src/robotaxi/pages/begin-route/index.tsx
@@ -205,6 +205,10 @@
      setRouteOptions([]);
      setRoutePoints([]);
      setFareEstimate({});
      const shouldSkipOrderRouteSync = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(tripStatus); // 接驾阶段不拉取订单路线,避免误渲染行程中路线
      if (shouldSkipOrderRouteSync) {
        return;
      }
      routeRequestOrderRoute(orderId, tripStatus);
    }
  });
@@ -328,7 +332,7 @@
      alpha: 0,
      zIndex: 3,
      isRouteSummaryBubble: true,
      customCallout: { anchorY: 0, anchorX: 0, display: 'ALWAYS' }
      customCallout: { anchorY: -0.1, anchorX: 50, display: 'ALWAYS' }
    };
  }, [allowSelectableRoutes, routeDistance, routeDuration, routePoints]);
  const viewportState = useMapViewport({
@@ -768,6 +772,9 @@
  );
  const couponOrderMoney = Number(fareEstimate?.originalFare || fareEstimate?.finalFare || 0); // 优惠券可用性计算订单金额
  const showWalkNavigationButton = useMemo(() => {
    if (isModifyDestinationReviewVisible) {
      return false;
    }
    if (![TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(tripStatus)) {
      return false;
    }
@@ -786,7 +793,7 @@
    }
    const walkingDistance = getDistance(currentLongitude, currentLatitude, startLongitude, startLatitude);
    return Number.isFinite(walkingDistance) && walkingDistance >= walkingNavigationDistance;
  }, [tripStatus, startSite?.latitude, startSite?.longitude, latitude, longitude, walkingNavigationDistance]);
  }, [isModifyDestinationReviewVisible, tripStatus, startSite?.latitude, startSite?.longitude, latitude, longitude, walkingNavigationDistance]);
  const { renderMapOverlays, renderTripCard } = useViewDelegates({
    isAndroid,
    tripStatus,
src/robotaxi/pages/order/index.less
@@ -102,6 +102,13 @@
      right: 30px;
    }
    .order-pay-box-position {
      position: absolute;
      padding: 10px;
      bottom: 14px;
      right: 194px;
    }
    .order-again-box {
      margin-top: 4px;
      display: flex;
@@ -131,6 +138,21 @@
      color: #181916;
    }
    .order-pay-text {
      width: 144px;
      z-index: 0;
      border-radius: 3112px;
      box-sizing: border-box;
      border: 2px solid #181916;
      font-family: PingFang SC;
      font-size: 24px;
      font-weight: normal;
      line-height: 48px;
      text-align: center;
      letter-spacing: normal;
      color: #181916;
    }
  }
}
src/robotaxi/pages/order/index.tsx
@@ -63,6 +63,7 @@
  }
  useEffect(() => {
    setGroups([]);
    setPageNum(1);
    setHasMore(true);
    // 切换筛选条件后重置列表并重新拉取第一页。
@@ -180,6 +181,12 @@
                  <View className='order-again-text' hoverClass='common-hover-class'>再来一单</View>
                </View>
              }
              {
                formatOrderStatus(item.status) == '待支付' &&
                <View className='order-pay-box-position' onClick={(e) => e.stopPropagation()}>
                  <View className='order-pay-text' hoverClass='common-hover-class'>去支付</View>
                </View>
              }
            </View>
          })}
        </View>)