13404089107
2026-03-27 cfac0b723ee0dc57174e11c9fa5e057485e74925
Merge branch 'dev' of http://120.76.84.145:10101/gitblit/r/H5/xp-robotaxi-miniprogram into dev
6个文件已修改
163 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/components/passenger-notice.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/release-route.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/index.tsx 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Robotaxi-public-method.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/websocket.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/passenger-notice.tsx
@@ -50,6 +50,8 @@
                    .replace(/<ol(?![^>]*style=)/gi, '<ol style="list-style-position:inside;padding-left:30px;margin:0;box-sizing:border-box"')
                    .replace(/<ul([^>]*)style="([^"]*)"/gi, '<ul$1style="$2;list-style-position:inside;padding-left:35px;margin:0;box-sizing:border-box"')
                    .replace(/<ul(?![^>]*style=)/gi, '<ul style="list-style-position:inside;padding-left:35px;margin:0;box-sizing:border-box"')
                    .replace(/<p><\/p>/gi, '<br/>')
                    .replace(/<p>\s*<\/p>/gi, '<br/>')
            }
            setNoticeContent(res.content);
        }).catch(() => { });
src/robotaxi/pages/begin-route/components/release-route.tsx
@@ -19,7 +19,7 @@
    const toSeleteSitePage = (type: string) => {
        Taro.navigateTo({
            url: `/robotaxi/pages/select-map-site/index?pageType=${type}`,
            url: `/robotaxi/pages/select-map-site/index?pageType=${type}&skipSource=1`,
        })
    }
    const toPriceDescription = () => {
src/robotaxi/pages/begin-route/index.tsx
@@ -31,6 +31,7 @@
  convertAdcodeToCityCode,
  getCityInfoWithId,
  getDistance,
  getProjectedPoint,
  mapOrderStatusToTripStatus,
  reportEvent
} from '../../../utils/Robotaxi-public-method';
@@ -138,12 +139,14 @@
  const orderRouteBound = useRef(false); // 订单路线是否绑定
  const openTrunkBound = useRef(false); // 打开后备箱事件是否绑定
  const orderUpdatePriceBound = useRef(false); // 订单价格更新事件是否绑定
  const orderReassignBound = useRef(false); // 订单重新指派事件是否绑定
  const authSuccessHandlerRef = useRef<(data: any) => void>(); // 认证成功处理函数
  const messageHandlerRef = useRef<(data: any) => void>(); // 消息处理函数
  const orderStatusHandlerRef = useRef<(data: any) => void>(); // 订单状态处理函数
  const orderRouteHandlerRef = useRef<(data: any) => void>(); // 订单路线处理函数
  const openTrunkHandlerRef = useRef<(data: any) => void>(); // 打开后备箱事件处理函数
  const orderUpdatePriceHandlerRef = useRef<(data: any) => void>(); // 订单价格更新事件处理函数
  const orderReassignHandlerRef = useRef<(data: any) => void>(); // 订单重新指派事件处理函数
  const socketOpenHandlerRef = useRef<(() => void) | null>(null); // WebSocket连接打开处理函数
  // 记录已为某订单拉取过车辆信息,避免重复
  const lastFetchedVehicleOrderIdRef = useRef<string | null>(null);
