修复填空复习功能2.2
This commit is contained in:
parent
9da98e9c82
commit
44ac502d5b
@ -37,6 +37,7 @@ Page({
|
|||||||
currentClozeResults: [],
|
currentClozeResults: [],
|
||||||
showClozeFeedback: false,
|
showClozeFeedback: false,
|
||||||
clozeSegments: [],
|
clozeSegments: [],
|
||||||
|
activeClozeBlankIndex: 0,
|
||||||
},
|
},
|
||||||
submitting: false,
|
submitting: false,
|
||||||
judged: false,
|
judged: false,
|
||||||
@ -65,32 +66,63 @@ Page({
|
|||||||
buildClozeSegments(clozeQuestion) {
|
buildClozeSegments(clozeQuestion) {
|
||||||
if (!clozeQuestion || !Array.isArray(clozeQuestion.blanks)) return []
|
if (!clozeQuestion || !Array.isArray(clozeQuestion.blanks)) return []
|
||||||
const prompt = clozeQuestion.prompt_text || ''
|
const prompt = clozeQuestion.prompt_text || ''
|
||||||
|
const blanks = clozeQuestion.blanks || []
|
||||||
const segments = []
|
const segments = []
|
||||||
let blankIndex = 0
|
const useUnderscorePrompt = prompt.includes('____')
|
||||||
if (prompt.includes('____')) {
|
const workingPrompt = useUnderscorePrompt ? prompt : (clozeQuestion.original_text || prompt)
|
||||||
prompt.split('____').forEach((text, index, parts) => {
|
|
||||||
|
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 (text) segments.push({ type: 'text', text, index: `text-${index}` })
|
||||||
if (index < parts.length - 1) {
|
if (index < parts.length - 1) {
|
||||||
segments.push({ type: 'blank', blankIndex, index: `blank-${blankIndex}` })
|
pushWordBlank(blankIndex, blanks[blankIndex])
|
||||||
blankIndex += 1
|
blankIndex += 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return segments
|
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
|
let cursor = 0
|
||||||
blanks.forEach((blank, idx) => {
|
blanks.forEach((blank, idx) => {
|
||||||
const start = Math.max(0, Number(blank.start || 0))
|
const start = Math.max(0, Number(blank.start || 0))
|
||||||
const end = Math.max(start, Number(blank.end || start + 1))
|
const end = Math.max(start, Number(blank.end || start + 1))
|
||||||
if (start > cursor) {
|
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
|
cursor = end
|
||||||
})
|
})
|
||||||
if (cursor < prompt.length) {
|
if (cursor < workingPrompt.length) {
|
||||||
segments.push({ type: 'text', text: prompt.slice(cursor), index: `text-tail` })
|
segments.push({ type: 'text', text: workingPrompt.slice(cursor), index: `text-tail` })
|
||||||
}
|
}
|
||||||
return segments
|
return segments
|
||||||
},
|
},
|
||||||
@ -159,6 +191,7 @@ Page({
|
|||||||
meaning: isCloze ? (current?.answer_hint || '') : '',
|
meaning: isCloze ? (current?.answer_hint || '') : '',
|
||||||
answer: session.currentAnswer || '',
|
answer: session.currentAnswer || '',
|
||||||
clozeAnswers: isCloze ? (Array.isArray(session.currentAnswers) ? session.currentAnswers : Array.from({ length: currentClozeQuestion?.blanks?.length || 0 }, () => '')) : [],
|
clozeAnswers: isCloze ? (Array.isArray(session.currentAnswers) ? session.currentAnswers : Array.from({ length: currentClozeQuestion?.blanks?.length || 0 }, () => '')) : [],
|
||||||
|
activeClozeBlankIndex: 0,
|
||||||
feedback: '',
|
feedback: '',
|
||||||
revealAnswer: '',
|
revealAnswer: '',
|
||||||
feedbackType: '',
|
feedbackType: '',
|
||||||
@ -179,20 +212,105 @@ Page({
|
|||||||
app.globalData.reviewSession.currentAnswer = answer
|
app.globalData.reviewSession.currentAnswer = answer
|
||||||
this.setData({ answer, showFeedback: false, revealAnswer: '', feedback: '', feedbackType: '', judged: false, showClozeFeedback: false })
|
this.setData({ answer, showFeedback: false, revealAnswer: '', feedback: '', feedbackType: '', judged: false, showClozeFeedback: false })
|
||||||
},
|
},
|
||||||
onClozeAnswerInput(e) {
|
onClozeLetterInput(e) {
|
||||||
const { index } = e.currentTarget.dataset
|
const { index, letterIndex } = e.currentTarget.dataset
|
||||||
const value = e.detail.value
|
const rawValue = e.detail.value || ''
|
||||||
|
const cleaned = rawValue.slice(-1)
|
||||||
const answers = [...(app.globalData.reviewSession.currentAnswers || [])]
|
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
|
app.globalData.reviewSession.currentAnswers = answers
|
||||||
this.setData({
|
this.setData({
|
||||||
[`clozeAnswers[${index}]`]: value,
|
[`clozeAnswers[${index}]`]: answers[index],
|
||||||
showFeedback: false,
|
showFeedback: false,
|
||||||
revealAnswer: '',
|
revealAnswer: '',
|
||||||
feedback: '',
|
feedback: '',
|
||||||
feedbackType: '',
|
feedbackType: '',
|
||||||
judged: false,
|
judged: false,
|
||||||
showClozeFeedback: 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) {
|
submitAnswer(showExplanation = false, advanceAfterSubmit = false) {
|
||||||
|
|||||||
@ -26,7 +26,13 @@
|
|||||||
<view wx:if="{{currentClozeQuestion && currentClozeQuestion.blanks.length}}" class="inline-cloze">
|
<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">
|
<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>
|
<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>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
<text wx:else class="cloze">{{word}}</text>
|
<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