From 814717329099522271622ee62eef015b6a92fba7 Mon Sep 17 00:00:00 2001 From: gujiangjiang Date: Sun, 19 Oct 2025 09:44:27 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=BB=B6=E9=95=BF?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=9C=9F=E5=90=8E=E5=80=92=E8=AE=A1=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E7=AB=8B=E5=8D=B3=E6=9B=B4=E6=96=B0=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/CountdownTimer.tsx | 43 +++++++++++++++------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/CountdownTimer.tsx b/frontend/src/components/CountdownTimer.tsx index 96fb6ec..374c8e0 100644 --- a/frontend/src/components/CountdownTimer.tsx +++ b/frontend/src/components/CountdownTimer.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; // fix: 导入 useCallback import { useTranslation } from 'react-i18next'; import ClockIcon from './icons/Clock'; // 导入时钟图标 import RefreshIcon from './icons/RefreshIcon'; // 导入刷新图标 @@ -17,8 +17,9 @@ const formatTimeUnit = (unit: number): string => { export function CountdownTimer({ expiryTimestamp, onExtend }: CountdownTimerProps) { const { t } = useTranslation(); // 用于国际化 - // 计算剩余时间的函数 - const calculateTimeLeft = () => { + // 计算剩余时间的函数 (使用 useCallback 避免不必要的重新创建) + const calculateTimeLeft = useCallback(() => { + // 直接使用最新的 expiryTimestamp prop const difference = expiryTimestamp - Date.now(); let timeLeft = { hours: '00', @@ -28,34 +29,48 @@ export function CountdownTimer({ expiryTimestamp, onExtend }: CountdownTimerProp }; if (difference > 0) { + const totalSeconds = Math.floor(difference / 1000); + const seconds = totalSeconds % 60; + const totalMinutes = Math.floor(totalSeconds / 60); + const minutes = totalMinutes % 60; + const hours = Math.floor(totalMinutes / 60); // 不再限制 % 24,可以显示超过一天的时间 + timeLeft = { - hours: formatTimeUnit(Math.floor((difference / (1000 * 60 * 60)) % 24)), - minutes: formatTimeUnit(Math.floor((difference / 1000 / 60) % 60)), - seconds: formatTimeUnit(Math.floor((difference / 1000) % 60)), + hours: formatTimeUnit(hours), + minutes: formatTimeUnit(minutes), + seconds: formatTimeUnit(seconds), expired: false, }; } return timeLeft; - }; + }, [expiryTimestamp]); // expiryTimestamp 作为依赖项 // 使用 useState 来存储剩余时间 const [timeLeft, setTimeLeft] = useState(calculateTimeLeft()); // 使用 useEffect 来设置定时器,每秒更新剩余时间 useEffect(() => { - // 如果已经过期,则不设置定时器 - if (timeLeft.expired) { - return; - } + // 立即计算一次以避免初始延迟 + setTimeLeft(calculateTimeLeft()); + // 设置定时器 const timer = setInterval(() => { setTimeLeft(calculateTimeLeft()); }, 1000); - // 组件卸载时清除定时器 + // 组件卸载或 expiryTimestamp 变化时清除定时器 + // fix: 将 expiryTimestamp 添加到依赖项,确保每次时间戳更新时都重置定时器 return () => clearInterval(timer); - }, [expiryTimestamp, timeLeft.expired]); // 依赖项包含过期时间戳和是否过期状态 + }, [expiryTimestamp, calculateTimeLeft]); // 依赖项包含 expiryTimestamp 和 calculateTimeLeft + + // 当计算出的时间显示已过期时,停止计时器(通过清除上一个 effect 实现) + useEffect(() => { + if (timeLeft.expired) { + // 定时器已在上面的 effect 的 cleanup 函数中清除 + // 这里可以额外处理过期逻辑,如果需要的话 + } + }, [timeLeft.expired]); return ( // feat: 将容器改为 flex-row 并添加按钮 @@ -70,7 +85,7 @@ export function CountdownTimer({ expiryTimestamp, onExtend }: CountdownTimerProp // 显示剩余时间 时:分:秒 )} - {/* feat: 添加延长有效期按钮 */} + {/* feat: 添加延长有效期按钮, 仅在未过期时显示 */} {!timeLeft.expired && (