hejianhao
2026-04-22 52c7cd1fe93f7d0ebb2974a9cc066d46ad4372cb
feat(home): 添加快捷胶囊功能

- 新增快捷胶囊 API 接口 getUserQuickCapsule
- 创建 useQuickCapsule hook 处理胶囊数据的获取、展示与点击跳转逻辑
- 在首页集成快捷胶囊,根据登录态和上车点动态显示胶囊文案
- 点击胶囊将起点和终点存入缓存并跳转至发单页
1个文件已添加
2个文件已修改
206 ■■■■■ 已修改文件
src/pages/robotaxi/home/hooks/use-quick-capsule.ts 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/index.tsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/api/home.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-quick-capsule.ts
New file
@@ -0,0 +1,165 @@
import Taro from '@tarojs/taro';
import { useEffect, useMemo, useRef, useState } from 'react';
import { getUserQuickCapsule } from '../../../../robotaxi/api/home';
type CapsuleSite = {
  id: string | number;
  name: string;
  latitude: number;
  longitude: number;
  electronicAreaId?: string | number;
  time?: number;
  [key: string]: any;
};
type Deps = {
  /** 是否游客态(未登录) */
  isGuest: boolean;
  /** 登录校验 */
  checkOtp: (silent?: boolean) => Promise<any>;
  /** 当前上车点 */
  currentSite: any;
};
/**
 * 首页快捷胶囊编排:负责登录态校验、数据请求、点击跳转。
 */
