修复填空复习功能2.2
This commit is contained in:
parent
9da98e9c82
commit
44ac502d5b
@ -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) {
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user