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(