zjk
2026-06-03 c21b92ed591eb855d827c56042f9ab8e4b95c8e0
feat(robotaxi/coupon): 调整优惠券消息提示逻辑并优化列表交互

移除websocket中的COUPON_MESSAGE事件处理代码
将优惠券消息提示改为主动调用新接口获取文案
新增getFirstToastEnter接口获取首页toast提示
为优惠券列表页添加状态引用防止旧请求覆盖数据
优化tab切换逻辑,切换时立即清空列表提升体验
4个文件已修改
86 ■■■■■ 已修改文件
src/pages/robotaxi/home/hooks/use-coupon-message-ws.ts 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/api/coupon.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/coupon/index.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/websocket.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-coupon-message-ws.ts
@@ -1,18 +1,13 @@
import { useEffect, useRef, useState } from 'react';
import wsManager from '../../../../utils/websocket';
type CouponMessagePayload = {
  content?: any;
};
import { getFirstToastEnter } from '../../../../robotaxi/api/coupon';
/**
 * 首页优惠券消息 WebSocket 监听。
 * 首页优惠券消息提示。
 */
export default function useCouponMessageWs() {
  const [couponMessageContent, setCouponMessageContent] = useState(''); // 当前展示的消息文案
  const [showCouponMessage, setShowCouponMessage] = useState(false); // 是否展示消息提示
  const couponMessageTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); // 自动关闭定时器
  const couponMessageBoundRef = useRef(false); // 当前页面是否已绑定消息监听
  const clearCouponMessageTimer = () => {
    if (couponMessageTimerRef.current) {
@@ -21,50 +16,29 @@
    }
  };
  const resolveCouponMessageText = (payload?: CouponMessagePayload) => {
    const rawContent = payload?.content;
    if (typeof rawContent === 'string') {
  useEffect(() => {
    const fetchFirstToast = async () => {
      try {
        const parsedContent = JSON.parse(rawContent);
        if (parsedContent && typeof parsedContent?.content === 'string') {
          return parsedContent.content;
        const res = await getFirstToastEnter();
        if (res && typeof res === 'string' && res.trim()) {
          setCouponMessageContent(res.trim());
          setShowCouponMessage(true);
          clearCouponMessageTimer();
          // 新消息到达时重置倒计时,保证每条消息都完整展示 3 秒
          couponMessageTimerRef.current = setTimeout(() => {
            setShowCouponMessage(false);
            couponMessageTimerRef.current = null;
          }, 3000);
        }
      } catch (error) {
        console.error('获取首页toast提示失败', error);
      }
      return rawContent;
    }
    if (rawContent && typeof rawContent?.content === 'string') {
      return rawContent.content;
    }
    return '';
  };
  useEffect(() => {
    const couponMessageHandler = (payload?: CouponMessagePayload) => {
      const nextContent = resolveCouponMessageText(payload).trim();
      if (!nextContent) {
        return;
      }
      setCouponMessageContent(nextContent);
      setShowCouponMessage(true);
      clearCouponMessageTimer();
      // 新消息到达时重置倒计时,保证每条消息都完整展示 3 秒
      couponMessageTimerRef.current = setTimeout(() => {
        setShowCouponMessage(false);
        couponMessageTimerRef.current = null;
      }, 3000);
    };
    if (!couponMessageBoundRef.current) {
      wsManager.on('COUPON_MESSAGE', couponMessageHandler);
      couponMessageBoundRef.current = true;
    }
    fetchFirstToast();
    return () => {
      clearCouponMessageTimer();
      if (couponMessageBoundRef.current) {
        wsManager.off('COUPON_MESSAGE', couponMessageHandler);
        couponMessageBoundRef.current = false;
      }
    };
  }, []);
src/robotaxi/api/coupon.ts
@@ -74,3 +74,11 @@
        method: 'GET'
    }, true);
}
// 获取首次进入页面的 toast 提示文案
export const getFirstToastEnter = (): Promise<string> => {
    return request({
        url: _ROBOTAXI_HOST + '/operation/api/v1/mini-program/acticity/firstToastEnter',
        method: 'GET'
    }, true);
}
src/robotaxi/pages/coupon/index.tsx
@@ -1,5 +1,5 @@
import Taro, { useReachBottom } from "@tarojs/taro";
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { View, Text, Image, RichText, ScrollView } from "@tarojs/components";
import "./index.less";
import {
@@ -20,6 +20,7 @@
    { label: "已失效", value: 3 }
  ]);
  const [activeStatus, setActiveStatus] = useState<string | number>(1);
  const activeStatusRef = useRef<string | number>(1);
  const [couponList, setCouponList] = useState<CouponItem[]>([]);
  const [animationData, setAnimationData] = useState<any>(null);
  const [visible, setVisible] = useState(false);
@@ -35,6 +36,9 @@
  ) => {
    try {
      const res = await getCouponList({ pageNum: page, pageSize, status });
      if (status !== activeStatusRef.current) {
        return; // 防止切换 tab 后,上一个 tab 的请求结果覆盖当前 tab 的数据
      }
      const newList = res.list || [];
      if (isRefresh) {
        setCouponList(newList);
@@ -45,6 +49,9 @@
      setHasMore(newList.length >= pageSize);
    } catch (e) {
      console.error(e);
      if (status !== activeStatusRef.current) {
        return;
      }
      // 出错时如果是刷新,清空列表
      if (isRefresh) {
        setCouponList([]);
@@ -107,7 +114,10 @@
  });
  const changeStatus = (value: string | number) => {
    if (value === activeStatus) return;
    activeStatusRef.current = value;
    setActiveStatus(value);
    setCouponList([]); // 立即清空列表,提升交互体验
  };
  const openPopup = async (item: CouponItem) => {
src/utils/websocket.ts
@@ -854,12 +854,6 @@
      const status = Number(content?.status);
      this.notifyListeners('update_end_address_status', { status, data, content });
    }
    if (data?.type === 'COUPON_MESSAGE') {
      this.notifyListeners('COUPON_MESSAGE', {
        data,
        content: data?.content
      });
    }
  }
  /**