export default function useQuickCapsule(deps: Deps) {
  const [capsuleSites, setCapsuleSites] = useState<CapsuleSite[]>([]); // 快捷胶囊站点列表
  const requestSeqRef = useRef(0); // 快捷胶囊请求序号,避免旧请求覆盖
  /**
   * 获取用于请求的上车点 ID(仅使用当前选中站点,避免滑动过程误用旧缓存)。
   */
  const resolveRequestStartPointId = () => {
    const currentPointId = deps.currentSite?.id;
    if (!currentPointId) {
      return '';
    }
    return String(currentPointId);
  };
  /**
   * 获取用于跳转的有效上车点。
   */
  const resolveValidStartSite = () => {
    const storageStartSite = Taro.getStorageSync('startSite'); // 缓存上车点
    const candidate = deps.currentSite?.id ? deps.currentSite : storageStartSite;
    if (!candidate?.id) {
      return null;
    }
    return candidate;
  };
  const effectiveStartPointId = useMemo(() => resolveRequestStartPointId(), [deps.currentSite?.id]); // 当前可用于请求的上车点ID
  /**
   * 清空快捷胶囊数据。
   */
  const clearQuickCapsule = () => {
    setCapsuleSites([]);
  };
  /**
   * 归一化快捷胶囊站点结构,兼容不同后端字段。
   */
  const normalizeCapsuleSite = (item: any): CapsuleSite | null => {
    const latitude = Number(item?.latitude ?? item?.lat);
    const longitude = Number(item?.longitude ?? item?.lon);
    const id = item?.id ?? item?.pointId ?? '';
    const name = String(item?.name ?? item?.pointName ?? '').trim();
    if (!id || !name || !Number.isFinite(latitude) || !Number.isFinite(longitude)) {
      return null;
    }
    return {
      ...item,
      id,
      name,
      latitude,
      longitude,
      electronicAreaId: item?.electronicAreaId ?? item?.xpElectronicFenceAreaId ?? '',
      time: Number(item?.time ?? item?.parkingTimeLimit ?? 0)
    };
  };
  /**
   * 拉取快捷胶囊数据(仅登录态)。
   */
  const refreshQuickCapsule = async (startPointId?: string) => {
    const otp = await deps.checkOtp(false);
    if (!otp) {
      clearQuickCapsule();
      return;
    }
    const targetStartPointId = String(startPointId || effectiveStartPointId || '');
    if (!targetStartPointId) {
      clearQuickCapsule();
      return;
    }
    const nextSeq = requestSeqRef.current + 1;
    requestSeqRef.current = nextSeq;
    try {
      const res: any = await getUserQuickCapsule({ startPointId: targetStartPointId });
      if (nextSeq !== requestSeqRef.current) {
        return;
      }
      const rawList = Array.isArray(res?.points) ? res.points : Array.isArray(res) ? res : [];
      const normalizedList = rawList
        .map(normalizeCapsuleSite)
        .filter((item: CapsuleSite | null): item is CapsuleSite => !!item);
      setCapsuleSites(normalizedList);
    } catch (error) {
      if (nextSeq !== requestSeqRef.current) {
        return;
      }
      clearQuickCapsule();
    }
  };
  /**
   * 点击快捷胶囊:写入缓存并跳转到冒泡发单页。
   */
  const handleQuickCapsuleClick = async () => {
    const targetStartSite = resolveValidStartSite();
    if (!targetStartSite) {
      Taro.showToast({ title: '请先选择起点站点', icon: 'none' });
      return;
    }
    if (capsuleSites.length === 0) {
      Taro.showToast({ title: '暂无快捷目的地', icon: 'none' });
      return;
    }
    const targetSite = capsuleSites[0];
    Taro.setStorageSync('endSite', targetSite);
    Taro.setStorageSync('startSite', targetStartSite);
    await Taro.reLaunch({ url: '/robotaxi/pages/begin-route/index' });
  };
  /**
   * 胶囊展示文案。
   */
  const quickCapsuleText = useMemo(() => {
    if (capsuleSites.length === 0) return '';
    return `去:${capsuleSites[0].name}`;
  }, [capsuleSites]);
  // 游客态清空,非游客态拉取
  useEffect(() => {
    if (deps.isGuest) {
      clearQuickCapsule();
      return;
    }
    if (!effectiveStartPointId) {
      clearQuickCapsule();
      return;
    }
    refreshQuickCapsule(effectiveStartPointId);
  }, [deps.isGuest, effectiveStartPointId]);
  return {
    capsuleSites,
    quickCapsuleText,
    showQuickCapsule: !!quickCapsuleText,
    refreshQuickCapsule,
    handleQuickCapsuleClick
  };
}
src/pages/robotaxi/home/index.tsx
@@ -44,6 +44,8 @@
import useOriginAction from './hooks/use-origin-action';
// 首页启动流程编排
import useHomeBootstrap from './hooks/use-home-bootstrap';
// 快捷胶囊编排
import useQuickCapsule from './hooks/use-quick-capsule';
import { DEFAULT_LATITUDE, DEFAULT_LONGITUDE, FIRST_ENTER_KEY } from '../../../robotaxi/constants/map';
import { TripStatus, toTripStatus } from '../../../robotaxi/pages/begin-route/constants/trip-status';
import {
@@ -348,6 +350,16 @@
    defaultLongitude: DEFAULT_LONGITUDE,
    toOrigin
  }); // 首页启动编排
  const {
    quickCapsuleText,
    showQuickCapsule,
    refreshQuickCapsule,
    handleQuickCapsuleClick
  } = useQuickCapsule({
    isGuest: isLogin,
    checkOtp,
    currentSite
  }); // 快捷胶囊编排
  useEffect(() => {
    if (selectedCity && selectedCity.name) {
@@ -367,6 +379,7 @@
  useDidShow(() => {
    bootstrap.onDidShow();
    refreshQuickCapsule();
  });
  const { handleRegionChange, queryPointerByCenter } = useRegionChange({
@@ -543,14 +556,17 @@
                <View className="to-site-dot"></View>
                <View className="to-site-address">
                  <View className="to-site-text">你想去哪?</View>
                  <View
                    onClick={(e) => {
                      e.stopPropagation();
                    }}
                    className="to-site-fast"
                  >
                    {renderTailPreserveText('去:中国南方电网·3号11111三大法师多发点11门')}
                  </View>
                  {showQuickCapsule && (
                    <View
                      onClick={(e) => {
                        e.stopPropagation();
                        handleQuickCapsuleClick();
                      }}
                      className="to-site-fast"
                    >
                      {renderTailPreserveText(quickCapsuleText)}
                    </View>
                  )}
                </View>
              </View>
            </View>
src/robotaxi/api/home.ts
@@ -92,3 +92,12 @@
        method: 'GET'
    }, true);
}
// 获取用户快捷胶囊
export const getUserQuickCapsule = async (payload) => {
    return request({
        url: _ROBOTAXI_HOST + '/order/api/v1/mini-program/order/quick-capsule',
        method: 'GET',
        payload
    }, true);
}