fix(练习): 修复中文输入法和字符验证问题
- 移除 input maxLength 限制,支持输入法多字符输入 - 使用 ref 保存最新输入状态,避免闭包导致的状态滞后 - 重构验证逻辑,修复字符对比错误(对比原始答案而非标准化答案) - 修复输入法确认后验证使用旧数据的问题 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -134,7 +134,6 @@ export function InputArea({
|
||||
!isComposing && isError && 'border-red-500',
|
||||
inputLocked && 'cursor-not-allowed opacity-50'
|
||||
)}
|
||||
maxLength={1}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import {
|
||||
generateCellSequence,
|
||||
@@ -39,6 +39,7 @@ export default function ProcessPracticePage() {
|
||||
expiresAt: number
|
||||
} | null>(null)
|
||||
const [inputLocked, setInputLocked] = useState(false)
|
||||
const latestInputRef = useRef<string[]>([])
|
||||
|
||||
// 初始化输入框
|
||||
useEffect(() => {
|
||||
@@ -49,6 +50,11 @@ export default function ProcessPracticePage() {
|
||||
}
|
||||
}, [currentCellId, cellSequence])
|
||||
|
||||
// 同步当前输入快照,供输入法确认后复用
|
||||
useEffect(() => {
|
||||
latestInputRef.current = userInput
|
||||
}, [userInput])
|
||||
|
||||
// 切换到指定格子
|
||||
const switchToCell = useCallback(
|
||||
(cell: CellInfo) => {
|
||||
@@ -94,55 +100,57 @@ export default function ProcessPracticePage() {
|
||||
switchToCell(prevCell)
|
||||
}, [currentCellId, cellSequence, switchToCell])
|
||||
|
||||
// 输入验证
|
||||
const handleInputChange = useCallback(
|
||||
(newInput: string[]) => {
|
||||
setUserInput(newInput)
|
||||
|
||||
// 等待输入法确认后再验证
|
||||
if (isComposing) return
|
||||
|
||||
// 统一的输入验证逻辑
|
||||
const validateInput = useCallback(
|
||||
(input: string[]) => {
|
||||
const currentCell = cellSequence.find((c) => c.id === currentCellId)
|
||||
if (!currentCell || !currentCellId) return
|
||||
|
||||
// 使用原始答案长度渲染横线,使用标准化答案验证
|
||||
const originalAnswer = currentCell.answer
|
||||
const normalizedInput = normalizeAnswer(
|
||||
newInput.join(''),
|
||||
input.join(''),
|
||||
currentCell.type === 'knowledge-area'
|
||||
)
|
||||
const normalizedAnswer = currentCell.normalizedAnswer
|
||||
const normalizeChar = (char: string) =>
|
||||
normalizeAnswer(char, false) || char
|
||||
|
||||
// 逐字符验证状态(基于原始答案)
|
||||
const newCharStatuses = newInput.map((char, i) => {
|
||||
const newCharStatuses = input.map((char, i) => {
|
||||
if (!char) return 'pending' as CharStatus
|
||||
// 对比时使用标准化后的字符
|
||||
const normalizedChar = normalizeAnswer(char, false)
|
||||
const expectedChar = normalizedAnswer[i]
|
||||
return normalizedChar === expectedChar
|
||||
const expectedChar = originalAnswer[i] || ''
|
||||
if (!expectedChar) return 'error' as CharStatus
|
||||
return normalizeChar(char) === normalizeChar(expectedChar)
|
||||
? ('correct' as CharStatus)
|
||||
: ('error' as CharStatus)
|
||||
})
|
||||
setCharStatuses(newCharStatuses)
|
||||
|
||||
// 完整答案验证
|
||||
const isComplete =
|
||||
newInput.every((c) => c !== '') &&
|
||||
newInput.length === originalAnswer.length
|
||||
input.every((c) => c !== '') && input.length === originalAnswer.length
|
||||
const isCorrect = isComplete && normalizedInput === normalizedAnswer
|
||||
|
||||
if (isCorrect) {
|
||||
// 答对:标记格子,延迟跳转(给用户反馈时间)
|
||||
setAnsweredCells((prev) => new Map(prev).set(currentCellId, true))
|
||||
setTimeout(() => {
|
||||
moveToNextCell()
|
||||
}, 300)
|
||||
} else if (isComplete) {
|
||||
// 答错:记录错误时间戳,触发红线动画
|
||||
setLastErrorTimestamp(Date.now())
|
||||
}
|
||||
},
|
||||
[isComposing, currentCellId, cellSequence, moveToNextCell]
|
||||
[cellSequence, currentCellId, moveToNextCell]
|
||||
)
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(newInput: string[]) => {
|
||||
latestInputRef.current = newInput
|
||||
setUserInput(newInput)
|
||||
|
||||
if (isComposing) return
|
||||
|
||||
validateInput(newInput)
|
||||
},
|
||||
[isComposing, validateInput]
|
||||
)
|
||||
|
||||
// 输入法状态管理
|
||||
@@ -152,11 +160,11 @@ export default function ProcessPracticePage() {
|
||||
|
||||
const handleCompositionEnd = useCallback(() => {
|
||||
setIsComposing(false)
|
||||
// 输入法确认后立即验证(使用当前完整输入)
|
||||
// 输入法确认后根据最新快照重新验证
|
||||
requestAnimationFrame(() => {
|
||||
handleInputChange(userInput)
|
||||
validateInput(latestInputRef.current)
|
||||
})
|
||||
}, [userInput, handleInputChange])
|
||||
}, [validateInput])
|
||||
|
||||
// 批量粘贴处理
|
||||
const handlePaste = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user