fix(过程详情): 恢复练习模式查看答案快捷键

This commit is contained in:
ittoview
2026-03-24 05:49:41 +00:00
parent d9bbd28da5
commit 566eb3492f
3 changed files with 69 additions and 47 deletions

View File

@@ -7,7 +7,6 @@ interface ProficientInputAreaProps {
isComposing: boolean
inputLocked: boolean
hasError: boolean
statusText?: string | null
onChange: (value: string) => void
onCompositionStart: () => void
onCompositionEnd: (value: string) => void
@@ -18,7 +17,6 @@ export function ProficientInputArea({
isComposing,
inputLocked,
hasError,
statusText,
onChange,
onCompositionStart,
onCompositionEnd,
@@ -40,7 +38,7 @@ export function ProficientInputArea({
onCompositionStart={onCompositionStart}
onCompositionEnd={(e) => onCompositionEnd(e.currentTarget.value)}
disabled={inputLocked}
placeholder="输入当前分组中的一个完整条目,停顿 800ms 自动核对"
placeholder="输入当前分组中的一个完整条目"
className={clsx(
'w-full rounded-xl border px-4 py-3 text-base md:text-lg',
'bg-white dark:bg-gray-800/80 text-gray-900 dark:text-gray-100',
@@ -56,33 +54,18 @@ export function ProficientInputArea({
spellCheck="false"
/>
<div className="min-h-6 text-sm">
<AnimatePresence mode="wait">
{inputLocked ? (
<motion.div
key="locked"
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -6 }}
className="text-gray-500 dark:text-gray-400"
>
</motion.div>
) : statusText ? (
<motion.div
key={statusText}
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -6 }}
className={clsx(
hasError ? 'text-red-500 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'
)}
>
{statusText}
</motion.div>
) : null}
</AnimatePresence>
</div>
<AnimatePresence>
{inputLocked && (
<motion.div
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -6 }}
className="text-sm text-gray-500 dark:text-gray-400"
>
</motion.div>
)}
</AnimatePresence>
</div>
)
}

View File

@@ -1,5 +1,19 @@
{
"changelogEntries": [
{
"id": "2026-03-24-process-detail-shortcut-restore",
"date": "2026-03-24",
"type": "fix",
"title": "恢复过程详情练习模式的 Ctrl+H 查看答案快捷键",
"scope": "过程详情"
},
{
"id": "2026-03-24-proficient-mode-status-cleanup",
"date": "2026-03-24",
"type": "fix",
"title": "移除熟练模式底部多余说明与命中提示,精简输入区反馈",
"scope": "过程详情"
},
{
"id": "2026-03-23-process-detail-proficient-mode",
"date": "2026-03-23",

View File

@@ -169,7 +169,6 @@ export function ProcessDetailPage() {
const [currentSection, setCurrentSection] = useState<IttoSection>('inputs')
const [proficientInput, setProficientInput] = useState('')
const [proficientHasError, setProficientHasError] = useState(false)
const [proficientStatusText, setProficientStatusText] = useState<string | null>(null)
const [inputLocked, setInputLocked] = useState(false)
const [lastErrorTimestamp, setLastErrorTimestamp] = useState<number | null>(null)
@@ -253,7 +252,6 @@ export function ProcessDetailPage() {
setCurrentSection(getFirstAvailableSection(itemsBySection))
setProficientInput('')
setProficientHasError(false)
setProficientStatusText(null)
setLastErrorTimestamp(null)
setShowAnswer(false)
setInputLocked(false)
@@ -292,12 +290,10 @@ export function ProcessDetailPage() {
setCurrentPracticeId(null)
setProficientInput('')
setProficientHasError(false)
setProficientStatusText('当前分组内可乱序输入,停顿 800ms 自动核对。')
} else {
setCurrentPracticeId(practiceItems[0].id)
setProficientInput('')
setProficientHasError(false)
setProficientStatusText(null)
}
}, [
practiceItems,
@@ -326,6 +322,48 @@ export function ProcessDetailPage() {
exitPractice()
}, [id, practiceMode, exitPractice])
useEffect(() => {
if (!isPracticeMode) return
const handleKeyDown = (event: KeyboardEvent) => {
if (event.ctrlKey && event.key.toLowerCase() === 'h') {
event.preventDefault()
clearLongPressTimer()
clearDebounceTimer()
clearAnswerRevealTimer()
if (practiceMode === 'proficient') {
setProficientInput('')
setProficientHasError(false)
}
setShowAnswer(true)
setInputLocked(true)
}
}
const handleKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Control' || event.key.toLowerCase() === 'h') {
hideAnswerAndUnlock()
}
}
window.addEventListener('keydown', handleKeyDown)
window.addEventListener('keyup', handleKeyUp)
return () => {
window.removeEventListener('keydown', handleKeyDown)
window.removeEventListener('keyup', handleKeyUp)
}
}, [
isPracticeMode,
practiceMode,
hideAnswerAndUnlock,
clearLongPressTimer,
clearDebounceTimer,
clearAnswerRevealTimer,
])
const revealAnswersForThreeSeconds = useCallback(() => {
if (!isPracticeMode) return
clearLongPressTimer()
@@ -464,7 +502,6 @@ export function ProcessDetailPage() {
if (!matchedItem) {
setProficientHasError(true)
setProficientStatusText(`未匹配到当前${SECTION_LABELS[currentSection]}分组中的条目,请继续修改。`)
return
}
@@ -478,7 +515,6 @@ export function ProcessDetailPage() {
const allDone = practiceItems.every((item) => nextAnsweredItems.has(item.id))
if (allDone) {
setProficientStatusText('全部条目已完成,正在结束练习。')
finishPractice()
return
}
@@ -487,15 +523,9 @@ export function ProcessDetailPage() {
const nextSection = getNextSection(currentSection, itemsBySection, nextAnsweredItems)
if (nextSection) {
setCurrentSection(nextSection)
setProficientStatusText(`当前${SECTION_LABELS[currentSection]}已完成,已切换到${SECTION_LABELS[nextSection]}`)
} else {
setProficientStatusText(`当前${SECTION_LABELS[currentSection]}已完成。`)
}
return
}
const doneCount = itemsBySection[currentSection].filter((item) => nextAnsweredItems.has(item.id)).length
setProficientStatusText(`已命中:${matchedItem.name}${SECTION_LABELS[currentSection]} ${doneCount}/${itemsBySection[currentSection].length}`)
}, [answeredItems, currentSection, inputLocked, itemsBySection, practiceItems, finishPractice])
useEffect(() => {
@@ -682,7 +712,6 @@ export function ProcessDetailPage() {
setCurrentSection(section)
setProficientInput('')
setProficientHasError(false)
setProficientStatusText(`已切换到${SECTION_LABELS[section]}分组。`)
}}
className={`flex items-center gap-2 text-left ${canSwitchSection ? 'cursor-pointer' : 'cursor-default'}`}
>
@@ -794,13 +823,9 @@ export function ProcessDetailPage() {
isComposing={isComposing}
inputLocked={inputLocked}
hasError={proficientHasError}
statusText={proficientStatusText}
onChange={(value) => {
setProficientInput(value)
setProficientHasError(false)
if (!value) {
setProficientStatusText('当前分组内可乱序输入,停顿 800ms 自动核对。')
}
}}
onCompositionStart={() => handleCompositionStart()}
onCompositionEnd={(value) => handleCompositionEnd(value)}