@@ -504,29 +507,47 @@
  };
  // 使用 eraseLines 擦除已行驶路段(状态4/6专用)
  const erasePassedRouteSegments = (newPoints: any[]) => {
  const erasePassedRouteSegments = (newPoints: any[], served?: number) => {
    const oldPoints = currentFullRouteRef.current;
    if (!oldPoints?.length || !newPoints?.length) return;
    if (!oldPoints?.length || !newPoints?.length) return newPoints?.[0];
    const newFirst = newPoints[0];
    let bestMatchIndex = -1;
    let bestDistance = Infinity;
    const startIndex = lastErasedIndexRef.current;
    for (let i = startIndex; i < oldPoints.length; i++) {
      const dist = getDistance(
        oldPoints[i].latitude, oldPoints[i].longitude,
        newFirst.latitude, newFirst.longitude
      );
      if (dist < bestDistance) {
        bestDistance = dist;
        bestMatchIndex = i;
    // 如果后端提供了明确的已行驶点数 served (作为增量:这次少了几个点)
    if (typeof served === 'number' && !Number.isNaN(served) && served >= 0) {
      bestMatchIndex = startIndex + served;
      bestMatchIndex = Math.min(bestMatchIndex, oldPoints.length - 1);
    } else {
      let bestDistance = Infinity;
      // 降级使用距离匹配逻辑,移除不合理的退出条件,使匹配更加敏感
      for (let i = startIndex; i < oldPoints.length; i++) {
        const dist = getDistance(
          oldPoints[i].latitude, oldPoints[i].longitude,
          newFirst.latitude, newFirst.longitude
        );
        if (dist < bestDistance) {
          bestDistance = dist;
          bestMatchIndex = i;
        }
        // 如果找到了非常近的点(例如10米内),就可以认为这就是车辆当前位置所在的索引,提前结束查找
        if (bestDistance < 10) break;
      }
      // 已找到足够近的匹配点且距离开始增大,提前退出
      if (dist > bestDistance * 3 && bestDistance < 100) break;
    }
    if (bestMatchIndex >= startIndex && bestDistance < 500) {
    if (bestMatchIndex < startIndex) {
      // 路线发生较大变化,回退为全量重绘
      currentFullRouteRef.current = [...newPoints];
      lastErasedIndexRef.current = 0;
      lastRoutePointRef.current = null;
      lastRouteUpdateAtRef.current = 0;
      setRoutePoints([...newPoints]);
      return newPoints[0];
    }
    // 只要找到了比之前更大的索引,就进行擦除(无论距离多远,只要是往后走就应该擦除)
    if (bestMatchIndex > startIndex) {
      const mapCtx = Taro.createMapContext('map');
      const eraseIndex = Math.max(0, bestMatchIndex - 1);
@@ -551,14 +572,13 @@
      });
      lastErasedIndexRef.current = bestMatchIndex;
    } else {
      // 路线发生较大变化,回退为全量重绘
      currentFullRouteRef.current = [...newPoints];
      lastErasedIndexRef.current = 0;
      lastRoutePointRef.current = null;
      lastRouteUpdateAtRef.current = 0;
      setRoutePoints([...newPoints]);
    }
    const nextIndex = Math.min(bestMatchIndex + 1, oldPoints.length - 1);
    const pointA = oldPoints[bestMatchIndex];
    const pointB = oldPoints[nextIndex];
    return getProjectedPoint(newFirst, pointA, pointB);
  };
  // 更新车辆位置(移除节流,实时更新)
@@ -812,6 +832,22 @@
      }
      setPriceUpdateVersion((v) => v + 1);
    };
    orderReassignHandlerRef.current = (payload) => {
      const incomingOrderId = payload?.orderId || payload?.content?.orderId || Taro.getStorageSync('orderId');
      const currentOrderId = orderId || Taro.getStorageSync('orderId');
      if (incomingOrderId && currentOrderId && String(incomingOrderId) !== String(currentOrderId)) {
        return;
      }
      // 收到重新指派消息,清除上次记录的订单号,触发重新获取车辆信息
      lastFetchedVehicleOrderIdRef.current = null;
      queryVehicleByOrderId({ orderId: currentOrderId })
        .then((res: any) => {
          setVehicleInfo(res);
        })
        .catch(() => { });
    };
    // 优化订单状态回调中的车辆信息拉取逻辑
    orderStatusHandlerRef.current = (payload) => {
      const changeStatusOld = tripStatusRef.current;
@@ -901,7 +937,7 @@
    };
    orderRouteHandlerRef.current = (payload) => {
      if (tripStatusRef.current === 5) {
      if ([5, 8, 9].includes(tripStatusRef.current)) {
        return;
      }
      const points = payload?.points || [];
@@ -923,10 +959,27 @@
      // 状态4/6:使用 eraseLines 擦除已行驶路段,不再全量重绘
      const isEraseStatus = [4, 6].includes(tripStatusRef.current);
      if (isEraseStatus) {
        const firstPoint = points?.[0];
        let vehiclePoint = points?.[0];
        if (orderRouteInitializedRef.current && currentFullRouteRef.current.length > 0) {
          // 后续更新:擦除已行驶路段
          erasePassedRouteSegments(points);
          let servedCount = 0;
          try {
            const servedData = JSON.parse(payload?.content?.route || '{}')?.served;
            // 尝试将 served 解析为数组,获取其长度,否则如果是数字则直接使用,或者是字符串数组再解析
            if (Array.isArray(servedData)) {
              servedCount = servedData.length;
            } else if (typeof servedData === 'string') {
              const parsedServed = JSON.parse(servedData);
              if (Array.isArray(parsedServed)) {
                servedCount = parsedServed.length;
              }
            } else if (typeof servedData === 'number') {
              servedCount = servedData;
            }
          } catch (e) {
            console.error('解析 served 数据失败', e);
          }
          vehiclePoint = erasePassedRouteSegments(points, servedCount) || points?.[0];
        } else {
          // 首次绘制:全量渲染并存储完整路线
          orderRouteInitializedRef.current = true;
@@ -936,8 +989,8 @@
            applyRoutePoints(points);
          });
        }
        if (firstPoint && typeof firstPoint.latitude === 'number' && typeof firstPoint.longitude === 'number') {
          updateVehicleLocationThrottled(firstPoint.latitude, firstPoint.longitude, true);
        if (vehiclePoint && typeof vehiclePoint.latitude === 'number' && typeof vehiclePoint.longitude === 'number') {
          updateVehicleLocationThrottled(vehiclePoint.latitude, vehiclePoint.longitude, true);
        }
        return;
      }
@@ -978,6 +1031,10 @@
      if (orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) {
        wsManager.off('order_update_price', orderUpdatePriceHandlerRef.current);
        orderUpdatePriceBound.current = false;
      }
      if (orderReassignBound.current && orderReassignHandlerRef.current) {
        wsManager.off('order_reassign', orderReassignHandlerRef.current);
        orderReassignBound.current = false;
      }
    };
  }, []);
