修复填空复习功能2.2

This commit is contained in:
taiyi 2026-04-27 18:44:21 +08:00
parent 9da98e9c82
commit 44ac502d5b
3 changed files with 140 additions and 16 deletions

View File

@ -37,6 +37,7 @@ Page({
currentClozeResults: [],
showClozeFeedback: false,
clozeSegments: [],
activeClozeBlankIndex: 0,
},
submitting: false,
judged: false,
@ -65,32 +66,63 @@ Page({
buildClozeSegments(clozeQuestion) {
if (!clozeQuestion || !Array.isArray(clozeQuestion.blanks)) return []
const prompt = clozeQuestion.prompt_text || ''
const blanks = clozeQuestion.blanks || []
const segments = []
let blankIndex = 0
if (prompt.includes('____')) {
prompt.split('____').forEach((text, index, parts) => {
const useUnderscorePrompt = prompt.includes('____')
const workingPrompt = useUnderscorePrompt ? prompt : (clozeQuestion.original_text || prompt)
const pushWordBlank = (blankIndex, blank) => {
const answerText = String(blank?.answer || '')
const answerLength = Math.max(1, answerText.length || Number(blank?.hint?.length || 1))
segments.push({
type: 'word-blank',
blankIndex,
index: `blank-${blankIndex}`,
width: `${Math.max(56, Math.min(260, 26 + answerLength * 24))}rpx`,
minWidth: `${Math.max(56, Math.min(260, 26 + answerLength * 24))}rpx`,
maxLength: Math.max(1, answerLength),
})
}
if (useUnderscorePrompt) {
let blankIndex = 0
workingPrompt.split('____').forEach((text, index, parts) => {
if (text) segments.push({ type: 'text', text, index: `text-${index}` })
if (index < parts.length - 1) {
segments.push({ type: 'blank', blankIndex, index: `blank-${blankIndex}` })
pushWordBlank(blankIndex, blanks[blankIndex])
blankIndex += 1
}
})
return segments
}
const blanks = clozeQuestion.blanks || []
if (blanks.length === 1 && workingPrompt === (clozeQuestion.original_text || prompt)) {
const blank = blanks[0]
const answerText = String(blank?.answer || '')
const letterSegments = answerText.split('').map((char, idx) => ({
type: 'letter-cell',
blankIndex: 0,
letterIndex: idx,
index: `blank-0-letter-${idx}`,
width: '42rpx',
minWidth: '42rpx',
maxLength: 1,
}))
return letterSegments.length ? letterSegments : [{ type: 'word-blank', blankIndex: 0, index: 'blank-0', width: '80rpx', minWidth: '80rpx', maxLength: 1 }]
}
let cursor = 0
blanks.forEach((blank, idx) => {
const start = Math.max(0, Number(blank.start || 0))
const end = Math.max(start, Number(blank.end || start + 1))
if (start > cursor) {
segments.push({ type: 'text', text: prompt.slice(cursor, start), index: `text-${idx}` })
segments.push({ type: 'text', text: workingPrompt.slice(cursor, start), index: `text-${idx}` })
}
segments.push({ type: 'blank', blankIndex: idx, index: `blank-${idx}` })
pushWordBlank(idx, blank)
cursor = end
})
if (cursor < prompt.length) {
segments.push({ type: 'text', text: prompt.slice(cursor), index: `text-tail` })
if (cursor < workingPrompt.length) {
segments.push({ type: 'text', text: workingPrompt.slice(cursor), index: `text-tail` })
}
return segments
},
@ -159,6 +191,7 @@ Page({
meaning: isCloze ? (current?.answer_hint || '') : '',
answer: session.currentAnswer || '',
clozeAnswers: isCloze ? (Array.isArray(session.currentAnswers) ? session.currentAnswers : Array.from({ length: currentClozeQuestion?.blanks?.length || 0 }, () => '')) : [],
activeClozeBlankIndex: 0,
feedback: '',
revealAnswer: '',
feedbackType: '',
@ -179,20 +212,105 @@ Page({
app.globalData.reviewSession.currentAnswer = answer
this.setData({ answer, showFeedback: false, revealAnswer: '', feedback: '', feedbackType: '', judged: false, showClozeFeedback: false })
},
onClozeAnswerInput(e) {
const { index } = e.currentTarget.dataset
const value = e.detail.value
onClozeLetterInput(e) {
const { index, letterIndex } = e.currentTarget.dataset
const rawValue = e.detail.value || ''
const cleaned = rawValue.slice(-1)
const answers = [...(app.globalData.reviewSession.currentAnswers || [])]
answers[index] = value
const current = answers[index] || ''
const chars = current.split('')
chars[letterIndex] = cleaned
answers[index] = chars.join('').slice(0, Math.max(chars.length, letterIndex + 1))
app.globalData.reviewSession.currentAnswers = answers
this.setData({
[`clozeAnswers[${index}]`]: value,
[`clozeAnswers[${index}]`]: answers[index],
showFeedback: false,
revealAnswer: '',
feedback: '',
feedbackType: '',
judged: false,
showClozeFeedback: false,
}, () => {
if (cleaned) this.focusNextLetterCell(Number(index), Number(letterIndex) + 1)
})
},
focusNextLetterCell(blankIndex, letterIndex) {
const selector = `#cloze-input-${blankIndex}-${letterIndex}`
wx.nextTick(() => {
const query = wx.createSelectorQuery().in(this)
query.select(selector).fields({ node: true }).exec((res) => {
if (!res || !res[0]) return
const node = res[0].node
if (node && typeof node.focus === 'function') node.focus()
})
})
},
handleLetterKeydown(e) {
const key = e?.detail?.key || e?.detail?.value || ''
const { index, letterIndex } = e.currentTarget.dataset
if (key === 'Backspace' || key === 'Delete') {
const answers = [...(app.globalData.reviewSession.currentAnswers || [])]
const current = answers[index] || ''
const chars = current.split('')
const pos = Number(letterIndex)
if (chars[pos]) {
chars[pos] = ''
answers[index] = chars.join('')
app.globalData.reviewSession.currentAnswers = answers
this.setData({ [`clozeAnswers[${index}]`]: answers[index] })
return
}
const prevIndex = Number(letterIndex) - 1
if (prevIndex >= 0) this.focusNextLetterCell(Number(index), prevIndex)
return
}
if (key === 'ArrowLeft') {
const prevIndex = Number(letterIndex) - 1
if (prevIndex >= 0) this.focusNextLetterCell(Number(index), prevIndex)
return
}
if (key === 'ArrowRight' || key === ' ' || key === 'Space') {
this.focusNextLetterCell(Number(index), Number(letterIndex) + 1)
}
},
onClozeAnswerInput(e) {
const { index } = e.currentTarget.dataset
const rawValue = e.detail.value || ''
const clozeQuestion = this.data.currentClozeQuestion
const blank = clozeQuestion?.blanks?.[index]
const maxLength = Math.max(1, Number(blank?.answer?.length || 1))
const normalizedValue = rawValue.slice(-maxLength).slice(0, maxLength)
const answers = [...(app.globalData.reviewSession.currentAnswers || [])]
answers[index] = normalizedValue
app.globalData.reviewSession.currentAnswers = answers
const nextIndex = Number(index) + (normalizedValue.length >= maxLength ? 1 : 0)
const prevIndex = Number(index) - 1
this.setData({
[`clozeAnswers[${index}]`]: normalizedValue,
activeClozeBlankIndex: nextIndex,
showFeedback: false,
revealAnswer: '',
feedback: '',
feedbackType: '',
judged: false,
showClozeFeedback: false,
}, () => {
if (!normalizedValue && this.data.isCloze && rawValue === '') {
this.focusNextClozeInput(Math.max(0, prevIndex))
return
}
if (normalizedValue.length >= maxLength && this.data.isCloze) this.focusNextClozeInput(nextIndex)
})
},
focusNextClozeInput(nextIndex) {
const selector = `#cloze-input-${nextIndex}`
wx.nextTick(() => {
const query = wx.createSelectorQuery().in(this)
query.select(selector).fields({ node: true, size: true }).exec((res) => {
if (!res || !res[0]) return
const node = res[0].node
if (node && typeof node.focus === 'function') node.focus()
})
})
},
submitAnswer(showExplanation = false, advanceAfterSubmit = false) {

View File

@ -26,7 +26,13 @@
<view wx:if="{{currentClozeQuestion && currentClozeQuestion.blanks.length}}" class="inline-cloze">
<block wx:for="{{clozeSegments}}" wx:for-item="segment" wx:for-index="segmentIndex" wx:key="index">
<text wx:if="{{segment.type === 'text'}}" class="cloze-segment-text">{{segment.text}}</text>
<input wx:else class="answer inline-cloze-answer" data-index="{{segment.blankIndex}}" value="{{clozeAnswers[segment.blankIndex] || ''}}" placeholder="空{{segment.blankIndex + 1}}" bindinput="onClozeAnswerInput" />
<view wx:elif="{{segment.type === 'letter-cell'}}" class="cloze-letter-cell" style="width: {{segment.width}}; min-width: {{segment.minWidth}};">
<input id="cloze-input-{{segment.blankIndex}}-{{segment.letterIndex}}" focus="{{activeClozeBlankIndex === segment.blankIndex && segment.letterIndex === 0}}" class="answer inline-cloze-answer letter-cell-input" data-index="{{segment.blankIndex}}" data-letter-index="{{segment.letterIndex}}" value="{{clozeAnswers[segment.blankIndex] ? clozeAnswers[segment.blankIndex].charAt(segment.letterIndex) : ''}}" placeholder="" maxlength="1" bindinput="onClozeLetterInput" bindkeydown="handleLetterKeydown" />
<view class="cloze-letter-underline"></view>
</view>
<view wx:else class="cloze-blank-wrap" style="width: {{segment.width}}; min-width: {{segment.minWidth}};">
<input id="cloze-input-{{segment.blankIndex}}" focus="{{activeClozeBlankIndex === segment.blankIndex}}" class="answer inline-cloze-answer" data-index="{{segment.blankIndex}}" value="{{clozeAnswers[segment.blankIndex] || ''}}" placeholder="" maxlength="{{segment.maxLength}}" bindinput="onClozeAnswerInput" />
</view>
</block>
</view>
<text wx:else class="cloze">{{word}}</text>

File diff suppressed because one or more lines are too long