diff --git a/review-config/review-config.js b/review-config/review-config.js
index 781b6b2..be521bf 100644
--- a/review-config/review-config.js
+++ b/review-config/review-config.js
@@ -69,25 +69,45 @@ Page({
const question_mode = questionModeMap[this.data.activeMode]
const scope_config = {}
this.setData({ loading: true, error: '' })
- wx.request({
- url: `${app.globalData.baseUrl}/api/v1/review/generate`,
- method: 'POST',
- header: { 'Content-Type': 'application/json', 'X-OpenID': app.globalData.openid || '' },
- data: {
- scope_type,
- scope_config,
- content_type,
- question_mode,
- review_strategy: this.data.onlyReview ? 'review_only' : 'all',
- sort_mode: this.data.reviewOrder === 'shuffle' ? 'shuffle' : 'sequential',
- total_count: this.data.questionCount,
- },
- success: (res) => {
- app.globalData.reviewSessionMeta = res.data?.data || null
- wx.navigateTo({ url: '/pages/review-session/review-session' })
- },
- fail: () => this.setData({ error: '生成复习会话失败' }),
- complete: () => this.setData({ loading: false }),
+ app.ensureLogin().then((openid) => {
+ if (!openid) {
+ this.setData({ loading: false, error: '请先登录后再开始复习' })
+ return
+ }
+ wx.request({
+ url: `${app.globalData.baseUrl}/api/v1/review/generate`,
+ method: 'POST',
+ header: { 'Content-Type': 'application/json', 'X-OpenID': openid },
+ data: {
+ scope_type,
+ scope_config,
+ content_type,
+ question_mode,
+ review_strategy: this.data.onlyReview ? 'review_only' : 'all',
+ sort_mode: this.data.reviewOrder === 'shuffle' ? 'shuffle' : 'sequential',
+ total_count: this.data.questionCount,
+ },
+ success: (res) => {
+ const session = res.data?.data || null
+ if (res.statusCode < 200 || res.statusCode >= 300 || !session?.id) {
+ const message = res.data?.detail || res.data?.message || '生成复习会话失败'
+ this.setData({ error: message })
+ wx.showToast({ title: message, icon: 'none' })
+ return
+ }
+ if (!session.record_ids?.length) {
+ const message = '当前条件下没有可复习题目,请切换时间范围或关闭仅复习'
+ this.setData({ error: message })
+ wx.showToast({ title: message, icon: 'none' })
+ return
+ }
+ app.globalData.reviewSessionMeta = session
+ wx.setStorageSync('reviewSessionMeta', session)
+ wx.navigateTo({ url: '/pages/review-session/review-session' })
+ },
+ fail: () => this.setData({ error: '生成复习会话失败' }),
+ complete: () => this.setData({ loading: false }),
+ })
})
},
goHome() {
diff --git a/review-session/review-session.js b/review-session/review-session.js
index 729b1a2..840df68 100644
--- a/review-session/review-session.js
+++ b/review-session/review-session.js
@@ -36,6 +36,7 @@ Page({
currentClozeQuestion: null,
currentClozeResults: [],
showClozeFeedback: false,
+ clozeSegments: [],
},
submitting: false,
judged: false,
@@ -61,17 +62,55 @@ Page({
},
})
},
+ buildClozeSegments(clozeQuestion) {
+ if (!clozeQuestion || !Array.isArray(clozeQuestion.blanks)) return []
+ const prompt = clozeQuestion.prompt_text || ''
+ const segments = []
+ let blankIndex = 0
+ if (prompt.includes('____')) {
+ prompt.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}` })
+ blankIndex += 1
+ }
+ })
+ return segments
+ }
+
+ const blanks = clozeQuestion.blanks || []
+ 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: 'blank', blankIndex: idx, index: `blank-${idx}` })
+ cursor = end
+ })
+ if (cursor < prompt.length) {
+ segments.push({ type: 'text', text: prompt.slice(cursor), index: `text-tail` })
+ }
+ return segments
+ },
loadQuestions() {
- const session = app.globalData.reviewSessionMeta
- if (!session) {
+ const session = app.globalData.reviewSessionMeta || wx.getStorageSync('reviewSessionMeta')
+ if (!session?.id) {
this.setData({ error: '暂无复习会话' })
return
}
+ app.globalData.reviewSessionMeta = session
this.setData({ loading: true, error: '' })
wx.request({
url: `${app.globalData.baseUrl}/api/v1/review/questions?session_id=${session.id}`,
header: { 'X-OpenID': app.globalData.openid || '' },
success: (res) => {
+ if (res.statusCode < 200 || res.statusCode >= 300) {
+ const message = res.data?.detail || res.data?.message || '题目加载失败'
+ this.setData({ error: message, questions: [] })
+ return
+ }
const questions = (res.data?.data || []).map((item) => {
if (item.question_mode === 'C') {
return {
@@ -85,6 +124,10 @@ Page({
entry_type: item.entry_type || 'word',
}
})
+ if (!questions.length) {
+ this.setData({ error: '当前复习会话没有题目,请返回重新选择范围', questions: [] })
+ return
+ }
app.globalData.reviewSession = {
deck: questions,
currentIndex: 0,
@@ -115,7 +158,7 @@ Page({
word: isCloze ? (current?.blank_prompt || clozeQuestion?.prompt_text || current?.prompt || '') : (current?.prompt || ''),
meaning: isCloze ? (current?.answer_hint || '') : '',
answer: session.currentAnswer || '',
- clozeAnswers: isCloze ? (session.currentAnswers || []) : [],
+ clozeAnswers: isCloze ? (Array.isArray(session.currentAnswers) ? session.currentAnswers : Array.from({ length: currentClozeQuestion?.blanks?.length || 0 }, () => '')) : [],
feedback: '',
revealAnswer: '',
feedbackType: '',
@@ -128,6 +171,7 @@ Page({
currentClozeQuestion: clozeQuestion,
currentClozeResults: session.lastClozeResults || [],
showClozeFeedback: false,
+ clozeSegments: isCloze ? this.buildClozeSegments(clozeQuestion) : [],
})
},
onAnswerInput(e) {
@@ -202,6 +246,10 @@ Page({
showClozeFeedback: clozeResults.length > 0,
currentClozeResults: clozeResults,
})
+ if (this.data.isCloze) {
+ app.globalData.reviewSession.currentAnswers = []
+ this.setData({ clozeAnswers: [] })
+ }
if (this.data.settings.autoNextQuestion || advanceAfterSubmit) {
setTimeout(() => this.nextQuestion(), shouldShowAnswer ? 1200 : 0)
}
@@ -239,6 +287,7 @@ Page({
}
session.currentIndex += 1
session.currentAnswer = ''
+ session.currentAnswers = []
this.syncSession()
},
markMastered() {
diff --git a/review-session/review-session.wxml b/review-session/review-session.wxml
index b2a7e9d..12e9e5e 100644
--- a/review-session/review-session.wxml
+++ b/review-session/review-session.wxml
@@ -10,7 +10,7 @@
暂无题目
- 请先生成复习会话
+ {{error || '请先生成复习会话'}}
@@ -23,14 +23,14 @@
- {{word}}
-
-
- 空 {{blankIndex + 1}}
- 提示:首字母 {{blank.hint.first_letter}} · 长度 {{blank.hint.length}}
-
-
+
+
+ {{segment.text}}
+
+
+ {{word}}
+
{{word}}
diff --git a/review-session/review-session.wxss b/review-session/review-session.wxss
index 98c4827..74b4cd8 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}.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: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}