]> acesimba.cloud Git - words.git/commitdiff
学习模式: 朗读期间禁止进入闪记模式
authorroot <root@localhost.localdomain>
Fri, 26 Jun 2026 14:40:41 +0000 (22:40 +0800)
committerroot <root@localhost.localdomain>
Fri, 26 Jun 2026 14:40:41 +0000 (22:40 +0800)
- 新增 isSpeaking 状态,朗读期间为 true
- 键盘 Enter 监听在 isSpeaking 时直接 return
- 进入闪记按钮 disabled={isSpeaking},文案切换为朗读中,请稍候...
- 两遍音频全部播完(或播放失败)才 setIsSpeaking(false)

frontend/src/components/StudyMode.js

index 75c69adbb4c473466674e71d4a20b57f7829c6d2..1edaa74cb1ef8f9c9b6d6bed70f8032b12a72bbb 100644 (file)
@@ -13,21 +13,32 @@ function StudyMode() {
     const [phase, setPhase] = useState('learning'); // learning, flashcard, correction
     const [userInput, setUserInput] = useState("");
     const [correctionInput, setCorrectionInput] = useState("");
+    const [isSpeaking, setIsSpeaking] = useState(false);
 
     const hasSpokenRef = useRef(null);
     const transitioningRef = useRef(false);
     const currentWord = words[currentIndex];
 
     // 朗读逻辑:通过后端 TTS 代理(有道美式发音)播放两遍,间隔 500ms
+    // 朗读期间 isSpeaking=true,阻止进入闪记模式
     const speak = useCallback((text) => {
         if (!text) return;
         const url = `${API_BASE}/api/tts/${encodeURIComponent(text)}`;
+        setIsSpeaking(true);
+        const finish = () => setIsSpeaking(false);
         const audio1 = new Audio(url);
-        audio1.play().catch(err => console.error("TTS play failed:", err));
+        audio1.play().catch(err => {
+            console.error("TTS play failed:", err);
+            finish();
+        });
         audio1.onended = () => {
             setTimeout(() => {
                 const audio2 = new Audio(url);
-                audio2.play().catch(err => console.error("TTS play failed:", err));
+                audio2.play().catch(err => {
+                    console.error("TTS play failed:", err);
+                    finish();
+                });
+                audio2.onended = finish;
             }, 500);
         };
     }, []);
@@ -47,17 +58,18 @@ function StudyMode() {
         }
     }, [currentIndex, currentWord, phase, speak]);
 
-    // 全局键盘监听:在学习模式下按回车进入闪记
+    // 全局键盘监听:在学习模式下按回车进入闪记(朗读中禁用)
     useEffect(() => {
         const handleGlobalKeyDown = (event) => {
             if (transitioningRef.current) return;
+            if (isSpeaking) return;
             if (phase === 'learning' && event.key === 'Enter') {
                 setPhase('flashcard');
             }
         };
         window.addEventListener('keydown', handleGlobalKeyDown);
         return () => window.removeEventListener('keydown', handleGlobalKeyDown);
-    }, [phase]);
+    }, [phase, isSpeaking]);
 
     const handleNext = async () => {
         transitioningRef.current = true;
@@ -145,8 +157,12 @@ function StudyMode() {
                             <div className="details-anim">
                                 {currentWord.word_root && <p className="root"><strong>词根:</strong> {currentWord.word_root}</p>}
                                 {currentWord.affix && <p className="affix"><strong>组成:</strong> {currentWord.affix}</p>}
-                                <button className="action-btn" onClick={() => setPhase('flashcard')}>
-                                    进入闪记模式 (Enter)
+                                <button
+                                    className="action-btn"
+                                    onClick={() => setPhase('flashcard')}
+                                    disabled={isSpeaking}
+                                >
+                                    {isSpeaking ? "朗读中,请稍候..." : "进入闪记模式 (Enter)"}
                                 </button>
                             </div>
                         </div>