hejianhao
2026-02-24 6f2d94658236bc5c69291387857397f5a387ee7a
feat(robotaxi): 更新高德地图密钥、优化订单与地图交互逻辑

- 更换高德地图 API 密钥
- 新增获取活跃订单接口及状态映射逻辑
- 优化电子围栏显示,自动定位到运营区域或城市中心
- 改进站点搜索,集成高德 POI 并校验起终点区域一致性
- 优化步行路线计算,返回时长用于显示
- 统一费用明细展示,增加展开/收起功能
- 将教学视频替换为静态图片,提升加载性能
- 修复客服电话字段映射问题
- 更新开发环境配置,切换回本地测试服务
- 优化订单列表和优惠券页面的空状态显示
- 删除无用文件并修正文档拼写错误
1个文件已删除
21个文件已修改
803 ■■■■■ 已修改文件
config/dev.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/api.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.tsx 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/api/begin-route.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/api/home.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/api/pricing.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/components/customer-service/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/README.md 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/announcements-popup.tsx 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/release-route.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.less 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/coupon/index.less 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/coupon/index.tsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/detail.txt 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/index.less 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/order/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Robotaxi-public-method.ts 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/dev.js
@@ -17,10 +17,10 @@
const COMMON_OSS_HOST = '"https://spre.xiaopeng.com"'; // 官渠通用 oss 资源前缀
const ADMIN_OSS_HOST = '"https://pre-xps01.xiaopeng.com"'; // 运营后台上传的额文件的 oss 域名
// const ROBOTAXI_HOST = '"http://192.168.110.80:8000"'; // 打车接口域名
// const ROBOTAXI_WS_HOST = '"ws://192.168.110.34:8888/websocket"';
const ROBOTAXI_HOST = '"http://221.182.45.100:8000"';
const ROBOTAXI_WS_HOST = '"ws://221.182.45.100:8888/websocket"';
const ROBOTAXI_HOST = '"http://192.168.110.80:8000"'; // 打车接口域名
const ROBOTAXI_WS_HOST = '"ws://192.168.110.34:8888/websocket"';
// const ROBOTAXI_HOST = '"http://221.182.45.100:8000"';
// const ROBOTAXI_WS_HOST = '"ws://221.182.45.100:8888/websocket"';
const XP_APPID_MAP = {
  weapp: '"xxxxxxx"', // 用户中心登录appId
src/constants/api.ts
@@ -130,6 +130,6 @@
// 高德地图KEY
export const AMAP_KEY = 'e5212ad7656a00a09b9968dcbe551726'
export const AMAP_KEY = '8157fdfa13878d3f7bf69c560c8a846a'
src/pages/robotaxi/home/index.tsx
@@ -20,10 +20,12 @@
  convertAdcodeToCityCode,
  handleBannerJump,
  fetchMapData,
  getLocationByIP
  getLocationByIP,
  getCityLocation,
  getCityInfoWithId
} from '../../../utils/Robotaxi-public-method';
import { _ROBOTAXI_WS_HOST } from '../../../constants/api';
import { getGeofence, getBannerList, getAgreement } from '../../../robotaxi/api/home';
import { getGeofence, getBannerList, getAgreement, getOrderActive } from '../../../robotaxi/api/home';
import { getOrderDistance } from '../../../robotaxi/api/select-map-site';
import { checkOtp } from '../../../utils/tools';
@@ -48,6 +50,7 @@
  const [bannerList, setBannerList] = useState<any[]>([]); // 轮播图列表
  const [electronicAreaId, setElectronicAreaId] = useState(''); // 大头针所在电子围栏ID
  const [adsorptionDistance, setAdsorptionDistance] = useState(null); // 吸附距离
  const [orderData, setOrderData] = useState<any>({}); // 订单数据
  const [boxHeight, setBoxHeight] = useState<number | string>('auto'); // 活动区域高度
  const startY = useRef(0); // 拖动开始时的Y坐标
