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);
};
}, []);
}
}, [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;
<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>