From a16041cd67dd7a561194b08d2e0e6bfd8b2a2905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sun, 31 May 2026 13:53:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AE=A1=E6=97=B6=E5=99=A8=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=8F=8C=E6=96=B9=E7=8B=AC=E7=AB=8B=E8=B1=A1=E6=A3=8B?= =?UTF-8?q?=E9=92=9F=20+=20=E8=B6=85=E6=97=B6=E8=87=AA=E5=8A=A8=E5=88=A4?= =?UTF-8?q?=E8=B4=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- src/components/game/TimerDisplay.tsx | 76 ++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/src/components/game/TimerDisplay.tsx b/src/components/game/TimerDisplay.tsx index c384d09..47988a2 100644 --- a/src/components/game/TimerDisplay.tsx +++ b/src/components/game/TimerDisplay.tsx @@ -1,29 +1,85 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef, useCallback } from 'react'; import { useGameStore } from '../../store/gameStore'; +import { invoke } from '@tauri-apps/api/core'; export default function TimerDisplay() { const config = useGameStore((s) => s.config); const currentColor = useGameStore((s) => s.currentColor); const status = useGameStore((s) => s.status); - const [time, setTime] = useState(config.timeLimitSecs); + const refreshBoard = useGameStore((s) => s.refreshBoard); + + const [blackTime, setBlackTime] = useState(config.timeLimitSecs); + const [whiteTime, setWhiteTime] = useState(config.timeLimitSecs); + const lastColorRef = useRef(currentColor); + const hasTimedOutRef = useRef(false); + + // 初始化/重置时钟 + useEffect(() => { + setBlackTime(config.timeLimitSecs); + setWhiteTime(config.timeLimitSecs); + hasTimedOutRef.current = false; + lastColorRef.current = 'Black'; + }, [config.timeLimitSecs, status === 'waiting' ? status : null]); + + const handleTimeout = useCallback(async () => { + if (hasTimedOutRef.current) return; + hasTimedOutRef.current = true; + try { + await invoke('resign'); + await refreshBoard(); + } catch { + // 忽略错误 + } + }, [refreshBoard]); useEffect(() => { if (!config.useTimer || status !== 'playing') return; - setTime(config.timeLimitSecs); + const timer = setInterval(() => { - setTime((t) => { - if (t <= 1) { clearInterval(timer); return 0; } - return t - 1; - }); + if (currentColor === 'Black') { + setBlackTime((t) => { + if (t <= 1) { + clearInterval(timer); + handleTimeout(); + return 0; + } + return t - 1; + }); + } else { + setWhiteTime((t) => { + if (t <= 1) { + clearInterval(timer); + handleTimeout(); + return 0; + } + return t - 1; + }); + } }, 1000); + + lastColorRef.current = currentColor; + return () => clearInterval(timer); - }, [currentColor, config.useTimer, config.timeLimitSecs, status]); + }, [currentColor, config.useTimer, status, handleTimeout]); if (!config.useTimer) return null; + const displayTime = currentColor === 'Black' ? blackTime : whiteTime; + const isWarning = displayTime <= 10; + return ( -
- {Math.floor(time / 60)}:{(time % 60).toString().padStart(2, '0')} +
+
+ {Math.floor(displayTime / 60)}:{(displayTime % 60).toString().padStart(2, '0')} +
+
+ + 黑: {Math.floor(blackTime / 60)}:{String(blackTime % 60).padStart(2, '0')} + + + 白: {Math.floor(whiteTime / 60)}:{String(whiteTime % 60).padStart(2, '0')} + +
); }