Explorar el Código

fix(quiz): 文章阅读完成答题改为一题一页

答题弹窗原为三题全部平铺,现改为单题顺序作答:
- 新增步骤条「第 X / N 题」与圆点进度(未答/已选/当前三态)
- 导航按钮:首题下一步,中间题上/下一题,末题提交答案
- quizAnswers 按题下标存储,返回上一题答案保留,提交逻辑不变
- 题数由后端动态返回,改为 5 题也自适应;同步移除写死
  「三个问题」的文案
liaoxg hace 1 semana
padre
commit
b15da55182
Se han modificado 1 ficheros con 47 adiciones y 15 borrados
  1. 47 15
      cfc-frontend/pages/article-center/article-detail.vue

+ 47 - 15
cfc-frontend/pages/article-center/article-detail.vue

@@ -65,21 +65,30 @@
 
     <ContentSharePoster :show="showPoster" :title="(article && article.title) || ''" :coverImage="(article && article.coverImage) || ''" :qrCodeBase64="posterQrCode || ''" typeLabel="好文推荐" :dimensionCode="posterDimension || ''" :summary="posterSummary || ''" :inviteText="posterInviteText || ''" @close="closePoster" />
 
-    <!-- 浠宝答题弹窗 -->
+    <!-- 浠宝答题弹窗(一题一页) -->
     <view class="quiz-mask" v-if="showQuiz" @click="closeQuiz">
       <view class="quiz-dialog" @click.stop>
         <view class="quiz-mascot">
           <text class="mascot-icon">{{ mascotIcon }}</text>
           <text class="mascot-name">{{ mascotName }}</text>
         </view>
-        <text class="quiz-intro">阅读完成!考考你三个问题~</text>
-        <view class="quiz-question" v-for="(q, qi) in quizQuestions" :key="qi">
-          <text class="q-title">问题{{ qi+1 }}: {{ q.question }}</text>
+        <template v-if="currentQuestion">
+          <view class="quiz-step-bar">
+            <text class="quiz-step-text">第 {{ currentQuizIndex + 1 }} / {{ quizQuestions.length }} 题</text>
+            <view class="quiz-dots">
+              <view v-for="(q, qi) in quizQuestions" :key="qi" class="quiz-dot" :class="{ active: qi === currentQuizIndex, done: quizAnswers[qi] }"></view>
+            </view>
+          </view>
+          <text class="q-title">{{ currentQuestion.question }}</text>
           <view class="q-options">
-            <text v-for="(opt, oi) in q.options" :key="oi" class="q-option" :class="{ selected: quizAnswers[qi] === String.fromCharCode(65 + oi) }" @click="selectQuizAnswer(qi, oi)">{{ opt }}</text>
+            <text v-for="(opt, oi) in currentQuestion.options" :key="oi" class="q-option" :class="{ selected: quizAnswers[currentQuizIndex] === String.fromCharCode(65 + oi) }" @click="selectQuizAnswer(currentQuizIndex, oi)">{{ opt }}</text>
           </view>
-        </view>
-        <button class="quiz-submit" @click="submitQuiz">提交答案</button>
+          <view class="quiz-nav">
+            <button v-if="currentQuizIndex > 0" class="quiz-prev" @click="goPrevQuestion">上一题</button>
+            <button v-if="currentQuizIndex < quizQuestions.length - 1" class="quiz-submit" @click="goNextQuestion">下一题</button>
+            <button v-else class="quiz-submit" @click="submitQuiz">提交答案</button>
+          </view>
+        </template>
       </view>
     </view>
 
@@ -117,12 +126,18 @@ export default {
       posterDimension: '', posterSummary: '', posterInviteText: '',
       contentTagStyle: { p: 'font-size:32rpx;color:#444;line-height:1.8;', div: 'font-size:32rpx;color:#444;line-height:1.8;', section: 'font-size:32rpx;color:#444;line-height:1.8;', li: 'font-size:32rpx;color:#444;line-height:1.8;', blockquote: 'font-size:32rpx;color:#444;line-height:1.8;' },
       // 答题
-      showQuiz: false, quizQuestions: [], quizAnswers: [], showResult: false, quizResult: {}, quizRecordId: null,
+      showQuiz: false, quizQuestions: [], quizAnswers: [], showResult: false, quizResult: {}, quizRecordId: null, currentQuizIndex: 0,
       mascotList: [{ icon: '🐶', name: '浠宝' }, { icon: '🐼', name: '福宝' }],
       mascotIcon: '🐶', mascotName: '浠宝'
     }
   },
