liaof
2026-08-15 fe1d80e275e8b050cd8f8bcda37192feb83e0399
Merge branch 'fix/liaof-0814-center' into 'feature_2.6.0'

fix(begin-route): 修复弹窗组件导致地图视口跳变问题

See merge request robotaxi1/xp-robotaxi-miniprogram!282
4个文件已修改
113 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/hooks/camera/map-camera.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/camera/policies/pickup.policy.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/camera/use-map-camera-controller.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/camera/map-camera.ts
@@ -187,15 +187,16 @@
      points: [point],
      // 执行时才解析 padding 并换算纵向偏移,保证用的是执行那一刻的卡片高度/缩放
      run: () =>
        this.resolveFocusCenter(point, paddingInput).then((center) =>
          this.runNative((done) => {
        this.resolveFocusCenter(point, paddingInput).then((center) => {
          // mapPerfLog('focus:exec', { point, center });
          return this.runNative((done) => {
            this.getCtx().moveToLocation({
              latitude: center.latitude,
              longitude: center.longitude,
              complete: done,
            } as any);
          })
        ),
          });
        }),
    });
  }
@@ -214,6 +215,7 @@
        const [top, , bottom] = padding;
        const offsetPx = (Number(bottom) - Number(top)) / 2;
        if (!Number.isFinite(offsetPx) || offsetPx === 0) {
          // mapPerfLog('focus:fallback', { reason: 'zero-offset', offsetPx, padding });
          return point;
        }
        return Promise.all([this.getRegionSafe(), this.getMapHeightPx()]).then(
@@ -221,6 +223,7 @@
            const latSpan =
              (region?.northeast?.latitude ?? NaN) - (region?.southwest?.latitude ?? NaN);
            if (!Number.isFinite(latSpan) || latSpan <= 0 || !heightPx) {
              // mapPerfLog('focus:fallback', { reason: 'region-or-height-invalid', latSpan, heightPx });
              return point;
            }
            const degPerPx = latSpan / heightPx;
@@ -228,7 +231,7 @@
              latitude: point.latitude - offsetPx * degPerPx,
              longitude: point.longitude,
            };
            // mapPerfLog('focus:visible-center', { point, center, offsetPx });
            // mapPerfLog('focus:visible-center', { point, center, offsetPx, latSpan, heightPx, degPerPx });
            return center;
          }
        );
src/robotaxi/pages/begin-route/hooks/camera/policies/pickup.policy.ts
@@ -1,5 +1,5 @@
import { CameraContext, CameraIntent, CameraPolicy, NO_CAMERA_INTENT } from '../camera-policy.types';
import { LatLng, buildPointsSignature } from '../map-camera';
import { buildPointsSignature } from '../map-camera';
import { collectPickupViewportPoints, siteToPoint } from '../collect-viewport-points';
import { mapPerfLog } from '../map-perf-log';
@@ -47,44 +47,28 @@
};
/**
 * 车辆已到(VehicleArrivedPickup) 意图:fit 车 + 用户 + 上车点 (viewport:'car_user')。
 * 车辆位置缺失时用上车点兜底(车已到,二者接近);点不足 2 个则 focus 单点。
 * 车辆已到(VehicleArrivedPickup) 意图:focus 上车点,centerInVisibleArea 居中到
 * 扣除底部卡片后的可视区域。车已到上车点,二者接近,直接居中上车点即可。
 */
