From 5d97c70e06879ddcf7b495d540a78aebbb46a99a Mon Sep 17 00:00:00 2001 From: ittoview Date: Mon, 2 Mar 2026 01:34:31 +0000 Subject: [PATCH] =?UTF-8?q?fix(=E7=BB=83=E4=B9=A0):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95=E7=BB=84=E5=90=88=E6=9C=9F=E9=97=B4?= =?UTF-8?q?=E7=84=A6=E7=82=B9=E8=B7=B3=E8=BD=AC=E5=AF=BC=E8=87=B4=E5=AD=97?= =?UTF-8?q?=E6=AF=8D=E5=88=86=E6=95=A3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 组合输入期间禁止useEffect自动聚焦到下一个空输入框 - onCompositionEnd直接传入index和value,不再扫描数组查找 - 确保拼音字母留在同一输入框,确认后正确分散成中文 via [HAPI](https://hapi.run) Co-Authored-By: HAPI --- src/components/practice/InputArea.tsx | 13 +++++--- src/pages/ProcessPracticePage.tsx | 46 ++++++++++++++------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/components/practice/InputArea.tsx b/src/components/practice/InputArea.tsx index 15b5af8..33f4543 100644 --- a/src/components/practice/InputArea.tsx +++ b/src/components/practice/InputArea.tsx @@ -11,8 +11,8 @@ interface InputAreaProps { inputLocked: boolean lastErrorTimestamp: number | null onInputChange: (newInput: string[]) => void - onCompositionStart: () => void - onCompositionEnd: () => void + onCompositionStart: (index: number) => void + onCompositionEnd: (index: number, value: string) => void onPaste: (e: React.ClipboardEvent) => void } @@ -31,11 +31,12 @@ export function InputArea({ // 自动聚焦到第一个空输入框 useEffect(() => { + if (isComposing || inputLocked) return const firstEmptyIndex = userInput.findIndex((char) => !char) if (firstEmptyIndex !== -1 && inputRefs.current[firstEmptyIndex]) { inputRefs.current[firstEmptyIndex]?.focus() } - }, [userInput]) + }, [userInput, isComposing, inputLocked]) const handleCharInput = ( index: number, @@ -137,8 +138,10 @@ export function InputArea({ value={char} onChange={(e) => handleCharInput(index, e)} onKeyDown={(e) => handleKeyDown(index, e)} - onCompositionStart={onCompositionStart} - onCompositionEnd={onCompositionEnd} + onCompositionStart={() => onCompositionStart(index)} + onCompositionEnd={(e) => + onCompositionEnd(index, e.currentTarget.value) + } onPaste={onPaste} disabled={inputLocked} className={clsx( diff --git a/src/pages/ProcessPracticePage.tsx b/src/pages/ProcessPracticePage.tsx index 9e260d2..89d062a 100644 --- a/src/pages/ProcessPracticePage.tsx +++ b/src/pages/ProcessPracticePage.tsx @@ -207,39 +207,41 @@ export default function ProcessPracticePage() { ) // 输入法状态管理 - const handleCompositionStart = useCallback(() => { + const handleCompositionStart = useCallback((_index: number) => { isComposingRef.current = true setIsComposing(true) }, []) - const handleCompositionEnd = useCallback(() => { - isComposingRef.current = false - setIsComposing(false) - // 输入法确认后,需要将当前输入框中的所有字符分散到后续输入框 - requestAnimationFrame(() => { - const currentInput = latestInputRef.current - // 找到第一个有内容且长度>1的输入框(输入法组合的结果) - const composedIndex = currentInput.findIndex((char) => char.length > 1) - - if (composedIndex !== -1) { - const composedText = currentInput[composedIndex] - const chars = composedText.split('') + const handleCompositionEnd = useCallback( + (index: number, value: string) => { + isComposingRef.current = false + setIsComposing(false) + // 输入法确认后,需要将当前输入框中的所有字符分散到后续输入框 + requestAnimationFrame(() => { + const currentInput = latestInputRef.current + const composedText = value const newInput = [...currentInput] - // 将组合的字符分散到后续输入框 - for (let i = 0; i < chars.length && composedIndex + i < newInput.length; i++) { - newInput[composedIndex + i] = chars[i] + if (composedText) { + const chars = composedText.split('') + for ( + let i = 0; + i < chars.length && index + i < newInput.length; + i++ + ) { + newInput[index + i] = chars[i] + } + } else { + newInput[index] = '' } latestInputRef.current = newInput setUserInput(newInput) validateInput(newInput) - } else { - // 没有多字符组合,直接验证当前输入 - validateInput(currentInput) - } - }) - }, [validateInput]) + }) + }, + [validateInput] + ) // 批量粘贴处理 const handlePaste = useCallback(