@@ -1402,6 +1459,10 @@
      wsManager.on('order_update_price', orderUpdatePriceHandlerRef.current);
      orderUpdatePriceBound.current = true;
    }
    if (!orderReassignBound.current && orderReassignHandlerRef.current) {
      wsManager.on('order_reassign', orderReassignHandlerRef.current);
      orderReassignBound.current = true;
    }
  };
  const ensureSocketListeners = () => {
src/robotaxi/pages/select-map-site/index.tsx
@@ -315,6 +315,10 @@
      }
      Taro.setStorageSync('startSite', selectedSite)
      if (router.params.skipSource) {
        Taro.navigateBack()
        return
      }
      Taro.navigateBack({
        delta: 2,
      })
src/utils/Robotaxi-public-method.ts
@@ -19,6 +19,44 @@
  return s * 1000;
};
/**
 * 计算点 P 在线段 AB 上的投影点(吸附算法)
 * @param p 真实点 {latitude, longitude}
 * @param a 线段起点 {latitude, longitude}
 * @param b 线段终点 {latitude, longitude}
 * @returns 投影点 {latitude, longitude}
 */
export const getProjectedPoint = (p: any, a: any, b: any) => {
  if (!p || !a || !b) return p;
  if (a.latitude === b.latitude && a.longitude === b.longitude) return a;
  const x0 = p.longitude, y0 = p.latitude;
  const x1 = a.longitude, y1 = a.latitude;
  const x2 = b.longitude, y2 = b.latitude;
  // 向量 AB
  const dx = x2 - x1;
  const dy = y2 - y1;
  // 向量 AP
  const ex = x0 - x1;
  const ey = y0 - y1;
  // 计算投影比例 t
  const dotProduct = ex * dx + ey * dy;
  const lengthSquared = dx * dx + dy * dy;
  const t = dotProduct / lengthSquared;
  // 如果投影点落在线段外,则取最近的端点
  if (t <= 0) return { latitude: a.latitude, longitude: a.longitude };
  if (t >= 1) return { latitude: b.latitude, longitude: b.longitude };
  // 返回投影点
  return {
    latitude: y1 + t * dy,
    longitude: x1 + t * dx
  };
};
// 判断是否需要更新路线(距离大于100米或时间超过10秒)
export const shouldUpdateRoute = (lastLat: number, lastLng: number, newLat: number, newLng: number, lastUpdateTime: number): boolean => {
  const distance = getDistance(lastLat, lastLng, newLat, newLng);
src/utils/websocket.ts
@@ -358,6 +358,10 @@
      const orderId = content?.orderId ?? data?.orderId;
      this.notifyListeners('order_update_price', { orderId, data, content });
    }
    if (data?.type === 'ORDER_REASSIGN') {
      const orderId = content?.orderId ?? data?.orderId;
      this.notifyListeners('order_reassign', { orderId, data, content });
    }
    if (data?.type === 'OPEN_TRUNK') {
      const isOpen = content?.isOpen ?? content?.open;
      this.notifyListeners('open_trunk', { isOpen, data, content });