const buildArrivedPickupIntent = (ctx: CameraContext, stable: boolean): CameraIntent => {
  const vehicleRaw = siteToPoint(ctx.vehicleLocation);
  const pickup = siteToPoint(ctx.startSite);
  const car = vehicleRaw || pickup; // 车辆坐标校验,缺失用上车点兜底
  const points: LatLng[] = [];
  if (car) points.push(car);
  const user = siteToPoint(ctx.userLocation);
  if (user) points.push(user);
  // 上车点始终纳入 fit(三点:车 + 用户 + 上车点),避免车辆已偏离上车点位置时
  // 上车点被推出可视区域;car 用上车点兜底时该点会与 car 重合,includePoints 对
  // 重复点安全,不影响结果。
  if (pickup) points.push(pickup);
  // 调试日志:车辆到达上车点时打印三点各自是否取到及坐标,便于排查视口未居中问题。
  // includePoints 实际使用的 padding 值由 MapCamera.fit 内的 'fit:exec' 日志打印
  // (signature 同为 'arrived-pickup',可按签名关联两条日志)。
  mapPerfLog('policy:arrived-pickup-points', {
    stable,
    car: { got: !!vehicleRaw, usedPickupFallback: !vehicleRaw && !!pickup, point: car },
    user: { got: !!user, point: user },
    pickup: { got: !!pickup, point: pickup },
    fitPointsCount: points.length,
  });
  if (points.length >= MIN_FIT_POINTS) {
    return {
      type: 'fit',
      points,
      signature: stable ? 'arrived-pickup' : undefined,
    };
  // mapPerfLog('policy:arrived-pickup-points', {
  //   stable,
  //   pickup: { got: !!pickup, point: pickup },
  // });
  if (!pickup) {
    return NO_CAMERA_INTENT;
  }
  // 只有车(无用户定位、无上车点):居中到车
  return car ? { type: 'focus', point: car } : NO_CAMERA_INTENT;
  return {
    type: 'focus',
    point: pickup,
    centerInVisibleArea: true,
    signature: stable ? 'arrived-pickup' : undefined,
  };
};
/**
 * 车辆已到上车点相机策略:把车+用户+上车点三点居中一次;手势后空闲回中。
 * 车辆已到上车点相机策略:focus 上车点到可视区域中心;手势后空闲回中。
 */
export const arrivedPickupPolicy: CameraPolicy = {
  onInitial: (ctx) => buildArrivedPickupIntent(ctx, true),
src/robotaxi/pages/begin-route/hooks/camera/use-map-camera-controller.ts
@@ -29,6 +29,8 @@
  walkingPoints: LatLng[];
  /** 车辆当前位置(接驾/行程态 fit 用)。 */
  vehicleLocation: LatLng | null;
  /** 最新用户定位(接驾态 fit 用,由外部刷新传入;为空时回退首帧定位值)。 */
  userLocation?: LatLng | null;
  /** includePoints 留白(按底部卡片/安全区算好;支持异步,即 fit 执行前即时测量)。 */
  getPadding?: () => CameraPadding | Promise<CameraPadding>;
  /**
@@ -95,7 +97,7 @@
      endSite: d.endSite,
      routePoints: d.routePoints || [],
      walkingPoints: d.walkingPoints || [],
      userLocation: userLocationRef.current,
      userLocation: d.userLocation ?? userLocationRef.current,
      vehicleLocation: d.vehicleLocation ?? null,
    };
  }, []);
@@ -180,6 +182,7 @@
    params.routePoints,
    params.walkingPoints,
    params.vehicleLocation,
    params.userLocation,
    getPolicy,
    executeIntent,
    buildContext,
src/robotaxi/pages/begin-route/index.tsx
@@ -1,4 +1,5 @@
import Taro, { useDidShow, useShareAppMessage } from "@tarojs/taro"
import { View } from '@tarojs/components';
import { useEffect, useState, useRef, useMemo,useCallback } from 'react';
import './index.less';
@@ -1050,6 +1051,7 @@
    routePoints,
    walkingPoints,
    vehicleLocation,
    userLocation: currentUserLocation,
    getPadding: cameraGetPadding,
    getFocusPadding: cameraGetFocusPadding,
    onLocated: () => setMapLoading(false),
@@ -2653,23 +2655,28 @@
      <BeginRouteViewProvider value={beginRouteViewContextValue}>
        <BeginRouteView />
      </BeginRouteViewProvider>
      <ModifyDestinationFailPopup
        show={showModifyDestinationFailPopup}
        content={modifyDestinationFailMessage}
        onConfirm={handleAcknowledgeModifyDestinationFail}
      />
      <PickupExceptionPopup
        show={showPickupExceptionPopup}
        pickupExceptionInfo={pickupExceptionInfo}
        onClose={handleClosePickupExceptionPopup}
        onConfirm={handleConfirmPickupExceptionNavigation}
      />
      <CreditPayPopup
        show={showCreditPayPopup}
        onClose={handleCloseCreditPayPopup}
        onConfirm={handleConfirmCreditPayPopup}
      />
      <TripFinishedFireworks tripStatus={tripStatus} />
      <View>
        {/* 与 BeginRoutePopupArea 同理:以下弹窗组件隐藏时 return null,节点挂载/卸载
            不再连坐 <BeginRouteView> 中的 <Map>,避免 map 全量重应用 lat/lng/scale
            导致视口跳变。所有弹窗均为 position:fixed,外层 View 不影响定位。 */}
        <ModifyDestinationFailPopup
          show={showModifyDestinationFailPopup}
          content={modifyDestinationFailMessage}
          onConfirm={handleAcknowledgeModifyDestinationFail}
        />
        <PickupExceptionPopup
          show={showPickupExceptionPopup}
          pickupExceptionInfo={pickupExceptionInfo}
          onClose={handleClosePickupExceptionPopup}
          onConfirm={handleConfirmPickupExceptionNavigation}
        />
        <CreditPayPopup
          show={showCreditPayPopup}
          onClose={handleCloseCreditPayPopup}
          onConfirm={handleConfirmCreditPayPopup}
        />
        <TripFinishedFireworks tripStatus={tripStatus} />
      </View>
      {/* 转场动效调试面板 — 固定在地图上方,始终可见(临时开启,调试完成后请重新注释) */}
      {/* <DebugTransitionPanel
        orderId={orderId}
@@ -2677,8 +2684,8 @@
        setTripStatus={setTripStatus}
        bindSocketListeners={bindSocketListeners}
      /> */}
      {/* 模拟器入口已关闭,如需本地调试可取消下方注释
      <DebugOrderSimulatorPanel
      {/* 模拟器入口已关闭,如需本地调试可取消下方注释 */}
      {/* <DebugOrderSimulatorPanel
        orderId={orderId}
        setOrderId={setOrderId}
        setTripStatus={setTripStatus}
@@ -2689,7 +2696,7 @@
        setShowCancelPopup={setShowCancelPopup}
        setShowModifyDestinationFailPopup={setShowModifyDestinationFailPopup}
        setModifyDestinationFailMessage={setModifyDestinationFailMessage}
      />*/}
      /> */}
    </>
  )