hejianhao
2026-03-09 0f014011112ee8d3ed2afd76ef700dc69dad5fa2
feat(robotaxi): 更新UI资源、样式和功能逻辑

- 替换和更新多个图片资源,删除无用资源,新增按钮图标
- 调整页面样式细节,包括字体、间距、布局和颜色
- 优化订单详情页,修改标题和评价逻辑
- 改进地图页面,调整定位和区域显示逻辑
- 完善优惠券页面,调整状态显示和时间格式
- 增强行程相关组件,优化倒计时和取消逻辑
- 修复站点搜索功能,增加距离校验和提示弹窗
- 统一代码格式,修复部分样式文件结尾
1个文件已删除
4个文件已添加
30个文件已修改
665 ■■■■■ 已修改文件
src/pages/robotaxi/home/index.less 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.tsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/address.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/cancel-btn.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/car.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/clear-icon.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/complaint-btn.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/contact-service.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/coupon.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/map.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/submit-cancel-btn.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/assets/suggest.png 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/cancel-popup.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/release-route.less 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/evaluate-popup.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.less 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/coupon/index.less 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/coupon/index.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/expense-description/index.less 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/expense-description/index.tsx 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/expense-detail/index.less 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail/index.config.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail/index.less 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail/index.tsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/index.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-city/index.tsx 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/index.less 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/index.tsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.less 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Robotaxi-public-method.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.less
@@ -1,6 +1,6 @@
.select-city {
  position: fixed;
  top: 124px;
  position: absolute;
  top: 104px;
  left: 40px;
  display: flex;
  align-items: center;
@@ -102,7 +102,7 @@
  .pointer-icon {
    width: 52px;
    height: 76px;
    height: 76px;
    margin-top: -54px;
    &.bounce {
@@ -215,6 +215,8 @@
  .content-box-top {
    padding-bottom: 102px;
    width: fit-content;
    margin: 0 auto;
  }
  .origin-box {
@@ -230,7 +232,6 @@
  }
  .position-box {
    width: 100%;
    .is-operating-area {
      margin: 0 auto;
@@ -364,6 +365,7 @@
          }
          .now-site-icon {
            flex-shrink: 0;
            margin-left: 2px;
            width: 32px;
            height: 32px;
@@ -498,6 +500,7 @@
        margin-left: 16px;
        view:first-child {
          font-family: 'Figtree-SemiBold';
          font-size: 28px;
          font-weight: 600;
          line-height: 44px;
@@ -542,11 +545,12 @@
      margin-bottom: 24px;
      border: 2px solid #fff;
      border-radius: 32px;
      height: 190px;
      .activity-img {
        border-radius: 32px;
        width: 100%;
        height: 190px;
        height: 100%;
      }
    }
  }
src/pages/robotaxi/home/index.tsx
@@ -185,7 +185,7 @@
        console.log('getSetting获取scope.userLocation授权状态', auth);
        if (auth == undefined) {
          getLocationInfo();
          skipCityChangeEffectRef.current = true;
          // skipCityChangeEffectRef.current = true;
          return;
        }
        if (auth === false) {
@@ -354,17 +354,25 @@
        setPolygons(newPolygons);
        // if ((orderData || orderData?.id) && JSON.stringify(orderData) != '{}') return;
        // 定位到第一个运营区域的第一个点
        const firstPolygon = res[0];
        if (firstPolygon && firstPolygon.points && firstPolygon.points.length > 0) {
          const firstPoint = firstPolygon.points[0];
          setLatitude(Number(firstPoint.lat));
          setLongitude(Number(firstPoint.lng));
          skipNextLocationCenterRef.current = true;
          shouldIgnoreRegionChange.current = true;
        // const firstPolygon = res[0];
        // if (firstPolygon && firstPolygon.points && firstPolygon.points.length > 0) {
        //   const firstPoint = firstPolygon.points[0];
        //   setLatitude(Number(firstPoint.lat));
        //   setLongitude(Number(firstPoint.lng));
        //   skipNextLocationCenterRef.current = true;
        //   shouldIgnoreRegionChange.current = true;
        //   const mapCtx = Taro.createMapContext('map');
        //   mapCtx.moveToLocation({
        //     latitude: Number(firstPoint.lat),
        //     longitude: Number(firstPoint.lng),
        //   });
        // }
        if (!Taro.getStorageSync('startSite')) {
          const mapCtx = Taro.createMapContext('map');
          mapCtx.moveToLocation({
            latitude: Number(firstPoint.lat),
            longitude: Number(firstPoint.lng),
          const points = extractPointsFromPolygons(newPolygons);
          mapCtx.includePoints({
            points: points,
            padding: [100, 100, 100, 100]
          });
        }
      } else {
@@ -433,7 +441,8 @@
  // 更新地图数据
  const updateMapData = async (lat: number, lng: number) => {
    const { isInside, electronicAreaId, newMarkers, sites } = await fetchMapData(lat, lng);
    const selectedCity = Taro.getStorageSync('selectedCity');
    const { isInside, electronicAreaId, newMarkers, sites } = await fetchMapData(lat, lng, selectedCity?.code);
    setIsOperatingArea(isInside);
    setElectronicAreaId(electronicAreaId);
src/robotaxi/assets/address.png

src/robotaxi/assets/cancel-btn.png
src/robotaxi/assets/car.png
Binary files differ
src/robotaxi/assets/clear-icon.png
src/robotaxi/assets/complaint-btn.png
src/robotaxi/assets/contact-service.png

src/robotaxi/assets/coupon.png

src/robotaxi/assets/map.png

src/robotaxi/assets/submit-cancel-btn.png
src/robotaxi/assets/suggest.png

src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -15,13 +15,15 @@
    queryTemperatureByOrderId,
    queryOrderFinishTime,
    queryFareBreakdown,
    submitOrderComment
    submitOrderComment,
    cancelOrder
} from '../../../api/begin-route';
type Props = {
    tripStatus?: number; // 行程状态
    handleOpenCancelPopup?: () => void; // 打开取消弹窗
    handleCancelTimeout?: () => void; // 取消超时处理
    handleFinishTimeout?: () => void; // 到达目的地倒计时结束处理
    handleOpenOverRoutePopup?: () => void; // 打开超出行程时间弹窗
    handlePay?: () => void; // 支付处理
    updateStatus?: () => void; // 更新行程状态
@@ -38,7 +40,7 @@
    orderId?: string; // 订单ID
}
export default function CallingCar({ tripStatus = 3, handleOpenCancelPopup, handleCancelTimeout, handleOpenOverRoutePopup, handlePay, updateStatus, handleOpenEvaluatePopup, handleOpenAnnouncementsPopup, estimatedWaitMinutes, routeDurationSeconds, vehicleInfo, orderId }: Props) {
export default function CallingCar({ tripStatus = 3, handleOpenCancelPopup, handleCancelTimeout, handleFinishTimeout, handleOpenOverRoutePopup, handlePay, updateStatus, handleOpenEvaluatePopup, handleOpenAnnouncementsPopup, estimatedWaitMinutes, routeDurationSeconds, vehicleInfo, orderId }: Props) {
    const [trunkStatus, setTrunkStatus] = useState(false); // 后备箱状态
    const [lockStatus, setLockStatus] = useState(false); // 锁车状态
@@ -135,7 +137,7 @@
        return '普通时段';
    };
    const durationDisplay = formatDurationDisplay(displayRouteDurationSeconds);
    const isArriveSoon = tripStatus === 4 && typeof displayRouteDurationSeconds === 'number' && !Number.isNaN(displayRouteDurationSeconds) && displayRouteDurationSeconds < 60;
    const isArriveSoon = tripStatus === 4 && ((typeof displayRouteDurationSeconds === 'number' && !Number.isNaN(displayRouteDurationSeconds) && displayRouteDurationSeconds <= 60) || !displayRouteDurationSeconds);
    const titleMap = {
        3: <>预计<View className="time">{waitMinutes}</View>分钟内有车应答</>,
        4: <>车辆还有<View className="time">{durationDisplay.value}</View>{durationDisplay.unit}到达</>,
@@ -214,7 +216,14 @@
                    if (remaining !== null && remaining <= 0) {
                        if (!countdownFinishedRef.current) {
                            countdownFinishedRef.current = true;
                            handleCancelTimeout?.();
                            const targetOrderId = orderId || Taro.getStorageSync('orderId');
                            if (targetOrderId) {
                                cancelOrder(targetOrderId).finally(() => {
                                    handleCancelTimeout?.();
                                });
                            } else {
                                handleCancelTimeout?.();
                            }
                        }
                        if (countdownTimerRef.current) {
                            clearTimeout(countdownTimerRef.current);
@@ -257,6 +266,7 @@
                            clearTimeout(finishCountdownTimerRef.current);
                        }
                        setFinishCountdownSeconds(0);
                        handleFinishTimeout?.();
                        return;
                    }
                    setFinishCountdownSeconds(remaining);
@@ -425,7 +435,7 @@
        }).then((res: any) => {
            setIsEvaluated(true);
            Taro.showToast({
                title: '评价已提交',
                title: ['收到您的评价,我们将努力提升体验', '感谢您的反馈,欢迎下次乘坐'][index - 2],
                icon: 'none'
            });
        }).catch(() => {
@@ -452,12 +462,12 @@
                        <View className='calling-car-content-title'>{titleMap[tripStatus]}</View>
                    )}
                    {descMap[tripStatus] && (
                        <View style={{paddingBottom: tripStatus == 3 ? '20px' : '0px'}} className={descMap[tripStatus].className}>{descMap[tripStatus].text}</View>
                        <View style={{ paddingBottom: tripStatus == 3 ? '20px' : '0px' }} className={descMap[tripStatus].className}>{descMap[tripStatus].text}</View>
                    )}
                </View>
                {
                    [3, 4, 5].includes(tripStatus) &&
                    <View onClick={handleOpenCancelPopup} className="cancel">取消行程</View>
                    <Image onClick={handleOpenCancelPopup} src={require('../../../assets/cancel-btn.png')} className="cancel" />
                }
            </View>
            {
src/robotaxi/pages/begin-route/components/cancel-popup.tsx
@@ -99,7 +99,7 @@
                        </View>
                        :
                        <View className='cancel-btn-box'>
                            <View className='cancel-btn-cancel' onClick={onConfirm}>确认取消</View>
                            <Image className='cancel-btn-cancel' onClick={onConfirm} src={require('../../../assets/submit-cancel-btn.png')} />
                            <View className='cancel-btn-again' onClick={onClose}>再等等</View>
                        </View>
                }
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
@@ -21,7 +21,7 @@
        &-title {
            font-size: 36px;
            font-weight: bold;
            font-weight: 500;
            line-height: 36px;
            letter-spacing: normal;
            color: rgba(0, 0, 0, 0.9);
@@ -29,6 +29,7 @@
            align-items: center;
            .time {
                font-family: 'Figtree-SemiBold';
                margin: 0 4px;
                font-size: 40px;
                font-weight: 600;
@@ -59,17 +60,9 @@
        }
        .cancel {
            padding: 0 24px;
            border-radius: 3112px;
            border: 2px solid #181916;
            box-sizing: border-box;
            font-size: 24px;
            flex-shrink: 0;
            width: 144px;
            height: 48px;
            display: flex;
            align-items: center;
            justify-content: center;
            letter-spacing: normal;
            color: #181916;
        }
    }
@@ -94,6 +87,7 @@
            justify-content: space-between;
            &-num {
                font-family: 'Figtree-SemiBold';
                font-size: 48px;
                font-weight: 600;
                line-height: 64px;
@@ -223,6 +217,7 @@
                    align-items: center;
                    view:nth-child(1) {
                        font-family: 'Figtree-SemiBold';
                        font-size: 48px;
                        font-weight: 600;
                        line-height: 40px;
@@ -239,6 +234,7 @@
                    }
                    view:nth-child(3) {
                        font-family: 'Figtree-Light';
                        font-size: 28px;
                        line-height: 36px;
                        letter-spacing: normal;
@@ -286,7 +282,7 @@
                display: flex;
                flex-direction: column;
                gap: 16px;
                overflow:auto;
                overflow: auto;
                max-height: 0;
                margin-top: 0;
@@ -383,6 +379,7 @@
        padding-top: 22px;
        &-text {
            font-family: 'Figtree-Light';
            font-size: 28px;
            line-height: 44px;
            letter-spacing: normal;
@@ -462,6 +459,7 @@
        border: 3px solid #FFFFFF;
        &-title {
            font-weight: 500;
            padding-left: 32px;
            font-size: 28px;
            line-height: 40px;
@@ -504,4 +502,4 @@
            }
        }
    }
}
}
src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less
@@ -37,7 +37,7 @@
        .cancel-tip-box {
            margin-top: 4px;
            .cancel-tip-box-title{
            .cancel-tip-box-title {
                font-size: 36px;
                font-weight: bold;
                line-height: normal;
@@ -116,14 +116,7 @@
            .cancel-btn-cancel {
                flex: 1;
                line-height: 96px;
                text-align: center;
                border-radius: 16px;
                box-sizing: border-box;
                border: 2px solid #181916;
                font-size: 30px;
                letter-spacing: normal;
                color: #181916;
                height: 96px;
            }
            .cancel-btn-again {
src/robotaxi/pages/begin-route/components/css-folder/release-route.less
@@ -141,7 +141,7 @@
                    }
                    .money-num {
                        font-family: 'Figtree-Light';
                        font-family: 'Figtree-SemiBold';
                        margin: 0 4px;
                        font-size: 48px;
                        font-weight: 600;
@@ -201,6 +201,7 @@
                    }
                    .original-fare {
                        font-family: 'Figtree-Light';
                        font-size: 28px;
                        font-weight: normal;
                        line-height: 40px;
src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
@@ -63,6 +63,13 @@
            });
            return;
        }
        if (!feedbackContent.trim()) {
            Taro.showToast({
                title: '请输入问题描述',
                icon: 'none'
            });
            return;
        }
        setIsSubmitting(true);
        const submitter = submitMode === 'complaint' ? submitComplaint : submitOrderComment;
        submitter({
@@ -150,7 +157,7 @@
                        {/* <Image className='evaluate-problem-pic-box-img  /> */}
                        <View className='evaluate-problem-pic-box-upload'>
                            <Image className='evaluate-problem-pic-box-upload-img' src={require('../../../assets/upload-icon.png')} />
                            <View className='evaluate-problem-pic-box-upload-count'>0/9</View>
                            <View className='evaluate-problem-pic-box-upload-count'>0/3</View>
                        </View>
                    </View>
                </View>
src/robotaxi/pages/begin-route/index.less
@@ -53,7 +53,7 @@
.container {
    width: 100%;
    position: absolute;
    position: fixed;
    bottom: 0;
    z-index: 10;
@@ -200,6 +200,7 @@
    background: rgba(255, 255, 255, 0.9);
    padding: 12px 12px 12px 20px;
    min-width: 155px;
    text-align: center;
}
.waiting-callout-txt-content {
src/robotaxi/pages/begin-route/index.tsx
@@ -19,7 +19,7 @@
import suggest from '../../../robotaxi/assets/suggest.png'
import originIcon from '../../../robotaxi/assets/origin-icon.png'
import wsManager from '../../../utils/websocket';
import { getWalkingRoute, formatDurationToClock, formatDateTime, clearOrderTripStorage, preloadImageToTemp } from '../../../utils/Robotaxi-public-method';
import { getWalkingRoute, formatDurationToClock, formatDateTime, clearOrderTripStorage, preloadImageToTemp, debounce } from '../../../utils/Robotaxi-public-method';
import {
  saveOrder,
  queryRouteAndFare,
@@ -27,6 +27,7 @@
  getOrderPayment,
  cancelOrder,
  queryOrderRoute,
  endOrder,
} from '../../../robotaxi/api/begin-route';
import { getOrderDetail } from '../../../robotaxi/api/order';
import { checkOtp } from '../../../utils/tools';
@@ -584,7 +585,7 @@
            latitude: e.latitude,
            longitude: e.longitude,
          }],
          padding: [50, 50, 600, 50]
          padding: [50, 50, 800, 50]
        });
      }
      setElapsedSeconds(0);
@@ -592,17 +593,22 @@
      Taro.removeStorageSync(WAITING_TIMER_ELAPSED_KEY);
    } else if ([8, 9].includes(tripStatus)) {
      getOrderDetail(Taro.getStorageSync('orderId')).then((res: any) => {
        const mapCtx = Taro.createMapContext('map');
        if (res.trajectories) {
          const points = res.trajectories.map((item: any) => ({
            latitude: item.location.split(',')[1],
            longitude: item.location.split(',')[0],
          }));
          applyRoutePoints(points);
          const mapCtx = Taro.createMapContext('map');
          mapCtx.includePoints({
            points: points,
            padding: [40, 40, 500, 40]
          })
        } else {
          mapCtx.moveToLocation({
            latitude: 39.916527,
            longitude: 116.397128,
          });
        }
      });
      setElapsedSeconds(0);
@@ -855,10 +861,16 @@
  const setPopupVisible = (setter: (value: boolean) => void, visible: boolean) => () => setter(visible);
  const handleCloseCustomerService = setPopupVisible(setShowCustomerService, false);
  const openService = setPopupVisible(setShowCustomerService, true);
  const openService = () => {
    const phoneNumber = Taro.getStorageSync('customerServicePhone');
    Taro.makePhoneCall({
      phoneNumber,
    })
  }
  // 立即呼叫
  const callImmediately = async () => {
  const callImmediately = debounce(async () => {
    const otp = await checkOtp(false);
    if (!otp) {
      jumpTo({ url: '/pages/user/login?hideBack=1' });
@@ -916,7 +928,7 @@
      wsManager.on('vehicle_location', vehicleLocationHandlerRef.current);
      vehicleLocationBound.current = true;
    }
  }
  }, 500)
  // 确认取消
  const handleConfirmCancel = async () => {
@@ -953,8 +965,17 @@
  const handleCloseCancelPopup = setPopupVisible(setShowCancelPopup, false);
  const handleOpenCancelPopup = setPopupVisible(setShowCancelPopup, true);
  const handleOpenCancelPopupWithType = (type: number) => {
    setOrderId('');
    clearOrderTripStorage();
    setCancelType(type);
    setShowCancelPopup(true);
    setTripStatus(1);
    updateWalkingRouteForStart();
    const s = Taro.getStorageSync('startSite');
    const e = Taro.getStorageSync('endSite');
    if (s?.latitude && s?.longitude && e?.longitude && e?.latitude) {
      requestRouteAndFare(s.latitude, s.longitude, e.latitude, e.longitude, true);
    }
  };
  const handleOpenCancelPopupByTripStatus = (status: number) => {
    if ([2, 3].includes(status)) {
@@ -970,6 +991,15 @@
  // 确认结束行程
  const handleConfirmOverRoute = () => {
    setShowOverRoutePopup(false);
    setTripStatus(8);
  }
  const handleFinishCountdownTimeout = async () => {
    const targetOrderId = orderId || Taro.getStorageSync('orderId');
    if (!targetOrderId) return;
    try {
      await endOrder(targetOrderId);
    } catch (error) {
    }
    setTripStatus(8);
  }
@@ -1091,7 +1121,7 @@
        showLocation={!!longitude}
        onCalloutTap={handleCalloutTap}
        onRegionChange={handleRegionChange}
        style={{ width: '100%', height: '100%' }}
        style={{ width: '100%', height: '85vh' }}
      >
        {/* 自定义callout */}
        {
@@ -1199,6 +1229,7 @@
                  vehicleInfo={vehicleInfo}
                  handleOpenCancelPopup={() => handleOpenCancelPopupByTripStatus(tripStatus)}
                  handleCancelTimeout={() => handleOpenCancelPopupWithType(4)}
                  handleFinishTimeout={handleFinishCountdownTimeout}
                  handleOpenAnnouncementsPopup={handleOpenAnnouncementsPopup}
                  handleOpenOverRoutePopup={handleOpenOverRoutePopup}
                  handlePay={handlePay}
src/robotaxi/pages/coupon/index.less
@@ -74,10 +74,11 @@
        align-items: center;
        flex-shrink: 0;
        margin-right: 48px;
        min-width: 154px;
      }
      .price {
        font-family: Figtree;
        font-family: 'Figtree-SemiBold';
        font-size: 68px;
        font-weight: 600;
        line-height: 72px;
src/robotaxi/pages/coupon/index.tsx
@@ -8,12 +8,12 @@
export default function CouponPage() {
  const [couponStatus] = useState([
    { label: '全部', value: '' },
    // { label: '全部', value: '' },
    // { label: '待领取', value: 0 },
    { label: '未使用', value: 1 },
    { label: '已使用', value: 2 },
    { label: '已失效', value: 3 }])
  const [activeStatus, setActiveStatus] = useState<string | number>('')
  const [activeStatus, setActiveStatus] = useState<string | number>(1)
  const [couponList, setCouponList] = useState<CouponItem[]>([])
  const [animationData, setAnimationData] = useState<any>(null)
  const [visible, setVisible] = useState(false)
@@ -38,6 +38,15 @@
      // 出错时如果是刷新,清空列表
      if (isRefresh) setCouponList([]);
    }
  }
  const formatTime = (t?: string, type?: string) => {
    const d = new Date(t);
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    const h = String(d.getHours()).padStart(2, '0');
    const min = String(d.getMinutes()).padStart(2, '0');
    return type == 'list' ? `${y}年${m}月${day}日 ${h}:${min}` : `${y}-${m}-${day}`;
  }
  useEffect(() => {
@@ -103,8 +112,8 @@
  }
  const toHome = () => {
    Taro.switchTab({
      url: '/robotaxi/pages/home/index'
    Taro.reLaunch({
      url: '/pages/robotaxi/home/index'
    })
  }
@@ -127,7 +136,7 @@
                </View>
                <View className='coupon-info'>
                  <Text className='name'>{item.title}</Text>
                  <Text className='time'>{item.expireTime}到期</Text>
                  <Text className='time'>{formatTime(item.expireTime, 'list')}到期</Text>
                  <View className='more' onClick={() => openPopup(item)}>
                    <Text className='more-detail'>查看详情</Text>
                    <Image className='more-img' src={icon}></Image>
@@ -136,16 +145,16 @@
              </View>
              {/* 状态 1-未使用 2-已使用 3-已过期 0待领取 */}
              {
              // item.status === 0 ? (
              //   <View className='receive' onClick={(e) => handleReceive(e, item)}>领取</View>
              // ) :
               item.status === 1 ? (
                <View onClick={toHome} className='receive' style={{ backgroundColor: '#F58900' }}>去使用</View>
              ) : item.status === 2 ? (
                <Image className='status-img' src={require('../../assets/coupon-used.png')} />
              ) : item.status === 3 ? (
                <Image className='status-img' src={require('../../assets/coupon-lost-efficacy.png')} />
              ):''}
                // item.status === 0 ? (
                //   <View className='receive' onClick={(e) => handleReceive(e, item)}>领取</View>
                // ) :
                item.status === 1 ? (
                  <View onClick={toHome} className='receive' style={{ backgroundColor: '#F58900' }}>去使用</View>
                ) : item.status === 2 ? (
                  <Image className='status-img' src={require('../../assets/coupon-used.png')} />
                ) : item.status === 3 ? (
                  <Image className='status-img' src={require('../../assets/coupon-lost-efficacy.png')} />
                ) : ''}
            </View>
          })}
        </View>
@@ -169,15 +178,15 @@
          {/* 内容文案 */}
          <View className='remark-card'>
            <Text className='remark-label'>有效时间</Text>
            <Text className='remark-value'>{currentCoupon.expireTime} 失效</Text>
            <Text className='remark-value'>{formatTime(currentCoupon.expireTime, 'popup')} 失效</Text>
          </View>
          <View className='remark-card'>
            <Text className='remark-label'>使用时段</Text>
            <Text className='remark-value'>{currentCoupon.timeRule}</Text>
            <Text className='remark-value'>全天</Text>
          </View>
          <View className='remark-card'>
            <Text className='remark-label'>使用地区</Text>
            <Text className='remark-value'> {(currentCoupon.applicableCities || []).join('、')}</Text>
            <Text className='remark-value'> 广州</Text>
          </View>
          <View className='remark-card'>
            <Text className='remark-label'>使用说明</Text>
src/robotaxi/pages/expense-description/index.less
@@ -114,7 +114,7 @@
    }
    .num {
      font-family: Figtree;
      font-family: 'Figtree-SemiBold';
      font-size: 48px;
      font-weight: 600;
      line-height: 40px;
@@ -218,26 +218,9 @@
  .complaint {
    margin-top: 30px;
    display: flex;
    justify-content: center;
    align-items: center;
    .complaint-text {
      padding: 24px 0px;
      box-sizing: border-box;
      border: 2px solid #000000;
      width: 548px;
      height: 92px;
      border-radius: 16px;
      font-family: PingFang SC;
      font-size: 28px;
      font-weight: normal;
      line-height: 44px;
      letter-spacing: 0px;
      color: rgba(0, 0, 0, 0.9);
      text-align: center;
    }
    margin: 0 auto;
    width: 548px;
    height: 92px;
  }
}
@@ -259,4 +242,4 @@
.color-red {
  color: #FF4D4F !important;
}
}
src/robotaxi/pages/expense-description/index.tsx
@@ -218,9 +218,7 @@
        </View>
      )}
      {/* 去投诉 */}
      <View className='complaint' onClick={() => setShowEvaluatePopup(true)}>
        <Text className='complaint-text'>不认可车费,去投诉</Text>
      </View>
      <Image className='complaint' onClick={() => setShowEvaluatePopup(true)} src={require('../../../assets/complaint-btn.png')} />
      {/* 评价弹窗 */}
      <EvaluatePopup
src/robotaxi/pages/expense-detail/index.less
@@ -132,7 +132,7 @@
    }
    .num {
      font-family: Figtree;
      font-family: 'Figtree-SemiBold';
      font-size: 48px;
      font-weight: 600;
      line-height: 40px;
@@ -203,4 +203,4 @@
.color-red {
  color: #FF4D4F !important;
}
}
src/robotaxi/pages/order/detail/index.config.ts
@@ -1,3 +1,3 @@
export default {
  navigationBarTitleText: '订单详情',
  navigationBarTitleText: '订单已结束',
};
src/robotaxi/pages/order/detail/index.less
@@ -47,6 +47,7 @@
        height: 20px;
        z-index: 1;
        margin: 0 14px;
        background: #D8D8D8;
      }
      .car-code {
@@ -155,7 +156,7 @@
        align-items: center;
        .price {
          font-family: Figtree;
          font-family: 'Figtree-SemiBold';
          font-size: 48px;
          font-weight: 600;
          line-height: 40px;
@@ -174,7 +175,7 @@
        }
        .old-price {
          font-family: Figtree;
          font-family: 'Figtree-Light';
          font-size: 28px;
          font-weight: normal;
          line-height: 40px;
@@ -231,6 +232,7 @@
      .line-address {
        display: flex;
        align-items: center;
        .line-icon {
          /* 自动布局子元素 */
          width: 8px;
@@ -250,7 +252,8 @@
          letter-spacing: normal;
          color: rgba(0, 0, 0, 0.4);
        }
        .first-icon{
        .first-icon {
          background: #768AFF;
        }
@@ -258,4 +261,4 @@
    }
  }
}
}
src/robotaxi/pages/order/detail/index.tsx
@@ -9,29 +9,19 @@
import activeBad from '../../../assets/active-bad.png'
import activeNormal from '../../../assets/active-normal.png'
import right from '../../../assets/right.png'
import up from '../../../assets/up.png'
import down from '../../../assets/down.png'
import lineRight from '../../../assets/line-right.png'
import { getOrderDetail, OrderDetail } from '../../../api/order';
// Marker样式配置
const MARKER_CONFIG = {
  width: 20,        // marker宽度
  height: 36,       // marker高度(包含底部小圆点)
  anchorX: 0.55,     // 锚点X(0.5表示居中)
  anchorY: 0.8,       // 锚点Y(1表示底部对齐坐标点)
}
export default function orderDetailPage() {
  const router = useRouter();
  const { orderId } = router.params;
  const [activeIndex, setActiveIndex] = useState(3) // 默认选中"非常满意"
  const [activeIndex, setActiveIndex] = useState<number | null>(null) // 默认选中"非常满意"
  const [orderData, setOrderData] = useState<OrderDetail | null>(null);
  // 地图相关状态
  const [longitude, setLongitude] = useState(104.103731) // 地图中心点经度
  const [latitude, setLatitude] = useState(30.650722) // 地图中心点纬度
  const [longitude, setLongitude] = useState(116.397128) // 地图中心点经度
  const [latitude, setLatitude] = useState(39.916527) // 地图中心点纬度
  const [markers, setMarkers] = useState<any[]>([]) // 起点和终点标记
  const [polyline, setPolyline] = useState<any[]>([]) // 行驶路径
@@ -46,9 +36,7 @@
      const res = await getOrderDetail(id);
      setOrderData(res);
      initMap(res);
      if (res.orderRating) {
        setActiveIndex(res.orderRating);
      }
      setActiveIndex(res.orderRating);
    } catch (error) {
      console.error('获取订单详情失败', error);
      Taro.showToast({ title: '获取详情失败', icon: 'none' });
src/robotaxi/pages/order/index.tsx
@@ -16,9 +16,7 @@
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    const hh = String(d.getHours()).padStart(2, '0');
    const mm = String(d.getMinutes()).padStart(2, '0');
    return `${y}年${m}月${day}日 ${hh}:${mm}`;
    return `${y}年${m}月${day}日`;
  }
  const getList = async (page: number, isRefresh = false) => {
@@ -85,7 +83,7 @@
        return (<View key={group.dateLabel || gi}>
          {(group.orders || []).map((item, index) => {
            return <View className='order-item' key={item.orderId || index} onClick={() => toOrderDetail(item)}>
              <View className='order-time'>{`${item.time} ${item.orderTime}` || formatTime(item.time)}</View>
              <View className='order-time'>{formatTime(item.time)}</View>
              <View className='order-start'>起点:{item.originAddress || '-'}</View>
              <View className='order-start'>终点:{item.destinationAddress || '-'}</View>
              <View onClick={(e) => { e.stopPropagation(); againOrder(item) }} className='order-again'>
src/robotaxi/pages/select-city/index.tsx
@@ -146,99 +146,104 @@
      }
      {!isLoading && (
        <>
      {/* 搜索前 */}
      {
        !searchValue &&
        <View className='city-content'>
          <View className='service-city-box'>
            <View className='service-city-title'>运营城市</View>
            <View className='service-city-list'>
              {
                openCityList.map((item, index) => (
                  <View
                    key={item.id}
                    className={`service-city-item ${selectedCity.name === item.name && 'active-city'}`}
                    onClick={() => handleSelectCity(item, true)}
                  >
                    {item.name}
                  </View>
                ))
              }
            </View>
          </View>
          <View className='non-operational-city-box'>
            <View className='non-operational-city-title'>待开放城市</View>
            <ScrollView
              className='non-operational-city-list'
              scrollY
              scrollWithAnimation
              scrollIntoView={scrollIntoView}
            >
              {
                filteredPendingList.map((group) => (
                  <View key={group.title} id={`city-${group.title}`} className='city-group'>
                    <View className='city-group-title'>{group.title}</View>
                    {
                      group.items.map((city, index) => (
                        <View
                          key={index}
                          className='non-operational-city-item'
                        >
                          {city}
                        </View>
                      ))
                    }
                  </View>
                ))
              }
            </ScrollView>
          </View>
          <View className='shortcut'>
            {
              filteredPendingList.map((group) => (
                <View
                  key={group.title}
                  className='shortcut-item'
                  onClick={(e) => {
                    e.stopPropagation();
                    handleScrollTo(group.title);
                  }}
                >
                  {group.title}
                </View>
              ))
            }
          </View>
        </View>
      }
      {/* 搜索后 */}
      {
        searchValue &&
        <View>
          {/* 搜索前 */}
          {
            searchResultList.length > 0 ?
              <View className='search-result-box'>
                {
                  searchResultList.map((item, index) => {
                    const isOpen = openCityList.some(open => open.name === item.name);
                    return (
                      <View key={index} className='search-result-item' onClick={() => handleSelectCity(item, isOpen)}>
                        <View className='search-result-city'>{item.name}</View>
                        <View className={`search-result-zone ${!isOpen ? 'search-result-zone-disabled' : ''}`}>{isOpen ? '运营区' : '待开放'}</View>
            !searchValue &&
            <View className='city-content'>
              <View className='service-city-box'>
                <View className='service-city-title'>运营城市</View>
                <View className='service-city-list'>
                  {
                    openCityList.map((item, index) => (
                      <View
                        key={item.id}
                        className={`service-city-item ${selectedCity.name === item.name && 'active-city'}`}
                        onClick={() => handleSelectCity(item, true)}
                      >
                        {item.name}
                      </View>
                    )
                  })
                    ))
                  }
                </View>
              </View>
              <View className='non-operational-city-box'>
                <View className='non-operational-city-title'>待开放城市</View>
                <ScrollView
                  className='non-operational-city-list'
                  scrollY
                  scrollWithAnimation
                  scrollIntoView={scrollIntoView}
                >
                  {
                    filteredPendingList.map((group) => (
                      <View key={group.title} id={`city-${group.title}`} className='city-group'>
                        <View className='city-group-title'>{group.title}</View>
                        {
                          group.items.map((city, index) => {
                            const isOpen = openCityList.some(open => open.name === city);
                            return (
                              <View
                                onClick={() => handleSelectCity({}, isOpen)}
                                key={index}
                                className='non-operational-city-item'
                              >
                                {city}
                              </View>
                            )
                          }
                          )
                        }
                      </View>
                    ))
                  }
                </ScrollView>
              </View>
              <View className='shortcut'>
                {
                  filteredPendingList.map((group) => (
                    <View
                      key={group.title}
                      className='shortcut-item'
                      onClick={(e) => {
                        e.stopPropagation();
                        handleScrollTo(group.title);
                      }}
                    >
                      {group.title}
                    </View>
                  ))
                }
              </View>
              :
              <View className='no-data-box'>
                <Image className='no-data-image' src={require('../../assets/no-data-city.png')} />
                <View>暂无城市</View>
              </View>
            </View>
          }
        </View>
      }
          {/* 搜索后 */}
          {
            searchValue &&
            <View>
              {
                searchResultList.length > 0 ?
                  <View className='search-result-box'>
                    {
                      searchResultList.map((item, index) => {
                        const isOpen = openCityList.some(open => open.name === item.name);
                        return (
                          <View key={index} className='search-result-item' onClick={() => handleSelectCity(item, isOpen)}>
                            <View className='search-result-city'>{item.name}</View>
                            <View className={`search-result-zone ${!isOpen ? 'search-result-zone-disabled' : ''}`}>{isOpen ? '运营区' : '待开放'}</View>
                          </View>
                        )
                      })
                    }
                  </View>
                  :
                  <View className='no-data-box'>
                    <Image className='no-data-image' src={require('../../assets/no-data-city.png')} />
                    <View>暂无城市</View>
                  </View>
              }
            </View>
          }
        </>
      )}
    </View>
src/robotaxi/pages/select-map-site/index.less
@@ -182,7 +182,7 @@
        .content-title {
            font-size: 36px;
            font-weight: bold;
            font-weight: 500;
            line-height: 50px;
            letter-spacing: normal;
            color: rgba(0, 0, 0, 0.9);
src/robotaxi/pages/select-map-site/index.tsx
@@ -19,7 +19,9 @@
  formatGeofencePolygons,
  fetchMapData,
  getWalkingRoute,
  extractPointsFromPolygons
} from '../../../utils/Robotaxi-public-method';
import { preloadImageToTemp } from '../../../utils/Robotaxi-public-method';
import { getGeofence } from '../../api/home';
import { getOrderDistance } from '../../api/select-map-site';
import { checkOtp } from '../../../utils/tools';
@@ -46,6 +48,8 @@
  const [userLongitude, setUserLongitude] = useState<number | null>(null);
  const [userLatitude, setUserLatitude] = useState<number | null>(null);
  const [outOfServiceRange, setOutOfServiceRange] = useState(false);
  const tooltipBgUrl = require('../../../robotaxi/assets/tooltip-bg.png');
  const [tooltipBgSrc, setTooltipBgSrc] = useState(tooltipBgUrl);
  const router = Taro.useRouter();
@@ -117,9 +121,14 @@
              setOutOfServiceRange(getOutOfServiceRange(isInside, {}));
            });
            const mapCtx = Taro.createMapContext('map');
            mapCtx.moveToLocation({
              latitude: lat,
              longitude: lng,
            // mapCtx.moveToLocation({
            //   latitude: lat,
            //   longitude: lng,
            // });
            const points = extractPointsFromPolygons(newPolygons);
            mapCtx.includePoints({
              points: points,
              padding: [100, 100, 100, 100]
            });
          } else if (!hasCachedSite) {
            getLocationInfo();
@@ -140,6 +149,16 @@
    } else if (!hasCachedSite) {
      getLocationInfo();
    }
  }, []);
  useEffect(() => {
    let cancelled = false;
    const run = async () => {
      const local = await preloadImageToTemp(tooltipBgUrl);
      if (!cancelled) setTooltipBgSrc(local);
    };
    run();
    return () => { cancelled = true; };
  }, []);
  // 更新地图数据
@@ -253,7 +272,7 @@
    })
  }
  const confirmSite = () => {
  const confirmSite = async () => {
    if (!selectedSite.name) {
      Taro.showToast({
        title: '请选择站点',
@@ -287,6 +306,11 @@
      })
    }
    if (pageType === 'end') {
      const otp = await checkOtp(false);
      if (!otp) {
        jumpTo({ url: '/pages/user/login?hideBack=1' });
        return
      }
      const startSiteCache = Taro.getStorageSync('startSite');
      if (startSiteCache?.electronicAreaId && selectedSite?.electronicAreaId && startSiteCache.electronicAreaId != selectedSite.electronicAreaId) {
        Taro.showToast({
@@ -315,7 +339,9 @@
  // 关闭提示弹窗
  const handleCloseTipPopup = () => {
    setShowTipPopup(false);
    setTipType('');
    setTimeout(() => {
      setTipType('');
    }, 600);
  }
  // 切换子站点
@@ -397,7 +423,7 @@
        onRegionChange={handleRegionChange}
        style={{ width: '100%', height: '100%' }}
      >
        {/* 选择城市 */}
        {/* 返回 */}
        <View onClick={toBack} className="select-city">
          <Image className="select-city-icon" src={backIcon} />
        </View>
@@ -414,7 +440,7 @@
          {((showToolTip && selectedSite.name) || outOfServiceRange) && (
            <View className="toolTip">
              {(outOfServiceRange || selectedSite.time) && (
                <View className="toolTip-bg">
                <View className="toolTip-bg" style={{ backgroundImage: `url(${tooltipBgSrc})` }}>
                  <Text className="toolTip-bg-text">{outOfServiceRange ? `不在运营区` : `限停${selectedSite.time}分钟`}</Text>
                </View>
              )}
src/robotaxi/pages/site-search/index.less
@@ -35,11 +35,11 @@
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 10px;
        .cancel-icon {
          width: 32px;
          height: 32px;
          width: 36px;
          height: 36px;
          margin: 0 24px;
        }
      }
    }
src/robotaxi/pages/site-search/index.tsx
@@ -6,10 +6,13 @@
import address from '../../assets/address.png'
import arrow from '../../assets/arrow.png'
import { getStartPoints, getEndPoints, queryPointByName } from '../../api/site-search';
import { formatDistance, debounce } from '../../../utils/Robotaxi-public-method';
import { formatDistance, debounce, getDistance } from '../../../utils/Robotaxi-public-method';
import { getOrderDistance } from '../../api/select-map-site';
import { AMAP_KEY } from '../../../constants/api';
import amapFile from '../../../libs/amap-wx.130.js';
import { checkOtp } from '../../../utils/tools';
import TipPopup from '../select-map-site/components/tip-popup';
import { getNearestSite } from '../../api/home';
@@ -22,25 +25,27 @@
  const [pageType, setPageType] = useState('') //出发地 start 目的地 end
  const [showAllHistory, setShowAllHistory] = useState(false) // 是否展示全部历史记录
  const [showAllRecommend, setShowAllRecommend] = useState(false) // 是否展示全部推荐站点
  const [distanceConfig, setDistanceConfig] = useState<any>({})
  const [showTipPopup, setShowTipPopup] = useState(false)
  const [tipType, setTipType] = useState('')
  const MAX_SHOW_COUNT = 5 // 默认最多显示5条
  const MAX_EXPANDED_COUNT = 10
  const router = Taro.useRouter();
  const fetchNearbySites = async (lat: number, lon: number, electronicAreaId?: string | number) => {
  const fetchNearbySites = async (lat: number, lon: number) => {
    const selectedCity = Taro.getStorageSync('selectedCity');
    const payload = {
      cityCode: selectedCity?.code || '',
      electronicAreaId: electronicAreaId ? Number(electronicAreaId) : 0,
      lat: Number(lat),
      lon: Number(lon)
    };
    try {
      const res = await queryPointByName(payload);
      const { availablePoints } = res;
      if (Array.isArray(availablePoints)) {
        const list = availablePoints.map(item => ({
      const res = await getNearestSite({
        cityCode: selectedCity?.code || '',
        latitude: Number(lat),
        longitude: Number(lon),
      });
      const points = Array.isArray(res?.points) ? res.points : [];
      if (points.length > 0) {
        const list = points.map(item => ({
          ...item,
          distance: item.distance
        }));
@@ -60,7 +65,7 @@
    checkOtp(false).then(otp => {
      if (otp) {
        if (lat && lon) {
          fetchNearbySites(Number(lat), Number(lon), electronicAreaId);
          fetchNearbySites(Number(lat), Number(lon));
          const payload = {
            lat: Number(lat),
            lon: Number(lon)
@@ -90,6 +95,18 @@
    }
  }, [])
  useEffect(() => {
    getOrderDistance().then((res: any) => {
      if (res) {
        setDistanceConfig({
          maxDistance: res.maxDistance,
          minDistance: res.minDistance,
          adsorptionDistance: res.adsorptionDistance
        });
      }
    }).catch(() => { });
  }, []);
  const searchSite = async (val?: string) => {
    setNonOperationalSite([]);
    const queryVal = val !== undefined ? val : searchValue;
@@ -115,7 +132,7 @@
          ...item,
          distance: item.distance
        }));
        setSiteList(list);
        setSiteList(list.slice(0, MAX_EXPANDED_COUNT));
      } else {
        setSiteList([]);
      }
@@ -132,7 +149,7 @@
                ...item,
                longitude: Number(locationArr[0]),
                latitude: Number(locationArr[1]),
                distance: formatDistance(item.distance)
                // distance: formatDistance(item.distance)
              }
            });
            setNonOperationalSite(list.slice(0, MAX_SHOW_COUNT));
@@ -180,10 +197,10 @@
    })
  }
  const onChangeSelectSite = (item, isNonOperational = true) => {
  const onChangeSelectSite = async (item, isNonOperational = true) => {
    if (!isNonOperational) {
      Taro.showToast({
        title: '当前站点不在运营区域内',
        title: '当前点位暂未开放',
        icon: 'none',
      })
      return
@@ -197,9 +214,33 @@
        })
        return
      }
      try {
        if (!distanceConfig?.maxDistance) {
          const res = await getOrderDistance();
          if (res) {
            setDistanceConfig({
              maxDistance: res.maxDistance,
              minDistance: res.minDistance,
              adsorptionDistance: res.adsorptionDistance
            });
          }
        }
        const loc = await Taro.getLocation({ type: 'gcj02' });
        const distance = getDistance(loc.latitude, loc.longitude, item.latitude, item.longitude);
        if (distanceConfig?.maxDistance && distance > distanceConfig.maxDistance) {
          setTipType('2');
          setShowTipPopup(true);
          return;
        }
      } catch { }
      Taro.setStorageSync('startSite', item);
      Taro.navigateBack()
    } else if (pageType == 'end') {
      const otp = await checkOtp(false);
      if (!otp) {
        Taro.navigateTo({ url: '/pages/user/login?hideBack=1' });
        return
      }
      const startSiteCache = Taro.getStorageSync('startSite');
      if (startSiteCache?.electronicAreaId && item?.electronicAreaId && startSiteCache.electronicAreaId !== item.electronicAreaId) {
        Taro.showToast({
@@ -208,6 +249,26 @@
        })
        return
      }
      try {
        if (!distanceConfig?.minDistance) {
          const res = await getOrderDistance();
          if (res) {
            setDistanceConfig({
              maxDistance: res.maxDistance,
              minDistance: res.minDistance,
              adsorptionDistance: res.adsorptionDistance
            });
          }
        }
        if (startSiteCache && startSiteCache.longitude && startSiteCache.latitude) {
          const distance = getDistance(startSiteCache.latitude, startSiteCache.longitude, item.latitude, item.longitude);
          if (distanceConfig?.minDistance && distance < distanceConfig.minDistance) {
            setTipType('1');
            setShowTipPopup(true);
            return;
          }
        }
      } catch { }
      Taro.setStorageSync('endSite', item)
      Taro.reLaunch({
        url: '/robotaxi/pages/begin-route/index',
@@ -219,10 +280,14 @@
    setSearchValue('');
    searchSite('');
  }
  const handleCloseTipPopup = () => {
    setShowTipPopup(false);
    setTipType('');
  }
  const recommendList = siteList.length > 0 ? siteList : nearbySiteList;
  const visibleHistory = showAllHistory ? historySite : historySite.slice(0, MAX_SHOW_COUNT);
  const visibleHistory = showAllHistory ? historySite.slice(0, MAX_EXPANDED_COUNT) : historySite.slice(0, MAX_SHOW_COUNT);
  const visibleRecommend = showAllRecommend ? recommendList : recommendList.slice(0, MAX_SHOW_COUNT);
  // 使用数据截断展示,不再通过高度裁剪内容
@@ -242,7 +307,7 @@
          {
            searchValue &&
            <View onClick={clearSearchValue} className='cancel-icon-box'>
              <Image className='cancel-icon' src={require('../../assets/close-icon.png')} />
              <Image className='cancel-icon' src={require('../../assets/clear-icon.png')} />
            </View>
          }
        </View>
@@ -250,7 +315,7 @@
      </View>
      {/* 历史记录 */}
      {
        historySite.length > 0 &&
        (historySite.length > 0 && !searchValue) &&
        <View className='history-card'>
          <View className='card-header'>历史记录</View>
          <View className='card-content list-content' id='history-card-content'>
@@ -320,7 +385,7 @@
                    <View className='history-name'>{item.name || '_'}</View>
                    <View className='history-address-box'>
                      <View className='history-address'>{item.address || '-'}</View>
                      <View className='history-distance'>{item.distance}</View>
                      {/* <View className='history-distance'>{item.distance}</View> */}
                    </View>
                  </View>
                </View>
@@ -330,12 +395,17 @@
        </View>
      }
      {
        (!siteList.length && !nearbySiteList.length && !nonOperationalSite.length && !historySite.length) &&
        (!siteList.length && !nearbySiteList.length && !nonOperationalSite.length && (!searchValue ? !historySite.length : true)) &&
        <View className='no-data-box'>
          <Image className='no-data-image' src={require('../../assets/no-data-city.png')} />
          <Image className='no-data-image' src={require('../../assets/no-data-site.png')} />
          <View>暂无站点</View>
        </View>
      }
      <TipPopup
        show={showTipPopup}
        onClose={handleCloseTipPopup}
        tipType={tipType}
      />
    </View>
  )
}
src/utils/Robotaxi-public-method.ts
@@ -218,10 +218,10 @@
}
// 获取地图数据(围栏检查+附近站点)
export const fetchMapData = async (lat: number, lng: number) => {
export const fetchMapData = async (lat: number, lng: number, cityCode?: string) => {
  try {
    // 1. 先检查是否在运营区域内
    const fenceRes: any = await getFenceCheck({ latitude: lat, longitude: lng }).catch(e => {
    const fenceRes: any = await getFenceCheck({ latitude: lat, longitude: lng, cityCode }).catch(e => {
      return { status: 'inside' }; // 默认兜底
    });
@@ -282,19 +282,19 @@
      city: cityName,
      success: (data) => {
        if (data && data.tips && data.tips.length > 0) {
           // 优先取第一个有坐标的结果
           const tip = data.tips.find(t => t.location && t.location.length > 0);
           if (tip) {
             const locationArr = tip.location.split(',');
             if (locationArr.length === 2) {
                resolve({
                  longitude: Number(locationArr[0]),
                  latitude: Number(locationArr[1])
                });
                return;
             }
           }
           reject('No valid location found');
          // 优先取第一个有坐标的结果
          const tip = data.tips.find(t => t.location && t.location.length > 0);
          if (tip) {
            const locationArr = tip.location.split(',');
            if (locationArr.length === 2) {
              resolve({
                longitude: Number(locationArr[0]),
                latitude: Number(locationArr[1])
              });
              return;
            }
          }
          reject('No valid location found');
        } else {
          reject('No data found');
        }