@@ -75,6 +78,20 @@
    7: '车辆已到达目的地,请尽快结束行程',
  }
  const mapActiveStatusToTripStatus = (status: number) => {
    if (status === 0) return 1;
    if (status === 1 || status === 2) return 3;
    if (status === 3) return 4;
    if (status === 4) return 5;
    if (status === 5) return 6;
    if (status === 6) return 7;
    if (status === 7) return 7;
    if (status === 8) return 8;
    if (status === 9) return 9;
    if (status === 10) return 9;
    return 0;
  };
  // 监听页面显示
  useDidShow(() => {
@@ -89,6 +106,30 @@
    checkOtp(false).then(otp => {
      if (otp) {
        wsManager.connect(_ROBOTAXI_WS_HOST);
        getOrderActive().then(res => {
          setOrderData(res || {});
          setTripStatus(mapActiveStatusToTripStatus(res.status || 0));
          Taro.setStorageSync('tripStatus', mapActiveStatusToTripStatus(res.status || 0));
          Taro.setStorageSync('orderId', res.id || '');
          if (!Taro.getStorageSync('startSite')) {
            Taro.setStorageSync('startSite', {
              id: res.startPointId || '',
              name: res.startPointName || '',
              latitude: res.startLat || 0,
              longitude: res.startLon || 0,
              electronicAreaId: res.xpElectronicFenceAreaId || '',
            })
          }
          if (!Taro.getStorageSync('endSite')) {
            Taro.setStorageSync('endSite', {
              id: res.endPointId || '',
              name: res.endPointName || '',
              latitude: res.endLat || 0,
              longitude: res.endLon || 0,
              electronicAreaId: res.xpElectronicFenceAreaId || '',
            })
          }
        })
      } else {
        // 没有登录
        console.log('用户未登录');
@@ -109,12 +150,10 @@
              setLongitude(res.longitude);
              const cityName = res.city || res.province || '北京市';
              const cityCode = convertAdcodeToCityCode(res.adcode, cityName, res.province);
              const cityInfo = {
                name: cityName,
                code: cityCode
              };
              setSelectedCity(cityInfo);
              Taro.setStorageSync('selectedCity', cityInfo);
              getCityInfoWithId(cityCode, cityName).then(cityInfo => {
                setSelectedCity(cityInfo);
                Taro.setStorageSync('selectedCity', cityInfo);
              });
            }).catch(() => {
              // IP 获取失败,使用默认值
              setLatitude(DEFAULT_LATITUDE);
@@ -157,12 +196,10 @@
                    const { city, province, adcode } = data.regeocodeData.addressComponent;
                    const cityName = (city && typeof city === 'string' && city.length > 0) ? city : province;
                    const cityCode = convertAdcodeToCityCode(adcode, cityName, province);
                    const cityInfo = {
                      name: cityName,
                      code: cityCode
                    };
                    setSelectedCity(cityInfo);
                    Taro.setStorageSync('selectedCity', cityInfo);
                    getCityInfoWithId(cityCode, cityName).then(cityInfo => {
                      setSelectedCity(cityInfo);
                      Taro.setStorageSync('selectedCity', cityInfo);
                    });
                  }
                })
                .catch(() => {
@@ -268,18 +305,43 @@
    getGeofence({ cityCode: selectedCity.code }).then((res: any) => {
      if (res && Array.isArray(res)) {
      if (res && Array.isArray(res) && res.length > 0) {
        const newPolygons = formatGeofencePolygons(res);
        setPolygons(newPolygons);
        // 定位到第一个运营区域的第一个点
        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));
        }
      } else {
        setPolygons([]);
        // 定位到城市中心
        if (selectedCity.name) {
          getCityLocation(selectedCity.name).then(loc => {
            setLatitude(loc.latitude);
            setLongitude(loc.longitude);
          }).catch(err => {
            console.error('获取城市中心失败', err);
          });
        }
      }
    }).catch(err => {
      console.error('获取电子围栏失败', err);
      setPolygons([]);
      // 定位到城市中心
      if (selectedCity.name) {
        getCityLocation(selectedCity.name).then(loc => {
          setLatitude(loc.latitude);
          setLongitude(loc.longitude);
        }).catch(err => {
          console.error('获取城市中心失败', err);
        });
      }
    });
  }, [selectedCity.code]);
  }, [selectedCity.code, selectedCity.name]);
  const calculateBoxHeight = () => {
    // 延迟计算,确保 DOM 渲染完成
@@ -365,12 +427,10 @@
              // 将区级 adcode 转换为市级 adcode
              const cityCode = convertAdcodeToCityCode(adcode, cityName, province);
              const cityInfo = {
                name: cityName,
                code: cityCode
              };
              setSelectedCity(cityInfo);
              Taro.setStorageSync('selectedCity', cityInfo);
              getCityInfoWithId(cityCode, cityName).then(cityInfo => {
                setSelectedCity(cityInfo);
                Taro.setStorageSync('selectedCity', cityInfo);
              });
            }
          })
          .catch((info) => {
@@ -386,12 +446,10 @@
          setLongitude(res.longitude);
          const cityName = res.city || res.province || '北京市';
          const cityCode = convertAdcodeToCityCode(res.adcode, cityName, res.province);
          const cityInfo = {
            name: cityName,
            code: cityCode
          };
          setSelectedCity(cityInfo);
          Taro.setStorageSync('selectedCity', cityInfo);
          getCityInfoWithId(cityCode, cityName).then(cityInfo => {
            setSelectedCity(cityInfo);
            Taro.setStorageSync('selectedCity', cityInfo);
          });
          setCurrentAddress(cityName);
          setCurrentSite({
            name: cityName,
@@ -477,6 +535,10 @@
      mapCtx.getCenterLocation({
        success: async (res) => {
          const { latitude: lat, longitude: lng } = res;
          if (causedBy === 'gesture' || causedBy === 'drag' || causedBy === 'scale') {
            setLatitude(lat);
            setLongitude(lng);
          }
          try {
            // 更新地图数据
@@ -796,7 +858,7 @@
                <View className="order-status-left">
                  <Image className="order-status-left-icon" src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e76e8fd6-1f52-482f-b9c0-6a026f4d2797.png' />
                  <View className="order-status-left-detail">
                    <View>粤PXV7029</View>
                    <View>{orderData.plateNumber}</View>
                    <View>{ORDER_STATUS[tripStatus]}</View>
                  </View>
                </View>
src/robotaxi/api/begin-route.ts
@@ -4,7 +4,7 @@
// 导航-获取路线和费用
export const queryRouteAndFare = async (payload) => {
    return request({
        url: _ROBOTAXI_HOST + '/operation/base/v1/mini-program/navigation/route-and-fare',
        url: _ROBOTAXI_HOST + '/itinerary/base/v1/mini-program/navigation/route-and-fare',
        method: 'POST',
        payload
    });
@@ -13,7 +13,7 @@
// 保存订单
export const saveOrder = async (payload) => {
    return request({
        url: _ROBOTAXI_HOST + '/operation/base/v1/mini-program/order/save',
        url: _ROBOTAXI_HOST + '/itinerary/base/v1/mini-program/order/save',
        method: 'POST',
        payload
    });
@@ -22,7 +22,7 @@
// 获取等待时间
export const getWaitTime = async (payload) => {
    return request({
        url: _ROBOTAXI_HOST + '/operation/base/v1/mini-program/pricing/getWaitTime',
        url: _ROBOTAXI_HOST + '/itinerary/base/v1/mini-program/pricing/getWaitTime',
        method: 'POST',
        payload
    });
src/robotaxi/api/home.ts
@@ -67,4 +67,12 @@
        url: _ROBOTAXI_HOST + '/operation/api/v1/mini-program/privacy/getAgreement',
        method: 'POST'
    });
}
// 获取订单状态
export const getOrderActive = async () => {
    return request({
        url: _ROBOTAXI_HOST + `/order/api/v1/mini-program/order/active`,
        method: 'GET'
    });
}
src/robotaxi/api/pricing.ts
@@ -7,7 +7,7 @@
 */
export const getPricingRules = async (openCityId: string | number) => {
    return request({
        url: `${_ROBOTAXI_HOST}/operation/base/v1/mini-program/pricing/rules/${openCityId}`,
        url: `${_ROBOTAXI_HOST}/itinerary/base/v1/mini-program/pricing/rules/${openCityId}`,
        method: 'GET'
    });
};
src/robotaxi/components/customer-service/index.tsx
@@ -17,7 +17,7 @@
  useEffect(() => {
    if (show && !visible) {
      getServicePhone().then((res) => {
        setPhoneNumber(res?.msg);
        setPhoneNumber(res?.customerServicePhone);
        setTimeout(() => {
          Taro.hideTabBar({ animation: true });
          setVisible(true);
src/robotaxi/pages/begin-route/README.md
@@ -17,6 +17,6 @@
tripStatus不同状态标点和线路渲染逻辑,路线都是从socket消息来,车辆点位也是
4:渲染上车点、车辆点位、车辆位置公里、路线
5、6:渲染车辆点位、车辆位置公里、下车点、路线
5、6:渲染车辆点位、车辆位置公里、下#车点、路线
7:渲染车辆点位、下车点
8、9::渲染车辆上车点、下车点、路线
src/robotaxi/pages/begin-route/components/announcements-popup.tsx
@@ -11,17 +11,17 @@
export default function AnnouncementsPopup({ show = false, onClose }: Props) {
    const [visible, setVisible] = useState(false);
    const [animationData, setAnimationData] = useState<any>();
    const [video1Error, setVideo1Error] = useState(false);
    const [video2Error, setVideo2Error] = useState(false);
    const [doorImgError, setDoorImgError] = useState(false);
    const [seatbeltImgError, setSeatbeltImgError] = useState(false);
    const video1 = 'https://www.psciio.com/idcard/a9f71103ce7a497495cfdaf81363b6f5.mp4'
    const video2 = 'https://www.psciio.com/idcard/7d82b80f20bf4791915855f05667cecc.mp4'
    const doorImage = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/f4a18c3f-a2db-45e6-8967-1510355b5e3b.webp'
    const seatbeltImage = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/54cb5215-7603-443c-95a9-2558f54b6cb5.webp'
    useEffect(() => {
        if (show && !visible) {
            setVisible(true);
            setVideo1Error(false);
            setVideo2Error(false);
            setDoorImgError(false);
            setSeatbeltImgError(false);
            const animation = Taro.createAnimation({
                duration: 300,
                timingFunction: 'ease-out'
@@ -53,18 +53,13 @@
                    <Image onClick={onClose} className='announcements-icon' src={require('../../../assets/close-icon.png')} />
                </View>
                {
                    video1Error ? (
                    doorImgError ? (
                        <Image className='demo-img' src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/fd2b5c95-ba7b-405c-b566-98357ec9c3a6.png' />
                    ) : (
                        <Video
                        <Image
                            className='demo-img'
                            src={video1}
                            autoplay
                            loop
                            controls={false}
                            showCenterPlayBtn={false}
                            enablePlayGesture={false}
                            onError={() => setVideo1Error(true)}
                            src={doorImage}
                            onError={() => setDoorImgError(true)}
                        />
                    )
                }
@@ -72,18 +67,13 @@
                    <View className='announcements-text'>第二步:系上安全带</View>
                </View>
                {
                    video2Error ? (
                    seatbeltImgError ? (
                        <Image className='demo-img' src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/d4aa9610-c1b6-45b6-b6ae-affd2e9cc8ef.png' />
                    ) : (
                        <Video
                        <Image
                            className='demo-img'
                            src={video2}
                            autoplay
                            loop
                            controls={false}
                            showCenterPlayBtn={false}
                            enablePlayGesture={false}
                            onError={() => setVideo2Error(true)}
                            src={seatbeltImage}
                            onError={() => setSeatbeltImgError(true)}
                        />
                    )
                }
src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -50,6 +50,8 @@
    const [pickupCountdownSeconds, setPickupCountdownSeconds] = useState<number | null>(null); // 上车倒计时
    const [finishCountdownSeconds, setFinishCountdownSeconds] = useState<number | null>(null); // 到达目的地倒计时
    const [fareBreakdown, setFareBreakdown] = useState<any>(null);
    const [isEvaluated, setIsEvaluated] = useState(false); // 是否已评价
    const [showDetail, setShowDetail] = useState(false); // 是否显示费用明细
    const countdownTimerRef = useRef<any>(null); // 倒计时定时器引用
    const finishCountdownTimerRef = useRef<any>(null); // 结束倒计时定时器引用
    const tempTimerRef = useRef<any>(null); // 温度定时器引用
@@ -58,6 +60,7 @@
    const pickupEndTimeRef = useRef<number | null>(null); // 上车时间
    const finishEndTimeRef = useRef<number | null>(null); // 到达目的地时间
    const lastFareOrderIdRef = useRef<string | null>(null);
    const hasShownAnnouncementsRef = useRef(false); // 是否已展示过公告弹窗
    const formatCountdown = (seconds: number) => {
        const safeSeconds = Math.max(0, seconds);
@@ -118,6 +121,7 @@
        if (type === 3) return '节假日时段';
        return '普通时段';
    };
    const isArriveSoon = tripStatus === 4 && typeof displayRouteDuration === 'number' && !Number.isNaN(displayRouteDuration) && displayRouteDuration < 1;
    const titleMap = {
        3: <>预计<View className="time">{waitMinutes}</View>分钟内有车应答</>,
        4: <>车辆还有<View className="time">{displayRouteDuration}</View>分钟到达</>,
@@ -130,7 +134,7 @@
    const descMap = {
        3: { text: '正全力为您叫车中,请耐心等待...', className: 'calling-car-content-desc' },
        4: limitMinutes ? { text: `此处限停${limitMinutes}分钟,请尽快前往上车点`, className: 'calling-car-content-warning' } : null,
        4: limitMinutes ? { text: `此处限停${limitMinutes}分钟,请尽快前往上车点`, className: isArriveSoon ? 'calling-car-content-desc-red' : 'calling-car-content-warning' } : null,
        5: { text: '此处限停,请在倒计时结束前到达上车点', className: 'calling-car-content-desc' },
        6: { text: '行程已开始,请全程系好安全带', className: 'calling-car-content-desc' },
        7: { text: '倒计时结束时车辆将自动驶离,请带好物品安全下车', className: 'calling-car-content-desc' },
@@ -188,8 +192,8 @@
            }
        } else if (tripStatus === 5) {
            // 已到达上车点:根据限停时间启动倒计时并处理超时
             // 2-12 新增
             const duration = Number(Taro.getStorageSync('orderRouteDuration'));
            // 2-12 新增
            const duration = Number(Taro.getStorageSync('orderRouteDuration'));
            if (!Number.isNaN(duration)) {
                setRouteDuration(duration);
            }
@@ -295,7 +299,7 @@
                if (isFailure) {
                    setTempStatus(lastConfirmedTempRef.current);
                    Taro.showToast({
                        title: '网络连接不稳定,设置失败,请重试',
                        title: res?.msg || '网络连接不稳定,设置失败,请重试',
                        icon: 'none'
                    });
                    return;
@@ -308,7 +312,7 @@
            }).catch(() => {
                setTempStatus(lastConfirmedTempRef.current);
                Taro.showToast({
                    title: '网络连接不稳定,设置失败,请重试',
                    title: res?.msg || '网络连接不稳定,设置失败,请重试',
                    icon: 'none'
                });
            });
@@ -337,18 +341,21 @@
            const isFailure = res?.success === false || (typeof res?.code === 'number' && res.code !== 0);
            if (isFailure) {
                Taro.showToast({
                    title: '网络连接不稳定,解锁失败,请重试',
                    title: res?.msg || '网络连接不稳定,解锁失败,请重试',
                    icon: 'none'
                });
                return;
            }
            onSuccess?.();
            if (doorType === 'door') {
                handleOpenAnnouncementsPopup?.();
                if (!hasShownAnnouncementsRef.current) {
                    handleOpenAnnouncementsPopup?.();
                    hasShownAnnouncementsRef.current = true;
                }
            }
        }).catch(() => {
            Taro.showToast({
                title: '网络连接不稳定,解锁失败,请重试',
                title: res?.msg || '网络连接不稳定,解锁失败,请重试',
                icon: 'none'
            });
        });
@@ -356,6 +363,7 @@
    // 切换评价选项
    const changeEvaluate = (index: number) => {
        if (isEvaluated) return;
        setActiveIndex(index);
        if (index == 1) {
            handleOpenEvaluatePopup?.();
@@ -373,14 +381,7 @@
            orderRating: index,
            orderId: targetOrderId
        }).then((res: any) => {
            const isFailure = res?.success === false || (typeof res?.code === 'number' && res.code !== 0);
            if (isFailure) {
                Taro.showToast({
                    title: '评价提交失败,请重试',
                    icon: 'none'
                });
                return;
            }
            setIsEvaluated(true);
            Taro.showToast({
                title: '评价已提交',
                icon: 'none'
@@ -429,7 +430,7 @@
                                车身编码: {vehicleInfo?.vehicleCode}
                            </View>
                        </View>
                        <Image className="car-card-detail-img" src={vehicleInfo?.vehiclePictures?.[0].url || ''} />
                        <Image className="car-card-detail-img" src={vehicleInfo?.vehiclePictures?.[0].url || 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/825d7fa2-6cf6-4a31-aaac-3a4de806e9a3.png'} />
                    </View>
                    {
                        tripStatus == 4 && <View>
@@ -481,93 +482,95 @@
                                    <View>{formatMoney(fareBreakdown?.orderMoney - fareBreakdown?.redPacketMoney - fareBreakdown?.couponMoney)}元</View>
                                    <View>已优惠{formatMoney(fareBreakdown?.redPacketMoney + fareBreakdown?.couponMoney)}元</View>
                                </View>
                                <View className="car-card-end-particulars-view">
                                    <View>查看明细</View>
                                    <Image src={require('../../../assets/ccc-icon-right.png')} />
                                <View className="car-card-end-particulars-view" onClick={() => setShowDetail(!showDetail)}>
                                    <View>{showDetail ? '收起明细' : '查看明细'}</View>
                                    <Image style={{ transform: showDetail ? 'rotate(90deg)' : 'rotate(0deg)', transition: 'transform 0.3s' }} src={require('../../../assets/ccc-icon-right.png')} />
                                </View>
                            </View>
                            <View className="particulars-detail">
                                {Number(fareBreakdown?.orderMoney) > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>订单总额</View>
                                            <View>{formatMoney(fareBreakdown?.orderMoney)}元</View>
                                        </View>
                                    </View>
                                }
                                {Array.isArray(fareBreakdown?.start) && fareBreakdown.start.length > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>起步价</View>
                                            <View>{formatMoney(fareBreakdown.start.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                        </View>
                                        {fareBreakdown.start.filter((item: any) => item?.money || item?.time).map((item: any, index: number) => (
                                            <View key={`start-${index}`} className="particulars-detail-item-content">
                                                <View>{item?.time || '--'}</View>
                                                <View>{formatMoney(item?.money)}元</View>
                            {showDetail &&
                                <View className="particulars-detail">
                                    {Number(fareBreakdown?.orderMoney) > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>订单总额</View>
                                                <View>{formatMoney(fareBreakdown?.orderMoney)}元</View>
                                            </View>
                                        ))}
                                    </View>
                                }
                                {Array.isArray(fareBreakdown?.mileage) && fareBreakdown.mileage.length > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>里程费</View>
                                            <View>{formatMoney(fareBreakdown.mileage.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                        </View>
                                        {fareBreakdown.mileage.filter((item: any) => item?.money || item?.mileage).map((item: any, index: number) => (
                                            <View key={`mileage-${index}`} className="particulars-detail-item-content">
                                                <View>{getTypeLabel(item?.type)}({formatNumber(item?.mileage)}公里)</View>
                                                <View>{formatMoney(item?.money)}元</View>
                                    }
                                    {Array.isArray(fareBreakdown?.start) && fareBreakdown.start.length > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>起步价</View>
                                                <View>{formatMoney(fareBreakdown.start.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                            </View>
                                        ))}
                                    </View>
                                }
                                {Array.isArray(fareBreakdown?.duration) && fareBreakdown.duration.length > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>时长费</View>
                                            <View>{formatMoney(fareBreakdown.duration.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                            {fareBreakdown.start.filter((item: any) => item?.money || item?.time).map((item: any, index: number) => (
                                                <View key={`start-${index}`} className="particulars-detail-item-content">
                                                    <View>{item?.time || '--'}</View>
                                                    <View>{formatMoney(item?.money)}元</View>
                                                </View>
                                            ))}
                                        </View>
                                        {fareBreakdown.duration.filter((item: any) => item?.money || item?.duration).map((item: any, index: number) => (
                                            <View key={`duration-${index}`} className="particulars-detail-item-content">
                                                <View>{getTypeLabel(item?.type)}({formatNumber(item?.duration, 0)}分钟)</View>
                                                <View>{formatMoney(item?.money)}元</View>
                                    }
                                    {Array.isArray(fareBreakdown?.mileage) && fareBreakdown.mileage.length > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>里程费</View>
                                                <View>{formatMoney(fareBreakdown.mileage.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                            </View>
                                        ))}
                                    </View>
                                }
                                {Array.isArray(fareBreakdown?.longDistance) && fareBreakdown.longDistance.length > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>远途费</View>
                                            <View>{formatMoney(fareBreakdown.longDistance.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                            {fareBreakdown.mileage.filter((item: any) => item?.money || item?.mileage).map((item: any, index: number) => (
                                                <View key={`mileage-${index}`} className="particulars-detail-item-content">
                                                    <View>{getTypeLabel(item?.type)}({formatNumber(item?.mileage)}公里)</View>
                                                    <View>{formatMoney(item?.money)}元</View>
                                                </View>
                                            ))}
                                        </View>
                                        {fareBreakdown.longDistance.filter((item: any) => item?.money || item?.mileage).map((item: any, index: number) => (
                                            <View key={`long-${index}`} className="particulars-detail-item-content">
                                                <View>{getTypeLabel(item?.type)}({formatNumber(item?.mileage)}公里)</View>
                                                <View>{formatMoney(item?.money)}元</View>
                                    }
                                    {Array.isArray(fareBreakdown?.duration) && fareBreakdown.duration.length > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>时长费</View>
                                                <View>{formatMoney(fareBreakdown.duration.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                            </View>
                                        ))}
                                    </View>
                                }
                                {Number(fareBreakdown?.redPacketMoney) > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>红包</View>
                                            <View className="color1">-{formatMoney(fareBreakdown?.redPacketMoney)}元</View>
                                            {fareBreakdown.duration.filter((item: any) => item?.money || item?.duration).map((item: any, index: number) => (
                                                <View key={`duration-${index}`} className="particulars-detail-item-content">
                                                    <View>{getTypeLabel(item?.type)}({formatNumber(item?.duration, 0)}分钟)</View>
                                                    <View>{formatMoney(item?.money)}元</View>
                                                </View>
                                            ))}
                                        </View>
                                    </View>
                                }
                                {Number(fareBreakdown?.couponMoney) > 0 &&
                                    <View className="particulars-detail-item">
                                        <View className="particulars-detail-item-title">
                                            <View>优惠券</View>
                                            <View className="color1">-{formatMoney(fareBreakdown?.couponMoney)}元</View>
                                    }
                                    {Array.isArray(fareBreakdown?.longDistance) && fareBreakdown.longDistance.length > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>远途费</View>
                                                <View>{formatMoney(fareBreakdown.longDistance.reduce((sum: number, item: any) => sum + (Number(item?.money) || 0), 0))}元</View>
                                            </View>
                                            {fareBreakdown.longDistance.filter((item: any) => item?.money || item?.mileage).map((item: any, index: number) => (
                                                <View key={`long-${index}`} className="particulars-detail-item-content">
                                                    <View>{getTypeLabel(item?.type)}({formatNumber(item?.mileage)}公里)</View>
                                                    <View>{formatMoney(item?.money)}元</View>
                                                </View>
                                            ))}
                                        </View>
                                    </View>
                                }
                            </View>
                                    }
                                    {Number(fareBreakdown?.redPacketMoney) > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>红包</View>
                                                <View className="color1">-{formatMoney(fareBreakdown?.redPacketMoney)}元</View>
                                            </View>
                                        </View>
                                    }
                                    {Number(fareBreakdown?.couponMoney) > 0 &&
                                        <View className="particulars-detail-item">
                                            <View className="particulars-detail-item-title">
                                                <View>优惠券</View>
                                                <View className="color1">-{formatMoney(fareBreakdown?.couponMoney)}元</View>
                                            </View>
                                        </View>
                                    }
                                </View>
                            }
                            <View className="car-card-end-cost">
                                <View className="car-card-end-cost-money">
                                    全程{formatNumber(fareBreakdown?.kilometer)}公里|{formatNumber(fareBreakdown?.time)}分钟
@@ -590,7 +593,7 @@
                tripStatus == 7 &&
                <View className="car-card-reach">
                    <View className="car-card-reach-text">点击按钮开车门</View>
                    <Image className='car-card-reach-img' src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/fd2b5c95-ba7b-405c-b566-98357ec9c3a6.png' />
                    <Image className='car-card-reach-img' src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/f4a18c3f-a2db-45e6-8967-1510355b5e3b.webp' />
                    <View className="car-card-reach-btn">
                        <View className="car-card-reach-btn-item" onClick={() => handleUnlock('trunk', () => setTrunkStatus((prev) => !prev))}>
                            {
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
@@ -51,6 +51,13 @@
            color: #F58900;
        }
        &-desc-red {
            margin-top: 8px;
            font-size: 24px;
            line-height: 34px;
            color: #F53F3F;
        }
        .cancel {
            padding: 0 24px;
            border-radius: 3112px;
@@ -498,4 +505,4 @@
            }
        }
    }
}
}
src/robotaxi/pages/begin-route/components/release-route.tsx
@@ -11,9 +11,10 @@
    callImmediately: () => void;
    fareEstimate?: any;
    handleOpenPassengerNotice?: () => void;
    walkingTime?: number;
}
export default function ReleaseRoute({ startSite, endSite, callImmediately, fareEstimate = {}, handleOpenPassengerNotice }: Props) {
export default function ReleaseRoute({ startSite, endSite, callImmediately, fareEstimate = {}, handleOpenPassengerNotice, walkingTime = 0 }: Props) {
    const toSeleteSitePage = (type: string) => {
        Taro.navigateTo({
@@ -42,7 +43,9 @@
                                <Image className="now-site-icon" src={rightIcon} />
                            </View>
                        </View>
                        <View className="now-site-walk">步行6分钟</View>
                        {
                            walkingTime > 0 && <View className="now-site-walk">步行{walkingTime}分钟</View>
                        }
                    </View>
                    {/* 终点 */}
                    <View onClick={() => toSeleteSitePage('end')} className="to-site">
src/robotaxi/pages/begin-route/index.less
@@ -125,58 +125,21 @@
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    justify-content: flex-start;
    overflow: visible; // 确保辐射动画不被裁剪
    min-width: 220px;
    height: 270px;
}
.waiting-radiation-box {
.waiting-radar {
    position: absolute;
    top: 100%; // 移到气泡下方
    left: 50%;
    margin-top: 30px; // 向下偏移以对齐Marker锚点(预估值)
    width: 0;
    height: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    top: auto;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 220px;
    pointer-events: none;
    overflow: visible;
}
@keyframes ripple {
    0% {
        opacity: 0.6;
        transform: translate(-50%, -50%) scale(0.2);
    }
    100% {
        opacity: 0;
        transform: translate(-50%, -50%) scale(2.5);
    }
}
.waiting-radiation-ring {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: rgba(26, 79, 255, 0.05);
    border: 1px solid rgba(26, 79, 255, 0.2);
    opacity: 0; // 初始不可见,由动画控制
    animation: ripple 3.5s infinite linear;
    &:nth-child(1) {
        animation-delay: 0s;
    }
    &:nth-child(2) {
        animation-delay: 1s;
    }
}
.waiting-callout-bg {
    position: relative;
src/robotaxi/pages/begin-route/index.tsx
@@ -19,10 +19,8 @@
import suggest from '../../../robotaxi/assets/suggest.png'
import originIcon from '../../../robotaxi/assets/origin-icon.png'
import wsManager from '../../../utils/websocket';
// 2-13修改的备注 start: 引入步行路径方法与箭头资源
import { getWalkingRoute } from '../../../utils/Robotaxi-public-method';
import lineRight from '../../../robotaxi/assets/line-right.png';
// 2-13修改的备注 end
import {
  saveOrder,
  queryRouteAndFare,
@@ -49,9 +47,9 @@
  const [cancelType, setCancelType] = useState(1); // 取消类型
  const [showEvaluatePopup, setShowEvaluatePopup] = useState(false); // 评价弹窗是否显示
  const [routePoints, setRoutePoints] = useState<any[]>([]); // 路线点集合
  // 2-13修改的备注 start: 新增步行路径点
  // 新增步行路径点
  const [walkingPoints, setWalkingPoints] = useState<any[]>([]);
  // 2-13修改的备注 end
  const [walkingTime, setWalkingTime] = useState<number>(0);
  const [fareEstimate, setFareEstimate] = useState<any>({}); // 预估费用信息
  const [vehicleLocation, setVehicleLocation] = useState<{ latitude: number, longitude: number } | null>(null);
  const [routeDistance, setRouteDistance] = useState<number | null>(null); // 路线距离
@@ -61,9 +59,8 @@
  const tripStatusRef = useRef(tripStatus); // 行程状态引用
  const routeRequestKeyRef = useRef<string>(''); // 路线请求键
  // 2-13修改的备注 start: 在发布行程状态也允许展示路线
  // 在发布行程状态也允许展示路线
  const shouldShowRoute = [1, 4, 5, 6, 8, 9].includes(tripStatus);
  // 2-13修改的备注 end
  const authSuccessBound = useRef(false); // 认证成功是否绑定
  const messageBound = useRef(false); // 消息是否绑定
  const orderStatusBound = useRef(false); // 订单状态是否绑定
@@ -74,9 +71,8 @@
  const orderStatusHandlerRef = useRef<(data: any) => void>(); // 订单状态处理函数
  const orderRouteHandlerRef = useRef<(data: any) => void>(); // 订单路线处理函数
  const vehicleLocationHandlerRef = useRef<(data: any) => void>(); // 车辆位置处理函数
  // 2-13修改的备注 start: 记录已为某订单拉取过车辆信息,避免重复
  // 记录已为某订单拉取过车辆信息,避免重复
  const lastFetchedVehicleOrderIdRef = useRef<string | null>(null);
  // 2-13修改的备注 end
  // 应用路线点
  const applyRoutePoints = (points: any[]) => {
@@ -113,11 +109,11 @@
  };
  const requestRouteAndFare = (originLat: number, originLng: number, destLat: number, destLng: number) => {
    const requestKey = `${originLng},${originLat}-${destLng},${destLat}`;
    if (requestKey === routeRequestKeyRef.current) {
      return;
    }
    routeRequestKeyRef.current = requestKey;
    // const requestKey = `${originLng},${originLat}-${destLng},${destLat}`;
    // if (requestKey === routeRequestKeyRef.current) {
    //   return;
    // }
    // routeRequestKeyRef.current = requestKey;
    const selectedCity = Taro.getStorageSync('selectedCity');
    const openCityId = selectedCity ? selectedCity.id : 0;
    const params = {
@@ -135,10 +131,10 @@
      if (res?.fareEstimate) {
        setFareEstimate(res.fareEstimate);
      }
      if(res?.xpPricingCard){
      if (res?.xpPricingCard) {
        Taro.setStorageSync('xpPricingCard', res.xpPricingCard);
      }
      // 2-13修改的备注 start: 解析预估阶段返回的路线并展示
      // 解析预估阶段返回的路线并展示
      const route = res?.route;
      const distance = Number(route?.distance);
      const duration = Number(route?.duration);
@@ -156,12 +152,12 @@
      } else {
        applyRoutePoints([]);
      }
      // 2-13修改的备注 end
    }).catch(() => {
    });
  };
  // 2-13修改的备注 start: 计算“当前位置→上车点”的步行路径
  // 计算“当前位置→上车点”的步行路径
  const updateWalkingRouteForStart = async () => {
    try {
      const loc = await Taro.getLocation({ type: 'gcj02' });
@@ -175,17 +171,19 @@
      }
      const origin = `${originLng},${originLat}`;
      const destination = `${targetLng},${targetLat}`;
      const wPoints = await getWalkingRoute(origin, destination);
      const { points: wPoints, duration } = await getWalkingRoute(origin, destination);
      if (Array.isArray(wPoints) && wPoints.length > 0) {
        setWalkingPoints(wPoints);
        setWalkingTime(Math.ceil(duration / 60));
      } else {
        setWalkingPoints([]);
        setWalkingTime(0);
      }
    } catch (e) {
      setWalkingPoints([]);
      setWalkingTime(0);
    }
  };
  // 2-13修改的备注 end
  // 认证成功处理函数
  useEffect(() => {
@@ -199,10 +197,10 @@
    messageHandlerRef.current = (data) => {
      console.log('ws message:', data);
    };
    // 2-13修改的备注 start: 优化订单状态回调中的车辆信息拉取逻辑
    // 优化订单状态回调中的车辆信息拉取逻辑
    orderStatusHandlerRef.current = (payload) => {
      const mappedTripStatus = payload?.tripStatus;
      console.log('payload?.tripStatus',payload?.tripStatus)
      console.log('payload?.tripStatus', payload?.tripStatus)
      if (typeof mappedTripStatus === 'number') {
        setTripStatus(mappedTripStatus);
      }
@@ -239,9 +237,6 @@
        }
        setShowCancelPopup(true);
      }
      if (payload?.showAnnouncementsPopup) {
        setShowAnnouncementsPopup(true);
      }
      if (payload?.showPassengerNotice) {
        if (Taro.getStorageSync('passengerNoticeShowed') !== 'true') {
          setShowPassengerNotice(true);
@@ -255,7 +250,7 @@
        }
      }
    };
    // 2-13修改的备注 end
    orderRouteHandlerRef.current = (payload) => {
      const points = payload?.points || [];
      applyRoutePoints(points);
@@ -334,7 +329,7 @@
    applyRoutePoints(storedPoints);
  }, [tripStatus]);
  // 2-13修改的备注 start: 在tripStatus=1时也绘制预估路线与步行路线
  // 在tripStatus=1时也绘制预估路线与步行路线
  useEffect(() => {
    if ((shouldShowRoute) && (routePoints.length > 0 || walkingPoints.length > 0)) {
      const lines: any[] = [];
@@ -362,7 +357,6 @@
      setPolyline([]);
    }
  }, [tripStatus, routePoints, walkingPoints]);
  // 2-13修改的备注 end
  // 页面初始化
  useDidShow(() => {
@@ -405,11 +399,11 @@
    if (originLng && originLat && e?.longitude && e?.latitude) {
      requestRouteAndFare(originLat, originLng, e.latitude, e.longitude);
    }
    // 2-13修改的备注 start: 初始阶段尝试计算步行路线
    // 初始阶段尝试计算步行路线
    if (tripStatus === 1 && s?.latitude && s?.longitude) {
      updateWalkingRouteForStart();
    }
    // 2-13修改的备注 end
  });
  useEffect(() => {
@@ -418,11 +412,11 @@
    if (originLng && originLat && endSite?.longitude && endSite?.latitude) {
      requestRouteAndFare(originLat, originLng, endSite.latitude, endSite.longitude);
    }
    // 2-13修改的备注 start: 选择或更新上车点后重新计算步行路线
    // 选择或更新上车点后重新计算步行路线
    if (tripStatus === 1 && startSite?.latitude && startSite?.longitude) {
      updateWalkingRouteForStart();
    }
    // 2-13修改的备注 end
  }, [startSite?.longitude, startSite?.latitude, endSite?.longitude, endSite?.latitude, longitude, latitude]);
  // 生成标记点
@@ -439,7 +433,7 @@
      zIndex: 2,
      name: s?.name || '上车点',
      customCallout: {
        anchorY: 8,
        anchorY: [2, 3].includes(tripStatus) ? 80 : 8,
        anchorX: 0,
        display: 'ALWAYS'
      }
@@ -483,7 +477,7 @@
    setMarkers(nextMarkers);
  }, [tripStatus, startSite?.name, startSite?.latitude, startSite?.longitude, endSite.name, endSite?.latitude, endSite?.longitude, vehicleLocation])
  // 2-13修改的备注 start: 调整视图以包含所有路线点(含步行)
  // 调整视图以包含所有路线点(含步行)
  useEffect(() => {
    if (shouldShowRoute && (routePoints.length > 0 || walkingPoints.length > 0)) {
      const mapCtx = Taro.createMapContext('map');
@@ -497,7 +491,6 @@
      });
    }
  }, [tripStatus, routePoints, walkingPoints]);
  // 2-13修改的备注 end
  const formatTime = (sec: number) => {
    const m = Math.floor(sec / 60);
@@ -556,17 +549,18 @@
    const waitingTime = Number(fareEstimate?.waitingTime || 0);
    const estimatedTime = formatDateTime(new Date(Date.now() + waitingTime * 60 * 1000));
    const payload = {
      endPointId: endSite.id ?? endSite.pointId ?? endSite.point_id ?? 0,
      endPointId: endSite.id,
      openCityId: openCityId,
      placementAddress: s?.address || s?.name || '',
      placementAddress: s?.name,
      placementLat: s?.latitude,
      placementLon: s?.longitude,
      startPointId: s?.id ?? s?.pointId ?? s?.point_id ?? 0,
      startPointId: s?.id,
      xpElectronicFenceAreaId: s?.electronicAreaId || endSite?.electronicAreaId || '',
      estimatedTime: estimatedTime,
    };
    try {
      await saveOrder(payload);
      const { orderId } = await saveOrder(payload);
      Taro.setStorageSync('orderId', orderId);
    } catch (error) {
      Taro.showToast({
        title: '下单失败,请重试',
@@ -620,6 +614,12 @@
    Taro.removeStorageSync('orderRouteLatestType');
    Taro.removeStorageSync('orderId');
    Taro.removeStorageSync('tripStatus');
    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);
    }
  }
  // 弹窗操作
@@ -770,6 +770,7 @@
                  <CoverView className="waiting-callout-txt">
                    <CoverView className="waiting-callout-txt-content">{item.name}</CoverView>
                  </CoverView>
                  <CoverImage className="waiting-radar" src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/63ff36fc-e84c-4078-a53b-467870645105.webp" />
                </CoverView>
              )
            }
@@ -834,6 +835,7 @@
                  callImmediately={callImmediately}
                  fareEstimate={fareEstimate}
                  handleOpenPassengerNotice={handleOpenPassengerNotice}
                  walkingTime={walkingTime}
                />
              }
              {/* 发布中弹窗 */}
src/robotaxi/pages/coupon/index.less
@@ -247,3 +247,20 @@
    }
  }
}
.no-data-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 176px 135px 0 135px;
  font-size: 32px;
  letter-spacing: normal;
  color: rgba(0, 0, 0, 0.6);
  .no-data-image {
    width: 480px;
    height: 400px;
  }
}
src/robotaxi/pages/coupon/index.tsx
@@ -109,35 +109,44 @@
          return <View className={`tab-item ${activeStatus === item.value ? 'active-status' : ''}`} key={item.value || index} onClick={() => changeStatus(item.value)}>{item.label}</View>
        })}
      </View>
      <View className='coupon-card'>
        {couponList.map((item, index) => {
          return <View className='coupon-item' key={item.couponId || index}>
      {
        couponList.length > 0 &&
        <View className='coupon-card'>
          {couponList.map((item, index) => {
            return <View className='coupon-item' key={item.couponId || index}>
            <View className='coupon-price'>
              <Text className='price'>{item.discountAmount}</Text>
              <Text className='unit'>元</Text>
            </View>
            <View className='coupon-info'>
              <Text className='name'>{item.title}</Text>
              <Text className='time'>{item.expireTime}到期</Text>
              <View className='more' onClick={() => openPopup(item)}>
                <Text className='more-detail'>查看详情</Text>
                <Image className='more-img' src={icon}></Image>
              <View className='coupon-price'>
                <Text className='price'>{item.discountAmount}</Text>
                <Text className='unit'>元</Text>
              </View>
            </View>
            {/* 状态 1-未使用 2-已使用 3-已过期 0待领取 */}
            {item.status === 0 ? (
              <View className='coupon-info'>
                <Text className='name'>{item.title}</Text>
                <Text className='time'>{item.expireTime}到期</Text>
                <View className='more' onClick={() => openPopup(item)}>
                  <Text className='more-detail'>查看详情</Text>
                  <Image className='more-img' src={icon}></Image>
                </View>
              </View>
              {/* 状态 1-未使用 2-已使用 3-已过期 0待领取 */}
              {item.status === 0 ? (
                <View className='receive' onClick={(e) => handleReceive(e, item)}>领取</View>
            ) : item.status === 1 ? (
                <View className='receive' style={{backgroundColor: '#ccc'}}>未使用</View>
            ) : item.status === 2 ? (
                <View className='receive' style={{backgroundColor: '#f5f5f5', color: '#999'}}>已使用</View>
            ) : (
                <View className='receive' style={{backgroundColor: '#f5f5f5', color: '#999'}}>已过期</View>
            )}
          </View>
        })}
      </View>
              ) : item.status === 1 ? (
                <View className='receive' style={{ backgroundColor: '#ccc' }}>未使用</View>
              ) : item.status === 2 ? (
                <View className='receive' style={{ backgroundColor: '#f5f5f5', color: '#999' }}>已使用</View>
              ) : (
                <View className='receive' style={{ backgroundColor: '#f5f5f5', color: '#999' }}>已过期</View>
              )}
            </View>
          })}
        </View>
      }
      {
        !couponList.length && <View className='no-data-box'>
          <Image className='no-data-image' src={require('../../assets/no-data-coupon.png')} />
          <View>暂无优惠券</View>
        </View>
      }
      {/* 优惠券说明 */}
      {visible && currentCoupon && <View className='privacy-popup' catchMove>
src/robotaxi/pages/order/detail.txt
File was deleted
src/robotaxi/pages/order/index.less
@@ -10,7 +10,8 @@
    background: #FFFFFF;
    margin-bottom: 24px;
    padding: 28px 40px 24px 32px;
    &:last-child{
    &:last-child {
      margin-bottom: 32px;
    }
@@ -61,3 +62,20 @@
  }
}
.no-data-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 176px 135px 0 135px;
  font-size: 32px;
  letter-spacing: normal;
  color: rgba(0, 0, 0, 0.6);
  .no-data-image {
    width: 480px;
    height: 400px;
  }
}
src/robotaxi/pages/order/index.tsx
@@ -65,7 +65,7 @@
  return (
    <View className='order-page'>
      {groups.map((group, gi) => {
      {groups.length > 0 && groups.map((group, gi) => {
        return <View key={group.dateLabel || gi}>
          {(group.orders || []).map((item, index) => {
            return <View className='order-item' key={item.orderId || index} onClick={() => toOrderDetail(item)}>
@@ -79,7 +79,12 @@
          })}
        </View>
      })}
      {
        !groups.length && <View className='no-data-box'>
          <Image className='no-data-image' src={require('../../assets/no-data-city.png')} />
          <View>暂无订单</View>
        </View>
      }
    </View>
  )
}
src/robotaxi/pages/select-map-site/index.tsx
@@ -291,7 +291,7 @@
    }
    const origin = `${originLng},${originLat}`;
    const destination = `${selectedSite.longitude},${selectedSite.latitude}`;
    getWalkingRoute(origin, destination).then((points) => {
    getWalkingRoute(origin, destination).then(({ points }) => {
      if (!Array.isArray(points) || points.length === 0) {
        setPolyline([]);
        return;
src/robotaxi/pages/site-search/index.tsx
@@ -7,6 +7,8 @@
import arrow from '../../assets/arrow.png'
import { getStartPoints, getEndPoints, queryPointByName } from '../../api/site-search';
import { formatDistance, debounce } from '../../../utils/Robotaxi-public-method';
import { AMAP_KEY } from '../../../constants/api';
import amapFile from '../../../libs/amap-wx.130.js';
@@ -71,7 +73,7 @@
    try {
      const res = await queryPointByName(payload);
      const { availablePoints, unavailablePoints } = res;
      const { availablePoints } = res;
      if (Array.isArray(availablePoints)) {
        const list = availablePoints.map(item => ({
@@ -83,15 +85,29 @@
        setSiteList([]);
      }
      if (Array.isArray(unavailablePoints)) {
        const list = unavailablePoints.map(item => ({
          ...item,
          distance: formatDistance(item.distance)
        }));
        setNonOperationalSite(list);
      } else {
        setNonOperationalSite([]);
      }
      const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
      myAmapFun.getPoiAround({
        querykeywords: queryVal,
        location: `${longitude},${latitude}`,
        success: (data) => {
          if (data && data.poisData) {
            const list = data.poisData.map(item => {
              const locationArr = item.location.split(',');
              return {
                ...item,
                longitude: Number(locationArr[0]),
                latitude: Number(locationArr[1]),
                distance: formatDistance(item.distance)
              }
            });
            setNonOperationalSite(list);
          }
        },
        fail: (info) => {
          console.log(info);
        }
      });
    } catch (error) {
      console.error('search error', error);
      setSiteList([]);
@@ -129,27 +145,34 @@
    })
  }
  const onChangeSelectSite = (item) => {
  const onChangeSelectSite = (item, isNonOperational = true) => {
    if (!isNonOperational) {
      Taro.showToast({
        title: '当前站点不在运营区域内',
        icon: 'none',
      })
      return
    }
    if (pageType == 'start') {
      const endSiteCache = Taro.getStorageSync('endSite');
      // if (endSiteCache?.electronicAreaId && item?.electronicAreaId && endSiteCache.electronicAreaId !== item.electronicAreaId) {
      //   Taro.showToast({
      //     title: '起终点需在同一运营区域',
      //     icon: 'none',
      //   })
      //   return
      // }
      if (endSiteCache?.electronicAreaId && item?.electronicAreaId && endSiteCache.electronicAreaId !== item.electronicAreaId) {
        Taro.showToast({
          title: '起终点需在同一运营区域',
          icon: 'none',
        })
        return
      }
      Taro.setStorageSync('startSite', item);
      Taro.navigateBack()
    } else if (pageType == 'end') {
      const startSiteCache = Taro.getStorageSync('startSite');
      // if (startSiteCache?.electronicAreaId && item?.electronicAreaId && startSiteCache.electronicAreaId !== item.electronicAreaId) {
      //   Taro.showToast({
      //     title: '起终点需在同一运营区域',
      //     icon: 'none',
      //   })
      //   return
      // }
      if (startSiteCache?.electronicAreaId && item?.electronicAreaId && startSiteCache.electronicAreaId !== item.electronicAreaId) {
        Taro.showToast({
          title: '起终点需在同一运营区域',
          icon: 'none',
        })
        return
      }
      Taro.setStorageSync('endSite', item)
      Taro.reLaunch({
        url: '/robotaxi/pages/begin-route/index',
@@ -237,7 +260,7 @@
          <View className='card-content'>
            {
              nonOperationalSite.map((item, index) => {
                return <View onClick={() => onChangeSelectSite(item)} className='history-item' key={item.id || index}>
                return <View onClick={() => onChangeSelectSite(item, false)} className='history-item' key={item.id || index}>
                  <Image className='history-icon' src={address}></Image>
                  <View className='history-info'>
                    <View className='history-name'>{item.name || '_'}</View>
src/utils/Robotaxi-public-method.ts
@@ -3,6 +3,7 @@
import Taro from '@tarojs/taro';
import request from './request';
import { getFenceCheck, getNearestSite } from '../robotaxi/api/home';
import { getOpenCity } from '../robotaxi/api/select-city';
// 辅助函数:计算两点之间的距离(单位:米)
export const getDistance = (lat1: number, lng1: number, lat2: number, lng2: number) => {
@@ -230,7 +231,7 @@
}
// 获取步行规划路线
export const getWalkingRoute = (origin: string, destination: string): Promise<any[]> => {
export const getWalkingRoute = (origin: string, destination: string): Promise<{ points: any[], duration: number, distance: number }> => {
  return new Promise((resolve, reject) => {
    const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
    myAmapFun.getWalkingRoute({
@@ -238,7 +239,13 @@
      destination: destination,
      success: (data) => {
        const points = parseRouteToPoints(data);
        resolve(points);
        let duration = 0;
        let distance = 0;
        if (data.paths && data.paths[0]) {
          duration = Number(data.paths[0].duration);
          distance = Number(data.paths[0].distance);
        }
        resolve({ points, duration, distance });
      },
      fail: (info) => {
        reject(info);
@@ -304,3 +311,65 @@
    }
  });
};
// 获取城市中心经纬度
export const getCityLocation = (cityName: string): Promise<{ latitude: number, longitude: number }> => {
  return new Promise((resolve, reject) => {
    const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
    myAmapFun.getInputtips({
      keywords: cityName,
      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');
        } else {
          reject('No data found');
        }
      },
      fail: (info) => {
        reject(info);
      }
    });
  });
};
// 获取包含 ID 的城市信息
export const getCityInfoWithId = async (cityCode: string, cityName: string) => {
  try {
    const res: any = await getOpenCity();
    console.log(res);
    console.log(cityCode);
    let cityId = 0;
    if (Array.isArray(res.cities)) {
      const target = res.cities.find(item => item.adcode === cityCode);
      if (target) {
        cityId = target.id;
      }
    }
    return {
      name: cityName,
      code: cityCode,
      id: cityId
    };
  } catch (error) {
    console.error('getOpenCity error', error);
    return {
      name: cityName,
      code: cityCode,
      id: 0
    };
  }
};