From 2e7f43becff609d24da9ac193d057cef976eecb7 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Sun, 22 Mar 2026 15:23:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E5=B1=80=E9=94=99=E8=AF=AF=E6=A1=86?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=B6=88=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/GlobalErrorIndicator.tsx | 74 +++++++++++++++++++++---- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/src/components/GlobalErrorIndicator.tsx b/src/components/GlobalErrorIndicator.tsx index f05bb67..d0e5d14 100644 --- a/src/components/GlobalErrorIndicator.tsx +++ b/src/components/GlobalErrorIndicator.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; interface ErrorInfo { id: string; @@ -12,6 +12,42 @@ export function GlobalErrorIndicator() { const [currentError, setCurrentError] = useState(null); const [isVisible, setIsVisible] = useState(false); const [isReplacing, setIsReplacing] = useState(false); + const [isClosing, setIsClosing] = useState(false); + const currentErrorRef = useRef(null); + const closeTimerRef = useRef(null); + const exitTimerRef = useRef(null); + + const clearCloseTimer = useCallback(() => { + if (closeTimerRef.current !== null) { + window.clearTimeout(closeTimerRef.current); + closeTimerRef.current = null; + } + }, []); + + const clearExitTimer = useCallback(() => { + if (exitTimerRef.current !== null) { + window.clearTimeout(exitTimerRef.current); + exitTimerRef.current = null; + } + }, []); + + const handleClose = useCallback(() => { + clearCloseTimer(); + clearExitTimer(); + setIsClosing(true); + setIsReplacing(false); + + exitTimerRef.current = window.setTimeout(() => { + setIsVisible(false); + setCurrentError(null); + setIsClosing(false); + exitTimerRef.current = null; + }, 300); + }, [clearCloseTimer, clearExitTimer]); + + useEffect(() => { + currentErrorRef.current = currentError; + }, [currentError]); useEffect(() => { // 监听自定义错误事件 @@ -23,8 +59,13 @@ export function GlobalErrorIndicator() { timestamp: Date.now(), }; + clearCloseTimer(); + clearExitTimer(); + setIsClosing(false); + setIsVisible(true); + // 如果已有错误,开始替换动画 - if (currentError) { + if (currentErrorRef.current) { setCurrentError(newError); setIsReplacing(true); @@ -36,23 +77,32 @@ export function GlobalErrorIndicator() { // 第一次显示错误 setCurrentError(newError); } - - setIsVisible(true); }; // 监听错误事件 window.addEventListener('globalError', handleError as EventListener); return () => { + clearCloseTimer(); + clearExitTimer(); window.removeEventListener('globalError', handleError as EventListener); }; - }, [currentError]); + }, [clearCloseTimer, clearExitTimer]); - const handleClose = () => { - setIsVisible(false); - setCurrentError(null); - setIsReplacing(false); - }; + useEffect(() => { + if (!currentError || isClosing) { + return; + } + + clearCloseTimer(); + closeTimerRef.current = window.setTimeout(() => { + handleClose(); + }, 5000); + + return () => { + clearCloseTimer(); + }; + }, [currentError, handleClose, isClosing, clearCloseTimer]); if (!isVisible || !currentError) { return null; @@ -63,6 +113,10 @@ export function GlobalErrorIndicator() { {/* 错误卡片 */}