From 44ac502d5ba67ab7685c452c2faf6a99f65dc491 Mon Sep 17 00:00:00 2001 From: taiyi Date: Mon, 27 Apr 2026 18:44:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A1=AB=E7=A9=BA=E5=A4=8D?= =?UTF-8?q?=E4=B9=A0=E5=8A=9F=E8=83=BD2.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- review-session/review-session.js | 146 ++++++++++++++++++++++++++--- review-session/review-session.wxml | 8 +- review-session/review-session.wxss | 2 +- 3 files changed, 140 insertions(+), 16 deletions(-) diff --git a/review-session/review-session.js b/review-session/review-session.js index 840df68..5f2b9c3 100644 --- a/review-session/review-session.js +++ b/review-session/review-session.js @@ -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) { diff --git a/review-session/review-session.wxml b/review-session/review-session.wxml index 12e9e5e..6988575 100644 --- a/review-session/review-session.wxml +++ b/review-session/review-session.wxml @@ -26,7 +26,13 @@ {{segment.text}} - + + + + + + + {{word}} diff --git a/review-session/review-session.wxss b/review-session/review-session.wxss index 74b4cd8..aba7fa0 100644 --- a/review-session/review-session.wxss +++ b/review-session/review-session.wxss @@ -1 +1 @@ -page{background:#0c0e12;color:#f6f6fc}.page{min-height:100vh;background:#0c0e12}.topbar{display:flex;justify-content:space-between;align-items:center;padding:24rpx 32rpx;background:rgba(12,14,18,.8);border-bottom:1rpx solid rgba(255,255,255,.08);backdrop-filter:blur(24px);box-shadow:0 1rpx 20rpx rgba(0,0,0,.08)}.topbar-left{display:flex;align-items:center;gap:18rpx}.avatar-shell{width:80rpx;height:80rpx;border-radius:9999rpx;overflow:hidden;border:2rpx solid rgba(193,156,255,.2);flex-shrink:0}.avatar-img{width:100%;height:100%}.brand-text{font-size:38rpx;font-weight:800;color:#c19cff}.topbar-icon{width:72rpx;height:72rpx;border-radius:9999rpx;background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center}.content{height:calc(100vh - 120rpx);padding:32rpx;box-sizing:border-box}.progress-box{margin-bottom:28rpx}.progress-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:16rpx}.progress-label{font-size:24rpx;color:#aaabb0}.progress-count{font-size:28rpx;font-weight:700;color:#00e3fd}.progress-total{color:#aaabb0;font-size:22rpx;font-weight:400}.progress-bar{height:12rpx;background:#23262c;border-radius:9999rpx;overflow:hidden}.ghost-border{border:1rpx solid rgba(255,255,255,.1)}.progress-fill{height:100%;background:linear-gradient(90deg,#c19cff,#00e3fd)}.glass-card{background:rgba(29,32,37,.4);backdrop-filter:blur(24rpx)}.question-card{position:relative;overflow:hidden;border-radius:32rpx;padding:40rpx 32rpx;text-align:center;box-shadow:0 20rpx 40rpx rgba(0,0,0,.4);border:1rpx solid rgba(255,255,255,.1)}.ambient-glow{position:absolute;inset:0;background:linear-gradient(to bottom,rgba(193,156,255,.05),transparent);pointer-events:none}.mode-tag-wrap{display:flex;justify-content:center;margin-bottom:20rpx}.mode-tag{display:inline-flex;align-items:center;gap:10rpx;padding:10rpx 22rpx;border-radius:9999rpx;background:rgba(193,156,255,.12);border:1rpx solid rgba(193,156,255,.18);color:#d8c7ff;font-size:22rpx;letter-spacing:2rpx}.meaning-wrap{margin-bottom:28rpx}.meaning{display:block;color:#aaabb0;font-size:26rpx;letter-spacing:2rpx}.cloze-wrap,.word-wrap{margin:14rpx 0 10rpx}.cloze,.word{display:block;font-size:58rpx;font-weight:800;letter-spacing:2rpx;line-height:1.25}.cloze{color:#f6f6fc}.word{color:#f6f6fc}.entry-type-hint{margin-top:14rpx;color:#8f9198;font-size:22rpx;line-height:1.6}.inline-cloze{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:12rpx;font-size:44rpx;font-weight:800;line-height:1.5;color:#f6f6fc}.cloze-segment-text{display:inline;white-space:pre-wrap}.inline-cloze-answer{display:inline-flex;width:180rpx;min-height:64rpx;padding:8rpx 14rpx;border-radius:16rpx;background:rgba(12,14,18,.45);border:1rpx solid rgba(193,156,255,.28);font-size:28rpx}.cloze-input-list{margin-top:28rpx;display:flex;flex-direction:column;gap:20rpx}.cloze-input-item{padding:20rpx 18rpx;border-radius:24rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.08);text-align:left}.cloze-input-label{display:block;margin-bottom:12rpx;color:#d8c7ff;font-size:22rpx;font-weight:700;letter-spacing:1rpx}.cloze-hint{margin-bottom:12rpx;color:#8f9198;font-size:20rpx;line-height:1.5}.input-shell{margin:26rpx auto 0;max-width:520rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.08);border-radius:9999rpx}.answer{padding:22rpx 28rpx;text-align:center;color:#f6f6fc;font-size:32rpx;caret-color:#c19cff}.cloze-answer{max-width:100%;background:rgba(12,14,18,.35);border-radius:18rpx;border:1rpx solid rgba(255,255,255,.08);text-align:left;padding:22rpx 24rpx;margin:0 4rpx}.answer::placeholder{color:rgba(170,171,176,.72)}.answer::-webkit-input-placeholder{color:rgba(170,171,176,.72)}.answer::-moz-placeholder{color:rgba(170,171,176,.72)}.answer:-ms-input-placeholder{color:rgba(170,171,176,.72)}.feedback-box{margin-top:22rpx;padding:20rpx 24rpx;border-radius:24rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.06)}.feedback{display:block;color:#00e3fd}.reveal-answer{display:block;margin-top:10rpx;color:#f6f6fc;font-weight:700}.cloze-feedback-list{margin-top:18rpx;display:flex;flex-direction:column;gap:14rpx}.cloze-feedback-item{padding:18rpx 16rpx;border-radius:18rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.06)}.cloze-feedback-title{display:block;margin-bottom:8rpx;font-size:22rpx;font-weight:700;color:#c19cff}.cloze-feedback-text{display:block;font-size:22rpx;line-height:1.55;color:#e8e9ee}.actions-cluster{display:flex;flex-direction:row;gap:18rpx;justify-content:center;align-items:center;margin-top:24rpx}.secondary-btn,.primary-btn{flex:1;border-radius:9999rpx;padding:24rpx 0;font-weight:700}.secondary-btn{background:#23262c;color:#f6f6fc}.primary-btn{background:linear-gradient(90deg,#c19cff,#9146ff);color:#fff}.master-action{margin-top:34rpx;text-align:center}.master-text{color:#aaabb0;font-size:22rpx;border-bottom:1rpx solid transparent}.master-text.active{color:#00e5fd;border-color:#00e5fd} +page{background:#0c0e12;color:#f6f6fc}.page{min-height:100vh;background:#0c0e12}.topbar{display:flex;justify-content:space-between;align-items:center;padding:24rpx 32rpx;background:rgba(12,14,18,.8);border-bottom:1rpx solid rgba(255,255,255,.08);backdrop-filter:blur(24px);box-shadow:0 1rpx 20rpx rgba(0,0,0,.08)}.topbar-left{display:flex;align-items:center;gap:18rpx}.avatar-shell{width:80rpx;height:80rpx;border-radius:9999rpx;overflow:hidden;border:2rpx solid rgba(193,156,255,.2);flex-shrink:0}.avatar-img{width:100%;height:100%}.brand-text{font-size:38rpx;font-weight:800;color:#c19cff}.topbar-icon{width:72rpx;height:72rpx;border-radius:9999rpx;background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center}.content{height:calc(100vh - 120rpx);padding:32rpx;box-sizing:border-box}.progress-box{margin-bottom:28rpx}.progress-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:16rpx}.progress-label{font-size:24rpx;color:#aaabb0}.progress-count{font-size:28rpx;font-weight:700;color:#00e3fd}.progress-total{color:#aaabb0;font-size:22rpx;font-weight:400}.progress-bar{height:12rpx;background:#23262c;border-radius:9999rpx;overflow:hidden}.ghost-border{border:1rpx solid rgba(255,255,255,.1)}.progress-fill{height:100%;background:linear-gradient(90deg,#c19cff,#00e3fd)}.glass-card{background:rgba(29,32,37,.4);backdrop-filter:blur(24rpx)}.question-card{position:relative;overflow:hidden;border-radius:32rpx;padding:40rpx 32rpx;text-align:center;box-shadow:0 20rpx 40rpx rgba(0,0,0,.4);border:1rpx solid rgba(255,255,255,.1)}.ambient-glow{position:absolute;inset:0;background:linear-gradient(to bottom,rgba(193,156,255,.05),transparent);pointer-events:none}.mode-tag-wrap{display:flex;justify-content:center;margin-bottom:20rpx}.mode-tag{display:inline-flex;align-items:center;gap:10rpx;padding:10rpx 22rpx;border-radius:9999rpx;background:rgba(193,156,255,.12);border:1rpx solid rgba(193,156,255,.18);color:#d8c7ff;font-size:22rpx;letter-spacing:2rpx}.meaning-wrap{margin-bottom:28rpx}.meaning{display:block;color:#aaabb0;font-size:26rpx;letter-spacing:2rpx}.cloze-wrap,.word-wrap{margin:14rpx 0 10rpx}.cloze,.word{display:block;font-size:58rpx;font-weight:800;letter-spacing:2rpx;line-height:1.25}.cloze{color:#f6f6fc}.word{color:#f6f6fc}.entry-type-hint{margin-top:14rpx;color:#8f9198;font-size:22rpx;line-height:1.6}.inline-cloze{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:center;gap:10rpx;font-size:68rpx;font-weight:900;line-height:1.2;color:#f6f6fc;letter-spacing:1rpx}.cloze-segment-text{display:inline;white-space:pre-wrap;line-height:1.2}.cloze-blank-wrap{display:inline-flex;align-items:flex-end;justify-content:center;vertical-align:baseline;position:relative;top:2rpx}.cloze-letter-cell{display:inline-flex;flex-direction:column;align-items:center;justify-content:flex-end;vertical-align:baseline}.inline-cloze-answer{display:block;width:100%;min-width:100%;padding:0 0 4rpx;background:transparent;border:none;border-bottom:5rpx solid rgba(193,156,255,.98);border-radius:0;font-size:68rpx;font-weight:900;color:#f6f6fc;text-align:center;line-height:1.05;caret-color:#c19cff;box-sizing:border-box}.letter-cell-input{font-size:68rpx}.cloze-letter-underline{width:100%;height:0}.inline-cloze-answer::placeholder{color:transparent}.cloze-input-list{margin-top:28rpx;display:flex;flex-direction:column;gap:20rpx}.cloze-input-item{padding:20rpx 18rpx;border-radius:24rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.08);text-align:left}.cloze-input-label{display:block;margin-bottom:12rpx;color:#d8c7ff;font-size:22rpx;font-weight:700;letter-spacing:1rpx}.cloze-hint{margin-bottom:12rpx;color:#8f9198;font-size:20rpx;line-height:1.5}.input-shell{margin:26rpx auto 0;max-width:520rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.08);border-radius:9999rpx}.answer{padding:22rpx 28rpx;text-align:center;color:#f6f6fc;font-size:32rpx;caret-color:#c19cff}.cloze-answer{max-width:100%;background:rgba(12,14,18,.35);border-radius:18rpx;border:1rpx solid rgba(255,255,255,.08);text-align:left;padding:22rpx 24rpx;margin:0 4rpx}.answer::placeholder{color:rgba(170,171,176,.72)}.answer::-webkit-input-placeholder{color:rgba(170,171,176,.72)}.answer::-moz-placeholder{color:rgba(170,171,176,.72)}.answer:-ms-input-placeholder{color:rgba(170,171,176,.72)}.feedback-box{margin-top:22rpx;padding:20rpx 24rpx;border-radius:24rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.06)}.feedback{display:block;color:#00e3fd}.reveal-answer{display:block;margin-top:10rpx;color:#f6f6fc;font-weight:700}.cloze-feedback-list{margin-top:18rpx;display:flex;flex-direction:column;gap:14rpx}.cloze-feedback-item{padding:18rpx 16rpx;border-radius:18rpx;background:rgba(255,255,255,.04);border:1rpx solid rgba(255,255,255,.06)}.cloze-feedback-title{display:block;margin-bottom:8rpx;font-size:22rpx;font-weight:700;color:#c19cff}.cloze-feedback-text{display:block;font-size:22rpx;line-height:1.55;color:#e8e9ee}.actions-cluster{display:flex;flex-direction:row;gap:18rpx;justify-content:center;align-items:center;margin-top:24rpx}.secondary-btn,.primary-btn{flex:1;border-radius:9999rpx;padding:24rpx 0;font-weight:700}.secondary-btn{background:#23262c;color:#f6f6fc}.primary-btn{background:linear-gradient(90deg,#c19cff,#9146ff);color:#fff}.master-action{margin-top:34rpx;text-align:center}.master-text{color:#aaabb0;font-size:22rpx;border-bottom:1rpx solid transparent}.master-text.active{color:#00e5fd;border-color:#00e5fd}