-  computed: {},
+  computed: {
+    // 当前展示的一题(一题一页)
+    currentQuestion: function() {
+      if (!this.quizQuestions || !this.quizQuestions.length) return null
+      return this.quizQuestions[this.currentQuizIndex] || null
+    }
+  },
   onLoad(options) {
     this.mascotIcon = this.mascotList[Math.floor(Math.random() * 2)].icon
     this.mascotName = this.mascotList[Math.floor(Math.random() * 2)].name
@@ -314,6 +329,7 @@ export default {
           this.quizRecordId = res.data.recordId
           this.quizQuestions = res.data.questions
           this.quizAnswers = []
+          this.currentQuizIndex = 0
           this.showQuiz = true
         }
       } catch (e) {}
@@ -344,6 +360,13 @@ export default {
     },
     selectQuizAnswer: function(qi, oi) {
       this.quizAnswers[qi] = String.fromCharCode(65 + oi)
+    },
+    // 一题一页:上一题 / 下一题
+    goPrevQuestion: function() {
+      if (this.currentQuizIndex > 0) this.currentQuizIndex--
+    },
+    goNextQuestion: function() {
+      if (this.currentQuizIndex < this.quizQuestions.length - 1) this.currentQuizIndex++
     }
   }
 }
@@ -400,13 +423,22 @@ export default {
 .quiz-mascot { display: flex; align-items: center; justify-content: center; gap: 12rpx; margin-bottom: 16rpx; }
 .mascot-icon { font-size: 60rpx; }
 .mascot-name { font-size: 32rpx; font-weight: 700; color: #F97316; }
-.quiz-intro { text-align: center; font-size: 28rpx; color: #666; margin-bottom: 24rpx; }
-.quiz-question { margin-bottom: 20rpx; }
-.q-title { font-size: 26rpx; font-weight: 600; color: #333; margin-bottom: 12rpx; }
-.q-options { display: flex; flex-direction: column; gap: 8rpx; }
-.q-option { padding: 14rpx 20rpx; border: 2rpx solid #E5E7EB; border-radius: 12rpx; font-size: 24rpx; color: #333; }
+.quiz-step-bar { display: flex; align-items: center; justify-content: center; margin-bottom: 24rpx; }
+.quiz-step-text { font-size: 24rpx; color: #999; margin-right: 12rpx; }
+.quiz-dots { display: flex; align-items: center; gap: 8rpx; }
+.quiz-dot { width: 12rpx; height: 12rpx; border-radius: 50%; background: #E5E7EB; }
+.quiz-dot.active { background: #F97316; }
+.quiz-dot.done { background: #FDBA74; }
+.q-title { font-size: 30rpx; font-weight: 600; color: #333; line-height: 1.5; margin-bottom: 20rpx; }
+.q-options { display: flex; flex-direction: column; gap: 12rpx; }
+.q-option { padding: 16rpx 20rpx; border: 2rpx solid #E5E7EB; border-radius: 12rpx; font-size: 26rpx; color: #333; }
 .q-option.selected { border-color: #F97316; background: #FFF7ED; color: #92400E; }
-.quiz-submit, .result-btn { width: 100%; padding: 20rpx; background: linear-gradient(135deg, #F97316, #FB923C); color: #fff; font-size: 28rpx; border-radius: 12rpx; border: none; margin-top: 20rpx; }
+.quiz-nav { display: flex; gap: 16rpx; margin-top: 28rpx; }
+.quiz-prev { flex: 1; padding: 20rpx; background: #fff; color: #F97316; border: 2rpx solid #F97316; font-size: 28rpx; border-radius: 12rpx; }
+.quiz-prev::after { border: none; }
+.quiz-submit { flex: 1; padding: 20rpx; background: linear-gradient(135deg, #F97316, #FB923C); color: #fff; font-size: 28rpx; border-radius: 12rpx; border: none; }
+.quiz-submit::after { border: none; }
+.result-btn { width: 100%; padding: 20rpx; background: linear-gradient(135deg, #F97316, #FB923C); color: #fff; font-size: 28rpx; border-radius: 12rpx; border: none; margin-top: 20rpx; }
 .result-dialog { text-align: center; }
 .result-icon { font-size: 80rpx; }
 .result-text { font-size: 32rpx; font-weight: 700; color: #333; margin: 16rpx 0; }