|
|
@@ -0,0 +1,783 @@
|
|
|
+<!-- 刷题练习页 -->
|
|
|
+<template>
|
|
|
+ <view class="practice-screen">
|
|
|
+ <!-- 练习用时顶栏(绿色) -->
|
|
|
+ <view class="timer-bar">
|
|
|
+ <text class="timer-label">练习用时</text>
|
|
|
+ <text class="timer-text">{{ timerDisplay }}</text>
|
|
|
+ <text class="timer-mode">刷题练习模式</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 答题统计行 -->
|
|
|
+ <view class="stat-row">
|
|
|
+ <view class="stat-item">
|
|
|
+ <text class="stat-num">{{ answered }}</text>
|
|
|
+ <text class="stat-label">已答</text>
|
|
|
+ </view>
|
|
|
+ <view class="stat-divider"></view>
|
|
|
+ <view class="stat-item">
|
|
|
+ <text class="stat-num stat-correct">{{ correct }}</text>
|
|
|
+ <text class="stat-label">正确</text>
|
|
|
+ </view>
|
|
|
+ <view class="stat-divider"></view>
|
|
|
+ <view class="stat-item">
|
|
|
+ <text class="stat-num stat-wrong">{{ incorrect }}</text>
|
|
|
+ <text class="stat-label">错误</text>
|
|
|
+ </view>
|
|
|
+ <view class="stat-divider"></view>
|
|
|
+ <view class="stat-item">
|
|
|
+ <text class="stat-num">{{ accuracyText }}</text>
|
|
|
+ <text class="stat-label">正确率</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 题号导航面板(底部弹出,仿 mockExamPlay) -->
|
|
|
+ <view v-if="showQuestionNav" class="q-nav-panel" @tap="closeNav">
|
|
|
+ <view class="q-nav-scroll-wrapper" @tap.stop>
|
|
|
+ <view class="q-nav-header">
|
|
|
+ <text class="q-nav-title">题目列表</text>
|
|
|
+ <text class="q-nav-close" @tap="closeNav">✕</text>
|
|
|
+ </view>
|
|
|
+ <scroll-view scroll-y class="q-nav-scroll" :style="'height:' + navScrollHeight + 'px'">
|
|
|
+ <view v-for="(group, gi) in questionTypeGroups" :key="gi" class="q-group">
|
|
|
+ <view class="q-group-title">{{ group.questionTypeName }}({{ (group.questions || []).length }}题)</view>
|
|
|
+ <view class="q-num-row">
|
|
|
+ <view
|
|
|
+ v-for="(item, qi) in group.questions"
|
|
|
+ :key="qi"
|
|
|
+ class="q-num"
|
|
|
+ :class="getNavNumClass(item, gi, qi)"
|
|
|
+ :data-gi="gi"
|
|
|
+ :data-qi="qi"
|
|
|
+ @tap="onNavNumTap"
|
|
|
+ >{{ item.seq }}</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <!-- 图例 -->
|
|
|
+ <view class="nav-legend">
|
|
|
+ <view class="legend-item"><view class="q-num legend-dot answered"></view><text>已答对</text></view>
|
|
|
+ <view class="legend-item"><view class="q-num legend-dot wrong"></view><text>答错</text></view>
|
|
|
+ <view class="legend-item"><view class="q-num legend-dot current"></view><text>当前</text></view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 题目内容 -->
|
|
|
+ <scroll-view scroll-y class="q-scroll-content" :style="scrollStyle">
|
|
|
+ <view v-if="currentQuestion && currentQuestion.id" class="q-card">
|
|
|
+ <!-- 题目头部 -->
|
|
|
+ <view class="q-header">
|
|
|
+ <view class="q-header-left">
|
|
|
+ <text class="q-type-badge">{{ currentQuestion.questionTypeName }}</text>
|
|
|
+ <text class="q-num-text">第 {{ currentSeq }} 题</text>
|
|
|
+ <text v-if="currentQuestion.difficulty" class="diff-badge" :class="diffClass(currentQuestion.difficulty)">{{ diffName(currentQuestion.difficulty) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="view-answer-btn" @tap="toggleAnswer">
|
|
|
+ {{ viewAnswerShowType ? '关闭答案' : '查看答案' }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 题干 -->
|
|
|
+ <view class="q-content">
|
|
|
+ <rich-text :nodes="currentQuestion.questionContent || ''"></rich-text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 选项 -->
|
|
|
+ <view class="q-options">
|
|
|
+ <view
|
|
|
+ v-for="(opt, oi) in (currentQuestion.options || [])"
|
|
|
+ :key="oi"
|
|
|
+ class="q-option"
|
|
|
+ :class="{ selected: isSelected(opt.optionTag) }"
|
|
|
+ :data-tag="opt.optionTag"
|
|
|
+ @tap="onOptionTap"
|
|
|
+ >
|
|
|
+ <view class="q-option-label">{{ opt.optionTag }}</view>
|
|
|
+ <view class="q-option-content">
|
|
|
+ <rich-text :nodes="opt.optionContent || ''"></rich-text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 查看答案区域 -->
|
|
|
+ <view v-if="viewAnswerShowType" class="answer-result-box">
|
|
|
+ <view class="answer-row">
|
|
|
+ <text class="answer-label">答案:</text>
|
|
|
+ <text class="answer-value">{{ formatAnswer(currentQuestion.correctAnswer) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="analysis-title">试题解析</view>
|
|
|
+ <view class="analysis-content">
|
|
|
+ <rich-text v-if="currentQuestion.analysis" :nodes="currentQuestion.analysis"></rich-text>
|
|
|
+ <text v-else class="analysis-empty">无</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-else-if="loading" class="empty-tip">加载中...</view>
|
|
|
+ <view v-else class="empty-tip">暂无题目</view>
|
|
|
+ <view class="scroll-bottom-space"></view>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <!-- 上一题/下一题 -->
|
|
|
+ <view class="q-nav">
|
|
|
+ <view class="q-nav-btn" :class="{ disabled: isFirstQuestion }" @tap="prevQuestion">上一题</view>
|
|
|
+ <view class="q-nav-btn q-nav-center" @tap="toggleQuestionNav">选题</view>
|
|
|
+ <view class="q-nav-btn q-nav-btn-next" :class="{ disabled: isLastQuestion }" @tap="nextQuestion">下一题</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import {
|
|
|
+ examUserPracticePanel,
|
|
|
+ examUserPracticeQuestionDetail,
|
|
|
+ examUserPracticeSubmit,
|
|
|
+ examUserExamStart,
|
|
|
+ } from '../../api/index.js';
|
|
|
+
|
|
|
+ const DIFFICULTY_MAP = {
|
|
|
+ 1: { name: '简单', cls: 'diff-easy' },
|
|
|
+ 2: { name: '一般', cls: 'diff-normal' },
|
|
|
+ 3: { name: '较难', cls: 'diff-hard' },
|
|
|
+ 4: { name: '极难', cls: 'diff-extreme' },
|
|
|
+ };
|
|
|
+
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ newData: {},
|
|
|
+ examInfo: {
|
|
|
+ knowledgePointName: '',
|
|
|
+ totalCount: 0,
|
|
|
+ },
|
|
|
+ questionTypeGroups: [],
|
|
|
+ currentQuestion: null,
|
|
|
+ groupIndex: 0,
|
|
|
+ questionIndex: 0,
|
|
|
+ viewAnswerShowType: false,
|
|
|
+ showQuestionNav: false,
|
|
|
+ navScrollHeight: 300,
|
|
|
+ loading: false,
|
|
|
+ // 计时器
|
|
|
+ elapsedSeconds: 0,
|
|
|
+ timerInterval: null,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ answered() {
|
|
|
+ let n = 0;
|
|
|
+ this.questionTypeGroups.forEach(g => {
|
|
|
+ if (g.questions) g.questions.forEach(q => { if (q.answerStatus === 1 || q.answerStatus === 2) n++; });
|
|
|
+ });
|
|
|
+ return n;
|
|
|
+ },
|
|
|
+ correct() {
|
|
|
+ let n = 0;
|
|
|
+ this.questionTypeGroups.forEach(g => {
|
|
|
+ if (g.questions) g.questions.forEach(q => { if (q.answerStatus === 1) n++; });
|
|
|
+ });
|
|
|
+ return n;
|
|
|
+ },
|
|
|
+ incorrect() {
|
|
|
+ let n = 0;
|
|
|
+ this.questionTypeGroups.forEach(g => {
|
|
|
+ if (g.questions) g.questions.forEach(q => { if (q.answerStatus === 2) n++; });
|
|
|
+ });
|
|
|
+ return n;
|
|
|
+ },
|
|
|
+ accuracyText() {
|
|
|
+ let total = 0;
|
|
|
+ this.questionTypeGroups.forEach(g => { if (g.questions) total += g.questions.length; });
|
|
|
+ if (total === 0 || this.correct === 0) return '0%';
|
|
|
+ return (this.correct / total * 100).toFixed(0) + '%';
|
|
|
+ },
|
|
|
+ timerDisplay() {
|
|
|
+ const m = Math.floor(this.elapsedSeconds / 60);
|
|
|
+ const s = this.elapsedSeconds % 60;
|
|
|
+ return String(m).padStart(2, '0') + ':' + String(s).padStart(2, '0');
|
|
|
+ },
|
|
|
+ isFirstQuestion() {
|
|
|
+ return this.groupIndex === 0 && this.questionIndex === 0;
|
|
|
+ },
|
|
|
+ isLastQuestion() {
|
|
|
+ const last = this.questionTypeGroups.length - 1;
|
|
|
+ if (last < 0) return true;
|
|
|
+ const lastQ = this.questionTypeGroups[last].questions.length - 1;
|
|
|
+ return this.groupIndex === last && this.questionIndex === lastQ;
|
|
|
+ },
|
|
|
+ currentSeq() {
|
|
|
+ const g = this.questionTypeGroups[this.groupIndex];
|
|
|
+ if (!g || !g.questions[this.questionIndex]) return '-';
|
|
|
+ return g.questions[this.questionIndex].seq;
|
|
|
+ },
|
|
|
+ scrollStyle() {
|
|
|
+ // timer-bar ~48px + stat-row ~52px + bottom-nav ~44px
|
|
|
+ const sys = uni.getSystemInfoSync();
|
|
|
+ const h = sys.windowHeight - 48 - 52 - 44;
|
|
|
+ return 'height:' + (h > 200 ? h : 400) + 'px;';
|
|
|
+ },
|
|
|
+ },
|
|
|
+ onLoad(options) {
|
|
|
+ this.newData = {
|
|
|
+ knowledgePointId: options.knowledgePointId || '',
|
|
|
+ attemptId: options.attemptId || '',
|
|
|
+ examId: options.examId || '',
|
|
|
+ };
|
|
|
+ uni.setNavigationBarTitle({ title: '刷题练习' });
|
|
|
+ this.startTimer();
|
|
|
+ // 计算题号面板可滚动高度
|
|
|
+ const sys = uni.getSystemInfoSync();
|
|
|
+ this.navScrollHeight = Math.floor(sys.windowHeight * 0.5);
|
|
|
+ this.loadPanel();
|
|
|
+ },
|
|
|
+ onUnload() {
|
|
|
+ this.stopTimer();
|
|
|
+ },
|
|
|
+ // 支持导航栏"题号"按钮(pages.json 中配置 navigationBarButtons)
|
|
|
+ onNavigationBarButtonTap(e) {
|
|
|
+ if (e.index === 0) {
|
|
|
+ this.showQuestionNav = !this.showQuestionNav;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ closeNav() {
|
|
|
+ this.showQuestionNav = false;
|
|
|
+ },
|
|
|
+ toggleQuestionNav() {
|
|
|
+ this.showQuestionNav = !this.showQuestionNav;
|
|
|
+ },
|
|
|
+ toggleAnswer() {
|
|
|
+ this.viewAnswerShowType = !this.viewAnswerShowType;
|
|
|
+ },
|
|
|
+ onOptionTap(e) {
|
|
|
+ const tag = e.currentTarget.dataset.tag;
|
|
|
+ if (tag !== undefined) this.selectOption(tag);
|
|
|
+ },
|
|
|
+ onNavNumTap(e) {
|
|
|
+ const gi = Number(e.currentTarget.dataset.gi);
|
|
|
+ const qi = Number(e.currentTarget.dataset.qi);
|
|
|
+ this.jumpToQuestion(gi, qi);
|
|
|
+ },
|
|
|
+ startTimer() { this.timerInterval = setInterval(() => {
|
|
|
+ this.elapsedSeconds++;
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+ stopTimer() {
|
|
|
+ if (this.timerInterval) clearInterval(this.timerInterval);
|
|
|
+ },
|
|
|
+ diffName(d) { return DIFFICULTY_MAP[d] ? DIFFICULTY_MAP[d].name : ''; },
|
|
|
+ diffClass(d) { return DIFFICULTY_MAP[d] ? DIFFICULTY_MAP[d].cls : ''; },
|
|
|
+ isSelected(tag) {
|
|
|
+ if (!this.currentQuestion || !this.currentQuestion.userAnswer) return false;
|
|
|
+ return this.currentQuestion.userAnswer.indexOf(tag) !== -1;
|
|
|
+ },
|
|
|
+ formatAnswer(ans) {
|
|
|
+ if (!ans) return '';
|
|
|
+ return ans.split(',').map(a => {
|
|
|
+ if (a === 'Y') return '对';
|
|
|
+ if (a === 'N') return '错';
|
|
|
+ return a;
|
|
|
+ }).join('、');
|
|
|
+ },
|
|
|
+ getNavNumClass(item, gi, qi) {
|
|
|
+ const isCurrent = this.groupIndex === gi && this.questionIndex === qi;
|
|
|
+ if (isCurrent) return 'current';
|
|
|
+ if (item.answerStatus === 1) return 'answered';
|
|
|
+ if (item.answerStatus === 2) return 'wrong';
|
|
|
+ return '';
|
|
|
+ },
|
|
|
+ // 加载左侧面板数据
|
|
|
+ loadPanel() {
|
|
|
+ if (this.loading) return;
|
|
|
+ this.loading = true;
|
|
|
+ examUserPracticePanel({ knowledgePointId: this.newData.knowledgePointId }).then(res => {
|
|
|
+ const resp = res.data || {};
|
|
|
+ if (resp.code !== 200) {
|
|
|
+ uni.showToast({ title: resp.message || '加载失败', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const d = resp.data || {};
|
|
|
+ this.examInfo = {
|
|
|
+ knowledgePointName: d.knowledgePointName || '',
|
|
|
+ totalCount: d.totalCount || 0,
|
|
|
+ };
|
|
|
+ this.$set(this, 'questionTypeGroups', d.questionTypeGroups || []);
|
|
|
+ this.groupIndex = 0;
|
|
|
+ this.questionIndex = 0;
|
|
|
+ const first = (d.questionTypeGroups || [])[0];
|
|
|
+ if (first && first.questions && first.questions[0]) {
|
|
|
+ this.loadQuestionDetail(first.questions[0].questionId, first.questions[0].userAnswer);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ console.error('loadPanel error', e);
|
|
|
+ uni.showToast({ title: '加载失败', icon: 'none' });
|
|
|
+ }).finally(() => {
|
|
|
+ this.loading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 加载单题详情
|
|
|
+ loadQuestionDetail(questionId, userAnswer) {
|
|
|
+ examUserPracticeQuestionDetail({ questionId }).then(res => {
|
|
|
+ const resp = res.data || {};
|
|
|
+ if (resp.code !== 200) return;
|
|
|
+ const q = resp.data || {};
|
|
|
+ // 判断题补充选项
|
|
|
+ if (q.questionType == 3) {
|
|
|
+ q.options = [
|
|
|
+ { optionContent: '正确', optionTag: 'Y', sortOrder: 1 },
|
|
|
+ { optionContent: '错误', optionTag: 'N', sortOrder: 2 },
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ q.userAnswer = Array.isArray(userAnswer)
|
|
|
+ ? userAnswer
|
|
|
+ : (userAnswer ? userAnswer.split(',') : []);
|
|
|
+ this.$set(this, 'currentQuestion', q);
|
|
|
+ this.$set(this, 'viewAnswerShowType', false);
|
|
|
+ }).catch(e => {
|
|
|
+ console.error('loadQuestionDetail error', e);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 选择选项
|
|
|
+ selectOption(tag) {
|
|
|
+ if (!this.currentQuestion) return;
|
|
|
+ const type = this.currentQuestion.questionType;
|
|
|
+ let ans = [...(this.currentQuestion.userAnswer || [])];
|
|
|
+ if (type == 1 || type == 3) {
|
|
|
+ // 单选 / 判断:单选
|
|
|
+ ans = [tag];
|
|
|
+ } else if (type == 2) {
|
|
|
+ // 多选
|
|
|
+ const idx = ans.indexOf(tag);
|
|
|
+ if (idx === -1) {
|
|
|
+ ans.push(tag);
|
|
|
+ } else {
|
|
|
+ ans.splice(idx, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.$set(this.currentQuestion, 'userAnswer', ans);
|
|
|
+ },
|
|
|
+ // 提交当前题答案并切换题目
|
|
|
+ _submitThenGo(nextGroupIndex, nextQuestionIndex) {
|
|
|
+ const q = this.currentQuestion;
|
|
|
+ const hasAnswer = q && q.userAnswer && q.userAnswer[0];
|
|
|
+ if (!hasAnswer) {
|
|
|
+ this._goTo(nextGroupIndex, nextQuestionIndex);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const userAnswerStr = q.userAnswer.join(',');
|
|
|
+ examUserPracticeSubmit({
|
|
|
+ attemptId: this.newData.attemptId,
|
|
|
+ questionId: q.id,
|
|
|
+ userAnswer: userAnswerStr,
|
|
|
+ }).then(res => {
|
|
|
+ const data = (res.data || {}).data || {};
|
|
|
+ // 回写 answerStatus 和 userAnswer 到面板 item
|
|
|
+ const answerStatus = data.answerStatus;
|
|
|
+ const g = this.questionTypeGroups[this.groupIndex];
|
|
|
+ if (g && g.questions[this.questionIndex]) {
|
|
|
+ this.$set(g.questions[this.questionIndex], 'answerStatus', answerStatus);
|
|
|
+ this.$set(g.questions[this.questionIndex], 'userAnswer', userAnswerStr);
|
|
|
+ }
|
|
|
+ this._goTo(nextGroupIndex, nextQuestionIndex);
|
|
|
+ }).catch(() => {
|
|
|
+ this._goTo(nextGroupIndex, nextQuestionIndex);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ _goTo(gi, qi) {
|
|
|
+ this.groupIndex = gi;
|
|
|
+ this.questionIndex = qi;
|
|
|
+ const g = this.questionTypeGroups[gi];
|
|
|
+ if (!g) return;
|
|
|
+ const item = g.questions[qi];
|
|
|
+ if (!item) return;
|
|
|
+ this.loadQuestionDetail(item.questionId, item.userAnswer);
|
|
|
+ },
|
|
|
+ prevQuestion() {
|
|
|
+ if (this.isFirstQuestion) return;
|
|
|
+ let gi = this.groupIndex;
|
|
|
+ let qi = this.questionIndex;
|
|
|
+ if (qi > 0) {
|
|
|
+ qi--;
|
|
|
+ } else {
|
|
|
+ gi--;
|
|
|
+ qi = (this.questionTypeGroups[gi] || { questions: [] }).questions.length - 1;
|
|
|
+ }
|
|
|
+ this._submitThenGo(gi, qi);
|
|
|
+ },
|
|
|
+ nextQuestion() {
|
|
|
+ if (this.isLastQuestion) return;
|
|
|
+ let gi = this.groupIndex;
|
|
|
+ let qi = this.questionIndex;
|
|
|
+ const curGroup = this.questionTypeGroups[gi];
|
|
|
+ if (qi < curGroup.questions.length - 1) {
|
|
|
+ qi++;
|
|
|
+ } else {
|
|
|
+ gi++;
|
|
|
+ qi = 0;
|
|
|
+ }
|
|
|
+ this._submitThenGo(gi, qi);
|
|
|
+ },
|
|
|
+ // 从题号面板跳题
|
|
|
+ jumpToQuestion(gi, qi) {
|
|
|
+ this.showQuestionNav = false;
|
|
|
+ if (gi === this.groupIndex && qi === this.questionIndex) return;
|
|
|
+ this._submitThenGo(gi, qi);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="stylus" scoped>
|
|
|
+ .practice-screen
|
|
|
+ height 100%
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+ background #f0f4fb
|
|
|
+ overflow hidden
|
|
|
+
|
|
|
+ // 顶部绿色计时器
|
|
|
+ .timer-bar
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ justify-content center
|
|
|
+ gap 16rpx
|
|
|
+ background linear-gradient(90deg, #52c41a, #389e0d)
|
|
|
+ padding 12rpx 32rpx
|
|
|
+ flex-shrink 0
|
|
|
+
|
|
|
+ .timer-label
|
|
|
+ color rgba(255,255,255,.8)
|
|
|
+ font-size 24rpx
|
|
|
+
|
|
|
+ .timer-text
|
|
|
+ color #fff
|
|
|
+ font-size 44rpx
|
|
|
+ font-weight 700
|
|
|
+ letter-spacing 4rpx
|
|
|
+
|
|
|
+ .timer-mode
|
|
|
+ color rgba(255,255,255,.6)
|
|
|
+ font-size 22rpx
|
|
|
+
|
|
|
+ // 统计行
|
|
|
+ .stat-row
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ background #fff
|
|
|
+ padding 12rpx 0
|
|
|
+ flex-shrink 0
|
|
|
+ border-bottom 1rpx solid #e8edf3
|
|
|
+
|
|
|
+ .stat-item
|
|
|
+ flex 1
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+ align-items center
|
|
|
+
|
|
|
+ .stat-num
|
|
|
+ font-size 36rpx
|
|
|
+ font-weight 700
|
|
|
+ color #333
|
|
|
+
|
|
|
+ .stat-correct
|
|
|
+ color #52c41a
|
|
|
+
|
|
|
+ .stat-wrong
|
|
|
+ color #ff4d4f
|
|
|
+
|
|
|
+ .stat-label
|
|
|
+ font-size 22rpx
|
|
|
+ color #999
|
|
|
+ margin-top 2rpx
|
|
|
+
|
|
|
+ .stat-divider
|
|
|
+ width 1rpx
|
|
|
+ height 48rpx
|
|
|
+ background #e8edf3
|
|
|
+
|
|
|
+ // 题目内容滚动区
|
|
|
+ .q-scroll-content
|
|
|
+ flex 1
|
|
|
+ overflow hidden
|
|
|
+
|
|
|
+ .q-card
|
|
|
+ margin 24rpx
|
|
|
+ padding 28rpx
|
|
|
+ background #fff
|
|
|
+ border-radius 20rpx
|
|
|
+ border 1rpx solid #dde3ed
|
|
|
+
|
|
|
+ // 题头
|
|
|
+ .q-header
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ justify-content space-between
|
|
|
+ margin-bottom 20rpx
|
|
|
+
|
|
|
+ .q-header-left
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ flex-wrap wrap
|
|
|
+ gap 10rpx
|
|
|
+
|
|
|
+ .q-type-badge
|
|
|
+ background #1677ff
|
|
|
+ color #fff
|
|
|
+ font-size 22rpx
|
|
|
+ padding 4rpx 14rpx
|
|
|
+ border-radius 8rpx
|
|
|
+
|
|
|
+ .q-num-text
|
|
|
+ font-size 26rpx
|
|
|
+ font-weight 600
|
|
|
+ color #333
|
|
|
+
|
|
|
+ .diff-badge
|
|
|
+ font-size 20rpx
|
|
|
+ padding 2rpx 12rpx
|
|
|
+ border-radius 8rpx
|
|
|
+ border 1rpx solid transparent
|
|
|
+
|
|
|
+ .diff-easy
|
|
|
+ background #f6ffed
|
|
|
+ color #52c41a
|
|
|
+ border-color #b7eb8f
|
|
|
+
|
|
|
+ .diff-normal
|
|
|
+ background #e6f4ff
|
|
|
+ color #1677ff
|
|
|
+ border-color #91caff
|
|
|
+
|
|
|
+ .diff-hard
|
|
|
+ background #fff7e6
|
|
|
+ color #fa8c16
|
|
|
+ border-color #ffd591
|
|
|
+
|
|
|
+ .diff-extreme
|
|
|
+ background #f9f0ff
|
|
|
+ color #722ed1
|
|
|
+ border-color #d3adf7
|
|
|
+
|
|
|
+ .view-answer-btn
|
|
|
+ background #1677ff
|
|
|
+ color #fff
|
|
|
+ font-size 24rpx
|
|
|
+ padding 8rpx 24rpx
|
|
|
+ border-radius 10rpx
|
|
|
+ flex-shrink 0
|
|
|
+
|
|
|
+ // 题干
|
|
|
+ .q-content
|
|
|
+ font-size 28rpx
|
|
|
+ color #333
|
|
|
+ line-height 1.7
|
|
|
+ margin-bottom 24rpx
|
|
|
+
|
|
|
+ // 选项
|
|
|
+ .q-options
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+ gap 16rpx
|
|
|
+ margin-bottom 16rpx
|
|
|
+
|
|
|
+ .q-option
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ border 1rpx solid #e8edf3
|
|
|
+ border-radius 12rpx
|
|
|
+ padding 20rpx 24rpx
|
|
|
+
|
|
|
+ .q-option.selected
|
|
|
+ border-color #1677ff
|
|
|
+ background #e6f7ff
|
|
|
+
|
|
|
+ .q-option-label
|
|
|
+ background #1677ff
|
|
|
+ color #fff
|
|
|
+
|
|
|
+ .q-option-label
|
|
|
+ width 48rpx
|
|
|
+ height 48rpx
|
|
|
+ line-height 48rpx
|
|
|
+ text-align center
|
|
|
+ border-radius 50%
|
|
|
+ background #f0f0f0
|
|
|
+ color #666
|
|
|
+ font-size 24rpx
|
|
|
+ font-weight 600
|
|
|
+ flex-shrink 0
|
|
|
+ margin-right 20rpx
|
|
|
+
|
|
|
+ .q-option-content
|
|
|
+ flex 1
|
|
|
+ font-size 28rpx
|
|
|
+ color #333
|
|
|
+
|
|
|
+ // 答案区
|
|
|
+ .answer-result-box
|
|
|
+ background #f5f5f5
|
|
|
+ border-radius 12rpx
|
|
|
+ padding 24rpx
|
|
|
+ margin-top 16rpx
|
|
|
+
|
|
|
+ .answer-row
|
|
|
+ font-size 26rpx
|
|
|
+ color #333
|
|
|
+ margin-bottom 12rpx
|
|
|
+
|
|
|
+ .answer-label
|
|
|
+ font-weight 600
|
|
|
+
|
|
|
+ .answer-value
|
|
|
+ color #52c41a
|
|
|
+ font-weight 600
|
|
|
+
|
|
|
+ .analysis-title
|
|
|
+ font-size 26rpx
|
|
|
+ font-weight 600
|
|
|
+ color #333
|
|
|
+ margin-bottom 8rpx
|
|
|
+
|
|
|
+ .analysis-content
|
|
|
+ font-size 26rpx
|
|
|
+ color #666
|
|
|
+ line-height 1.7
|
|
|
+ margin 0 20rpx
|
|
|
+
|
|
|
+ .analysis-empty
|
|
|
+ color #999
|
|
|
+
|
|
|
+ .scroll-bottom-space
|
|
|
+ height 40rpx
|
|
|
+
|
|
|
+ // 底部上一题/选题/下一题
|
|
|
+ .q-nav
|
|
|
+ display flex
|
|
|
+ background #fff
|
|
|
+ border-top 1rpx solid #e8edf3
|
|
|
+ flex-shrink 0
|
|
|
+
|
|
|
+ .q-nav-btn
|
|
|
+ flex 1
|
|
|
+ height 88rpx
|
|
|
+ line-height 88rpx
|
|
|
+ text-align center
|
|
|
+ font-size 28rpx
|
|
|
+ color #1677ff
|
|
|
+
|
|
|
+ .q-nav-btn.q-nav-btn-next
|
|
|
+ border-left 1rpx solid #e8edf3
|
|
|
+
|
|
|
+ .q-nav-center
|
|
|
+ border-left 1rpx solid #e8edf3
|
|
|
+ border-right 1rpx solid #e8edf3
|
|
|
+
|
|
|
+ .q-nav-btn.disabled
|
|
|
+ color #ccc
|
|
|
+
|
|
|
+ // 题号导航底部弹层
|
|
|
+ .q-nav-panel
|
|
|
+ position fixed
|
|
|
+ top 0
|
|
|
+ left 0
|
|
|
+ right 0
|
|
|
+ bottom 0
|
|
|
+ background rgba(0,0,0,0.45)
|
|
|
+ z-index 100
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+ justify-content flex-end
|
|
|
+
|
|
|
+ .q-nav-scroll-wrapper
|
|
|
+ background #fff
|
|
|
+ border-radius 32rpx 32rpx 0 0
|
|
|
+ max-height 70vh
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+
|
|
|
+ .q-nav-header
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ justify-content space-between
|
|
|
+ padding 28rpx 28rpx 16rpx
|
|
|
+ flex-shrink 0
|
|
|
+
|
|
|
+ .q-nav-title
|
|
|
+ font-size 30rpx
|
|
|
+ font-weight 600
|
|
|
+ color #222
|
|
|
+
|
|
|
+ .q-nav-close
|
|
|
+ font-size 32rpx
|
|
|
+ color #999
|
|
|
+ padding 10rpx
|
|
|
+
|
|
|
+ .q-nav-scroll
|
|
|
+ overflow hidden
|
|
|
+ padding-bottom 40rpx
|
|
|
+
|
|
|
+ .q-group
|
|
|
+ padding 0 28rpx 16rpx
|
|
|
+
|
|
|
+ .q-group-title
|
|
|
+ font-size 24rpx
|
|
|
+ color #666
|
|
|
+ line-height 60rpx
|
|
|
+
|
|
|
+ .q-num-row
|
|
|
+ display flex
|
|
|
+ flex-wrap wrap
|
|
|
+
|
|
|
+ .q-num
|
|
|
+ width 72rpx
|
|
|
+ height 72rpx
|
|
|
+ line-height 72rpx
|
|
|
+ text-align center
|
|
|
+ font-size 24rpx
|
|
|
+ border-radius 12rpx
|
|
|
+ margin 8rpx
|
|
|
+ background #f4f6fa
|
|
|
+ color #333
|
|
|
+ border 1rpx solid transparent
|
|
|
+
|
|
|
+ .q-num.answered
|
|
|
+ background #f6ffed
|
|
|
+ color #52c41a
|
|
|
+ border-color #b7eb8f
|
|
|
+
|
|
|
+ .q-num.wrong
|
|
|
+ background #fff2f0
|
|
|
+ color #ff4d4f
|
|
|
+ border-color #ffa39e
|
|
|
+
|
|
|
+ .q-num.current
|
|
|
+ background #1677ff
|
|
|
+ color #fff
|
|
|
+
|
|
|
+ // 图例
|
|
|
+ .nav-legend
|
|
|
+ display flex
|
|
|
+ gap 24rpx
|
|
|
+ flex-wrap wrap
|
|
|
+ padding 12rpx 28rpx 24rpx
|
|
|
+
|
|
|
+ .legend-item
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ gap 8rpx
|
|
|
+ font-size 22rpx
|
|
|
+ color #666
|
|
|
+
|
|
|
+ .legend-dot
|
|
|
+ width 40rpx
|
|
|
+ height 40rpx
|
|
|
+ margin 0
|
|
|
+ font-size 0
|
|
|
+
|
|
|
+ // 空态
|
|
|
+ .empty-tip
|
|
|
+ text-align center
|
|
|
+ line-height 120rpx
|
|
|
+ color #aaa
|
|
|
+ font-size 24rpx
|
|
|
+</style>
|