用 Uni-app 开发 C3 继续教育题库系统:静态资源导入、响应式交互与考试逻辑实现全解析

news/2025/10/15 13:32:59/文章来源:https://www.cnblogs.com/Goddragon/p/19143119

基于 Uni-app 开发的 C3 继续教育题库系统是一个功能完整的移动端学习应用,专注于提供高质量的在线练习和模拟考试体验。系统采用现代化的技术栈,具备良好的用户体验和稳定的性能表现。

一、核心功能模块

  1. 题库练习系统
    1)三种题型支持:单选题(400道)、多选题(200道)、判断题(400道)
    2)实时答题反馈,正确答案绿色标识,错误答案红色标识
    3)智能题型切换,一键切换不同类型题目
    4)自动分页浏览,支持上一题 / 下一题快速导航
    5)单选题和判断题答对自动跳题功能
  2. 模拟考试系统
    1)智能组卷算法:随机抽取 40 道单选题 + 20 道多选题 + 40 道判断题
    2)严格考试计时:90 分钟倒计时,时间到自动交卷
    3)实时答题卡:直观显示已答 / 未答题目状态
    4)智能评分系统:支持多选题少选部分得分(0.5 分)
    5)详细成绩分析:按题型统计得分,错题归类展示
  3. 数据管理系统
    1)静态 JSON 文件优化加载,提升应用启动速度
    2)全局数据共享,实现页面间数据无缝传递
    3)本地数据缓存,减少网络请求
    4)数据验证机制,确保题目数据完整性

二、技术实现亮点

1、前端技术栈
1)Uni-app 框架:实现一套代码多端运行
2)Vue.js 响应式数据绑定:确保界面与数据实时同步
3)Uni-ui 组件库:提供丰富的 UI 组件支持
4)原生 CSS3 动画:提升用户交互体验
2、核心技术特性
1)模块化架构设计,代码组织清晰
2)响应式布局适配,支持各种屏幕尺寸
3)智能状态管理,优化应用性能
4)完善的错误处理机制
5)优雅的加载状态和错误提示
3、用户体验优化
1)流畅的页面切换动画
2)直观的答题状态标识
3)智能交卷提醒,未答题目提示
4)详细的成绩分析报告
5)友好的操作反馈机制

三、系统优势

1、跨平台兼容性:基于 Uni-app 开发,可同时运行于 iOS、Android、H5 等多个平台
2、性能优化:静态资源本地加载,无需网络请求,响应速度快
3、用户体验:现代化 UI 设计,流畅的交互动画,直观的操作流程
4、功能完整:从练习到考试,从答题到成绩分析,覆盖完整学习周期
5、易于维护:模块化代码结构,清晰的逻辑分层,便于后续功能扩展
代码如下:

主页面
<template><view class="container"><!-- 添加加载状态提示 --><view v-if="loading" class="loading-container"><activity-indicator size="large" color="#007AFF" /><text class="loading-text">正在加载题目数据...</text></view><!-- 主按钮区域 --><view v-else class="button-container"><button class="practice-btn":disabled="!dataReady"@tap="goPractice"><text class="btn-text">进入题库练习</text><text v-if="!dataReady" class="btn-tip">(题目加载中)</text></button><button class="mock-btn":disabled="!dataReady"@tap="startMockExam"><text class="btn-text">开始模拟考试</text><text v-if="!dataReady" class="btn-tip">(题目加载中)</text></button></view><!-- 错误提示 --><view v-if="error" class="error-container"><text class="error-text">题目加载失败,请检查数据文件</text><button class="retry-btn" @tap="loadAllQuestions"><text>重新加载</text></button></view></view>
</template><script>
// 方案一:使用import静态导入(推荐)
import singleData from '@/static/data/single.json'
import multiData from '@/static/data/multi.json'
import judgeData from '@/static/data/judge.json'export default {data() {return {singleQuestions: [],multiQuestions: [],judgeQuestions: [],loading: true,error: false}},computed: {dataReady() {return (this.singleQuestions.length > 0 &&this.multiQuestions.length > 0 &&this.judgeQuestions.length > 0)}},onLoad() {this.loadAllQuestions()},methods: {// 修改后的加载方法loadAllQuestions() {this.loading = truethis.error = falsetry {// 直接使用导入的数据this.singleQuestions = singleDatathis.multiQuestions = multiDatathis.judgeQuestions = judgeDataconsole.log('题目加载完成', {single: this.singleQuestions.length,multi: this.multiQuestions.length,judge: this.judgeQuestions.length})// 存储到全局getApp().globalData.questions = {single: this.singleQuestions,multi: this.multiQuestions,judge: this.judgeQuestions}} catch (e) {console.error('加载题目失败:', e)this.error = true} finally {this.loading = false}},goPractice() {if (!this.dataReady) {uni.showToast({title: '题目尚未加载完成',icon: 'none'})return}uni.navigateTo({url: '/pages/practice/practice'})},startMockExam() {if (!this.dataReady) {uni.showToast({title: '题目尚未加载完成',icon: 'none'})return}// 随机组卷(30单选+20多选+10判断)const examQuestions = [...this.getRandomQuestions(this.singleQuestions, 30),...this.getRandomQuestions(this.multiQuestions, 20),...this.getRandomQuestions(this.judgeQuestions, 10)]// 存储到全局getApp().globalData.examQuestions = this.shuffleArray(examQuestions)uni.navigateTo({url: '/pages/mock/mock'})},// 随机选题getRandomQuestions(questions, count) {const shuffled = [...questions].sort(() => Math.random() - 0.5)return shuffled.slice(0, count)},// 打乱数组顺序shuffleArray(array) {const result = [...array]for (let i = result.length - 1; i > 0; i--) {const j = Math.floor(Math.random() * (i + 1));[result[i], result[j]] = [result[j], result[i]]}return result}}
}
</script><style>
.container {padding: 40rpx;height: 100vh;display: flex;flex-direction: column;justify-content: center;background-color: #f8f8f8;
}.loading-container {flex: 1;display: flex;flex-direction: column;justify-content: center;align-items: center;
}.loading-text {margin-top: 30rpx;font-size: 28rpx;color: #666;
}.button-container {width: 100%;display: flex;flex-direction: column;gap: 40rpx;
}button {height: 120rpx;border-radius: 16rpx;display: flex;flex-direction: column;justify-content: center;align-items: center;font-size: 32rpx;color: white;box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
}button[disabled] {opacity: 0.6;
}.practice-btn {background: linear-gradient(135deg, #007AFF, #00a8ff);
}.mock-btn {background: linear-gradient(135deg, #FF9500, #FFB700);
}.btn-text {font-weight: bold;font-size: 36rpx;
}.btn-tip {font-size: 24rpx;opacity: 0.8;margin-top: 8rpx;
}.error-container {margin-top: 60rpx;display: flex;flex-direction: column;align-items: center;
}.error-text {color: #FF4D4F;font-size: 28rpx;margin-bottom: 30rpx;text-align: center;
}.retry-btn {padding: 20rpx 40rpx;background-color: #007AFF;color: white;border-radius: 10rpx;
}
</style>
练习题页面
<template><view class="container"><!-- 顶部题目类型标识 --><view class="question-type"><text>{{currentType === 'single' ? '单选题' : currentType === 'multi' ? '多选题' : '判断题'}}</text></view><!-- 题型切换按钮 --><view class="type-switch"><button class="type-btn" :class="{active: currentType === 'single'}"@click="switchType('single')">单选题</button><button class="type-btn" :class="{active: currentType === 'multi'}"@click="switchType('multi')">多选题</button><button class="type-btn" :class="{active: currentType === 'judge'}"@click="switchType('judge')">判断题</button></view><!-- 当前题目 --><view v-if="currentQuestion" class="question-box"><text class="q-title">{{currentPage}}. {{currentQuestion.question}}</text><!-- 多选题提示 --><view v-if="currentType === 'multi'" class="multi-tip"><text>(多选题,请选择所有正确答案)</text></view><view class="options-container"><view v-for="(opt, i) in currentQuestion.options" :key="i" class="option":style="{ backgroundColor: getOptionColor(currentQuestion, i) }"@click="choose(currentQuestion, i)"><!-- 多选题显示正确对号和错误叉号 --><view v-if="currentType === 'multi' && currentQuestion.selected.includes(i)" class="checkmark">{{ currentQuestion.answer.includes(i) ? '✓' : '✗' }}</view><!-- 单选题显示选择对错标记 --><view v-if="currentType === 'single' && currentQuestion.selected !== -1 && currentQuestion.selected === i" class="checkmark">{{ currentQuestion.answer.includes(i) ? '✓' : '✗' }}</view><text class="option-text">{{getOptionPrefix(i)}} {{opt}}</text></view></view></view><!-- 分页控制 --><view class="pagination"><button @click="prevPage" :disabled="currentPage === 1">上一题</button><text>{{currentPage}}/{{questions.length}}</text><button @click="nextPage" :disabled="currentPage === questions.length">下一题</button></view></view>
</template><script>
// 直接导入 JSON 数据,打包后可用
import singleData from '@/static/data/single.json';
import multiData from '@/static/data/multi.json';
import judgeData from '@/static/data/judge.json';export default {data() {return {questions: [],currentPage: 1,autoNextTimer: null,currentType: 'single',loading: false}},computed: {currentQuestion() {return this.questions[this.currentPage - 1];}},onLoad() {this.loadQuestions('single');},methods: {loadQuestions(type) {this.loading = true;this.currentType = type;this.currentPage = 1;let data = [];switch(type) {case 'single': data = singleData; break;case 'multi': data = multiData; break;case 'judge': data = judgeData; break;}// 格式化题目this.questions = data.map(q => {if (type === 'judge') {return {...q,options: ['正确','错误'],selected: -1,answer: Array.isArray(q.answer) ? q.answer : [q.answer]};} else if (type === 'multi') {return {...q, selected: []};} else { // 单选return {...q, selected: -1, answer: Array.isArray(q.answer) ? q.answer : [q.answer]};}});this.loading = false;},getOptionPrefix(i) {if (this.currentType === 'judge') return '';return String.fromCharCode(65 + i) + '.';},switchType(type) {if (this.currentType !== type) this.loadQuestions(type);},choose(q, i) {if (this.currentType === 'multi') {const idx = q.selected.indexOf(i);if (idx === -1) q.selected.push(i);else q.selected.splice(idx,1);} else {if (q.selected !== -1) return;q.selected = i;// 单选题:选择正确自动跳下一题if (this.currentType === 'single' && q.answer.includes(i)) {this.setAutoNext();}// 判断题:0表示正确选项,自动跳下一题if (this.currentType === 'judge') {if (q.answer.includes(i) && i===0) this.setAutoNext();}}},getOptionColor(q,i) {if (this.currentType === 'multi') {if (q.selected.includes(i)) return q.answer.includes(i) ? '#4CAF50' : '#F44336';return '#f5f5f5';} else if (this.currentType === 'single') {if (q.selected === -1) return '#f5f5f5';if (q.answer.includes(i)) return '#4CAF50';if (q.selected === i && !q.answer.includes(i)) return '#F44336';return '#f5f5f5';} else { // 判断题if (q.selected === -1) return '#f5f5f5';if (q.answer.includes(i)) return '#4CAF50';if (q.selected===i && !q.answer.includes(i)) return '#F44336';return '#f5f5f5';}},prevPage() {this.clearAutoNext();if (this.currentPage > 1) this.currentPage--;},nextPage() {this.clearAutoNext();if (this.currentPage < this.questions.length) this.currentPage++;},setAutoNext() {this.clearAutoNext();this.autoNextTimer = setTimeout(()=>{if(this.currentPage < this.questions.length) this.currentPage++;},1000);},clearAutoNext() {if(this.autoNextTimer) {clearTimeout(this.autoNextTimer);this.autoNextTimer=null;}}},beforeDestroy() {this.clearAutoNext();}
}
</script><style>
.container {padding:20rpx; display:flex; flex-direction:column; height:100vh; color:#000;}
.question-type {padding:10rpx 20rpx; background-color:#2196F3; color:#fff; border-radius:20rpx; align-self:flex-start; margin-bottom:20rpx; font-size:28rpx; font-weight:bold;}
.type-switch {display:flex; justify-content:space-between; margin-bottom:20rpx;}
.type-btn {flex:1; margin:0 10rpx; padding:15rpx; font-size:28rpx; border-radius:8rpx; background-color:#f0f0f0; color:#000;}
.type-btn.active {background-color:#2196F3; color:#fff;}
.question-box {flex:1; margin:20rpx 0; padding:30rpx; border:1px solid #eee; border-radius:15rpx; background-color:#fff; display:flex; flex-direction:column;}
.multi-tip {padding:10rpx 0; color:#666; font-size:26rpx; margin-bottom:20rpx;}
.q-title {font-weight:bold; margin-bottom:20rpx; font-size:36rpx; line-height:1.6; color:#000;}
.options-container {flex:1; display:flex; flex-direction:column; justify-content:space-around;}
.option {width:100%; padding:30rpx; margin:15rpx 0; border-radius:12rpx; color:#000; box-sizing:border-box; font-size:32rpx; transition:all 0.3s; display:flex; align-items:center; min-height:120rpx; position:relative;}
.checkmark {position:absolute; left:20rpx; color:#fff; font-weight:bold; font-size:28rpx;}
.option-text {width:100%; word-break:break-word; margin-left:40rpx;}
.option:active {opacity:0.8; transform:scale(0.98);}
.pagination {display:flex; justify-content:space-between; align-items:center; padding:20rpx 0; margin:20rpx 0;}
button {padding:15rpx 40rpx; font-size:32rpx; border-radius:8rpx; background-color:#f0f0f0; color:#000;}
button:disabled {opacity:0.5;}
text {color:#000 !important;}
</style>
模拟考试页面
<template><view class="exam-container"><!-- 考试进行中视图 --><view v-if="!examFinished"><!-- 考试头部信息 --><view class="exam-header"><text class="exam-title">模拟考试</text><view class="exam-info"><text class="question-type">{{getQuestionType(currentQuestion.type)}}</text><text>题号: {{currentIndex + 1}}/{{questions.length}}</text><text class="timer" :class="{ 'warning': remainingTime < 600 }">时间: {{formatTime(remainingTime)}}</text></view></view><!-- 考试题目 --><view class="question-container"><view class="question-box"><!-- 修复:确保题目为空时不报错 --><text class="q-title" v-if="currentQuestion.id">{{currentQuestion.id}}. {{currentQuestion.question}}</text><text class="q-title" v-else>加载题目中...</text><!-- 选项容器:修复选中背景不显示问题 --><view class="options-container" v-if="currentQuestion.options"><view v-for="(opt, i) in currentQuestion.options" :key="i"class="option":class="{'selected': isOptionSelected(i), // 核心:确保选中状态生效'correct': showAnswer && currentQuestion.answer.includes(i),'wrong': showAnswer && isOptionSelected(i) && !currentQuestion.answer.includes(i)}"@click="handleSelectOption(i)":disabled="examFinished"  ><text class="option-text">{{getOptionPrefix(i)}}. {{opt}}</text></view></view><view v-else class="loading-options">选项加载中...</view></view></view><!-- 考试控制按钮 --><view class="exam-controls"><button class="control-btn prev-btn":disabled="currentIndex === 0 || examFinished"@click="prevQuestion">上一题</button><button class="control-btn next-btn":disabled="currentIndex === questions.length - 1 || examFinished"@click="nextQuestion">下一题</button><button class="control-btn submit-btn":disabled="examFinished" @click="confirmSubmit">提交试卷</button></view><!-- 答题卡:显示已答/未答状态 --><view class="answer-sheet"><text class="sheet-title">答题卡 ({{answeredCount}}/{{questions.length}})</text><view class="sheet-questions"><view v-for="(q, index) in questions":key="q.id"class="sheet-item":class="{'answered': isQuestionAnswered(q), 'unanswered': !isQuestionAnswered(q), 'current': index === currentIndex}"@click="jumpToQuestion(index)":disabled="examFinished">{{index + 1}}</view></view></view></view><!-- 考试结果详情视图 --><view v-if="examFinished && showResultDetail"><view class="result-header"><text class="result-title">考试结果</text><view class="result-summary"><text class="score">得分: {{score.toFixed(1)}}/100.0</text><text class="evaluation">{{evaluation}}</text></view><button class="back-btn" @click="exitExam">返回</button></view><view class="result-detail"><view class="filter-controls"><button class="filter-btn" :class="{ 'active': filterType === 'all' }" @click="filterType = 'all'">全部题目</button><button class="filter-btn" :class="{ 'active': filterType === 'correct' }" @click="filterType = 'correct'">做对 ({{correctCount}})</button><button class="filter-btn" :class="{ 'active': filterType === 'wrong' }" @click="filterType = 'wrong'">做错/少选 ({{wrongOrPartialCount}})</button><button class="filter-btn" :class="{ 'active': filterType === 'unanswered' }" @click="filterType = 'unanswered'">未答 ({{unansweredCount}})</button></view><view class="questions-detail"><view v-for="(q, index) in filteredQuestions" :key="q.id"class="question-detail-item":class="{ 'correct': isQuestionCorrect(q), 'wrong': isQuestionWrong(q),'partial': isQuestionPartial(q),'unanswered': !isQuestionAnswered(q)}"><view class="question-header"><text class="question-num">{{index + 1}}. {{getQuestionType(q.type)}}</text><text class="question-status">{{!isQuestionAnswered(q) ? '未答' : (isQuestionCorrect(q) ? '全对' : (isQuestionPartial(q) ? '少选' : '错误'))}}</text></view><view class="question-content">{{q.question}}</view><!-- 显示每题得分(修复:少选显示0.5分) --><view class="question-score" v-if="isQuestionAnswered(q)">本题得分: {{getQuestionScore(q)}}/{{getQuestionFullScore(q)}}</view><view class="question-score" v-else>本题得分: 0/{{getQuestionFullScore(q)}}(未答)</view><view class="options-detail"><view v-for="(opt, i) in q.options" :key="i"class="detail-option":class="{'user-selected': isUserSelected(q, i), 'correct-answer': q.answer.includes(i),'wrong-selected': isUserSelected(q, i) && !q.answer.includes(i)}"><text class="option-prefix">{{getOptionPrefix(i)}}. </text><text class="option-content">{{opt}}</text><text v-if="q.answer.includes(i)" class="correct-mark">✓ 正确答案</text><text v-if="isUserSelected(q, i) && !q.answer.includes(i)" class="wrong-mark">✗ 你的答案</text><text v-if="isUserSelected(q, i) && q.answer.includes(i)" class="your-answer-mark">✓ 你的答案</text></view></view></view></view></view></view><!-- 提交确认弹窗 --><uni-popup ref="popup" type="dialog" :mask="true"><uni-popup-dialog type="info"title="确认提交"content="确定要提交试卷吗?提交后无法修改答案!"@confirm="submitExam"@close="closePopup"></uni-popup-dialog></uni-popup><!-- 考试结果弹窗 --><uni-popup ref="resultPopup" type="dialog" :mask="true"><uni-popup-dialog type="info"title="考试完成!":content="`本次考试得分:${score.toFixed(1)}/100.0\n${evaluation}\n\n是否查看详细答题情况?`"confirmText="查看详情"cancelText="直接返回"@confirm="handleShowDetail" @cancel="exitExam"></uni-popup-dialog></uni-popup></view>
</template><script>
import singleData from '@/static/data/single.json';
import multiData from '@/static/data/multi.json';
import judgeData from '@/static/data/judge.json';
import uniPopup from '@dcloudio/uni-ui/lib/uni-popup/uni-popup.vue';
import uniPopupDialog from '@dcloudio/uni-ui/lib/uni-popup-dialog/uni-popup-dialog.vue';export default {components: { uniPopup, uniPopupDialog },data() {return {questions: [],currentIndex: 0,remainingTime: 5400,timer: null,showAnswer: false,examFinished: false,showResultDetail: false,score: 0,evaluation: '',filterType: 'all'}},computed: {currentQuestion() {// 修复:确保currentIndex合法时才返回题目,避免空对象if (this.questions.length && this.currentIndex >= 0 && this.currentIndex < this.questions.length) {return this.questions[this.currentIndex];}return { id: '', question: '', options: [], answer: [], type: '' };},answeredCount() {return this.questions.filter(q => this.isQuestionAnswered(q)).length;},unansweredCount() {return this.questions.filter(q => !this.isQuestionAnswered(q)).length;},correctCount() {return this.questions.filter(q => this.isQuestionAnswered(q) && this.isQuestionCorrect(q)).length;},wrongOrPartialCount() {return this.questions.filter(q => this.isQuestionAnswered(q) && (!this.isQuestionCorrect(q) || this.isQuestionPartial(q))).length;},filteredQuestions() {switch(this.filterType) {case 'all':return this.questions;case 'correct':return this.questions.filter(q => this.isQuestionAnswered(q) && this.isQuestionCorrect(q));case 'wrong':return this.questions.filter(q => this.isQuestionAnswered(q) && (!this.isQuestionCorrect(q) || this.isQuestionPartial(q)));case 'unanswered':return this.questions.filter(q => !this.isQuestionAnswered(q));default:return this.questions;}}},onLoad() {this.initExam();this.startTimer();},onUnload() {this.stopTimer();},methods: {initExam() {try {// 修复:确保JSON数据格式正确,避免空数据const validSingle = singleData.filter(q => q.id && q.question && q.options && q.answer);const validMulti = multiData.filter(q => q.id && q.question && q.options && q.answer);const validJudge = judgeData.filter(q => q.id && q.question && q.options && q.answer);const singleQuestions = this.getRandomQuestions(validSingle, 40).map(q => ({...q,type: 'single',selected: -1 // 单选初始未选:-1}));const multiQuestions = this.getRandomQuestions(validMulti, 20).map(q => ({...q,type: 'multi',selected: [] // 多选初始未选:空数组}));const judgeQuestions = this.getRandomQuestions(validJudge, 40).map(q => ({...q,type: 'judge',selected: -1 // 判断初始未选:-1}));this.questions = [...singleQuestions, ...multiQuestions, ...judgeQuestions];} catch (error) {console.error('初始化考试失败:', error);uni.showToast({ title: '加载题目失败', icon: 'none' });setTimeout(() => { uni.navigateBack(); }, 1500);}},getRandomQuestions(questions, count) {const shuffled = [...questions].sort(() => Math.random() - 0.5);return shuffled.slice(0, count);},startTimer() {this.timer = setInterval(() => {if (this.remainingTime > 0) {this.remainingTime--;} else {this.stopTimer();this.autoSubmitExam();}}, 1000);},stopTimer() {if (this.timer) {clearInterval(this.timer);this.timer = null;}},formatTime(seconds) {const mins = Math.floor(seconds / 60);const secs = seconds % 60;return `${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;},getQuestionType(type) {const types = { single:'单选题(1分)', multi:'多选题(2分)', judge:'判断题(0.5分)' };return types[type] || '未知题型';},getOptionPrefix(index) {return String.fromCharCode(65 + index);},// 修复:选中状态判断,确保单选/多选都能正确识别isOptionSelected(index) {const question = this.currentQuestion;if (!question || question.selected === undefined) return false;if (question.type === 'multi') {// 多选:判断索引是否在selected数组中return Array.isArray(question.selected) && question.selected.includes(index);} else {// 单选/判断:判断selected是否等于索引(排除-1未选状态)return question.selected !== -1 && question.selected === index;}},isQuestionAnswered(question) {if (!question || question.selected === undefined) return false;if (question.type === 'multi') {return Array.isArray(question.selected) && question.selected.length > 0;} else {return question.selected !== -1;}},isQuestionCorrect(question) {if (!this.isQuestionAnswered(question)) return false;if (question.type === 'multi') {return question.selected.length === question.answer.length &&question.selected.every(opt => question.answer.includes(opt));} else {return question.answer.includes(question.selected);}},isQuestionPartial(question) {if (question.type !== 'multi' || !this.isQuestionAnswered(question)) return false;// 少选条件:无错选 + 未选全const noWrongSelection = question.selected.every(opt => question.answer.includes(opt));const notFullSelection = question.selected.length < question.answer.length;return noWrongSelection && notFullSelection;},isQuestionWrong(question) {if (!this.isQuestionAnswered(question)) return false;if (question.type === 'multi') {// 多选错误:有错选return question.selected.some(opt => !question.answer.includes(opt));} else {// 单选/判断错误:选中≠正确答案return !question.answer.includes(question.selected);}},// 核心修改:多选题少选得分从1分改为0.5分getQuestionScore(question) {if (!this.isQuestionAnswered(question)) return 0;switch(question.type) {case 'single':return this.isQuestionCorrect(question) ? 1 : 0;case 'multi':if (this.isQuestionCorrect(question)) return 2; // 全对:2分if (this.isQuestionPartial(question)) return 0.5; // 少选:0.5分(原1分)return 0; // 错选:0分case 'judge':return this.isQuestionCorrect(question) ? 0.5 : 0;default:return 0;}},getQuestionFullScore(question) {switch(question.type) {case 'single': return 1;case 'multi': return 2;case 'judge': return 0.5;default: return 0;}},isUserSelected(question, index) {if (!question || question.selected === undefined) return false;if (question.type === 'multi') {return Array.isArray(question.selected) && question.selected.includes(index);} else {return question.selected !== -1 && question.selected === index;}},// 修复:选项选择逻辑,确保响应式更新生效handleSelectOption(index) {if (this.examFinished) return;const question = this.currentQuestion;if (!question) return;// 用$set确保Vue能监听到selected的变化(响应式核心)if (question.type === 'multi') {const selected = [...(question.selected || [])];const idx = selected.indexOf(index);if (idx === -1) {selected.push(index);} else {selected.splice(idx, 1);}this.$set(this.questions[this.currentIndex], 'selected', selected);} else {this.$set(this.questions[this.currentIndex], 'selected', index);}},prevQuestion() {if (this.currentIndex > 0 && !this.examFinished) {this.currentIndex--;}},nextQuestion() {if (this.currentIndex < this.questions.length - 1 && !this.examFinished) {this.currentIndex++;}},jumpToQuestion(index) {if (!this.examFinished && index >= 0 && index < this.questions.length) {this.currentIndex = index;}},confirmSubmit() {if (this.examFinished) return;if (this.unansweredCount > 0) {uni.showModal({title: '提示',content: `仍有${this.unansweredCount}道题未答,确定要提交吗?`,success: (res) => {if (res.confirm) {this.$refs.popup.open();}}});} else {this.$refs.popup.open();}},closePopup() {this.$refs.popup.close();},autoSubmitExam() {uni.showModal({title: '考试时间到',content: '考试时间已结束,系统将自动提交您的答案',showCancel: false,success: () => {this.submitExam();}});},submitExam() {this.stopTimer();this.examFinished = true;this.score = this.calculateScore();this.evaluation = this.getEvaluation(this.score, 100);this.$refs.popup.close();setTimeout(() => {if (this.$refs.resultPopup) {this.$refs.resultPopup.open();} else {uni.showToast({ title: '分数已计算完成', icon: 'none' });this.showResultDetail = true;}}, 100);},calculateScore() {let total = 0;this.questions.forEach(q => {total += this.getQuestionScore(q);});return total;},getEvaluation(score, total) {const percent = (score / total) * 100;if (percent < 60) return '评价: 继续努力!';if (percent < 90) return '评价: 还不错!';if (percent < 100) return '评价: 你真棒!';return '评价: 完美!全部答对!';},handleShowDetail() {this.showResultDetail = true;this.$refs.resultPopup.close();},exitExam() {if (this.$refs.resultPopup) {this.$refs.resultPopup.close();}uni.navigateBack();}}
}
</script><style>
/* 基础样式保持不变,强化选中状态样式 */
.exam-container {padding: 20rpx;display: flex;flex-direction: column;height: 100vh;background-color: #f5f5f5;
}/* 修复:确保选中选项背景色明显生效 */
.option {padding: 25rpx;border-radius: 12rpx;background-color: #f5f5f5;transition: all 0.3s ease; /* 增加过渡效果,选中更流畅 */border: 2rpx solid transparent; /* 透明边框,选中时突出 */
}
/* 选中状态:蓝色背景+白色文字+边框强化 */
.option.selected {background-color: #2196F3 !important; /* !important确保优先级,避免被覆盖 */color: white !important;border-color: #1976D2;box-shadow: 0 4rpx 8rpx rgba(33, 150, 243, 0.2); /* 增加阴影,更醒目 */
}/* 加载状态样式 */
.loading-options {font-size: 28rpx;color: #666;padding: 30rpx;text-align: center;
}/* 答题卡样式 */
.sheet-item.unanswered {background-color: #e0e0e0;color: #666;
}
.sheet-item.answered {background-color: #2196F3;color: white;
}/* 结果页样式 */
.detail-option.user-selected {background-color: #e3f2fd;border-left: 4rpx solid #2196F3;
}
.detail-option.correct-answer {background-color: #e8f5e9;border-left: 4rpx solid #4CAF50;
}
.detail-option.wrong-selected {background-color: #ffebee;border-left: 4rpx solid #F44336;
}.your-answer-mark {color: #2196F3;font-size: 24rpx;margin-left: 10rpx;font-weight: bold;
}.question-detail-item.unanswered {border-left-color: #9e9e9e;background-color: #f5f5f5;
}
.question-detail-item.unanswered .question-status {background-color: #e0e0e0;color: #666;
}/* 其他原有样式保持不变 */
.exam-header {padding: 20rpx;background-color: #2196F3;color: white;border-radius: 10rpx;margin-bottom: 20rpx;
}.exam-title {font-size: 36rpx;font-weight: bold;display: block;margin-bottom: 10rpx;
}.exam-info {display: flex;justify-content: space-between;font-size: 28rpx;
}.question-type {font-weight: bold;color: #FFEB3B;
}.timer {color: #FFEB3B;font-weight: bold;
}.timer.warning {color: #FF5252;animation: flash 1s infinite alternate;
}@keyframes flash {from { opacity: 1; }to { opacity: 0.7; }
}.question-container {flex: 1;margin-bottom: 20rpx;overflow-y: auto;
}.question-box {background-color: #fff;border-radius: 15rpx;padding: 30rpx;box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}.q-title {font-size: 32rpx;line-height: 1.6;margin-bottom: 30rpx;color: #333;display: block;
}.options-container {display: flex;flex-direction: column;gap: 20rpx;
}.option.correct {background-color: #4CAF50;color: white;
}.option.wrong {background-color: #F44336;color: white;
}.exam-controls {display: flex;justify-content: space-between;margin-bottom: 20rpx;
}.control-btn {flex: 1;margin: 0 10rpx;padding: 20rpx;font-size: 28rpx;border-radius: 8rpx;color: white;border: none;
}.prev-btn {background-color: #607D8B;
}.next-btn {background-color: #2196F3;
}.submit-btn {background-color: #FF5722;
}.answer-sheet {background-color: #fff;border-radius: 15rpx;padding: 20rpx;box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);margin-bottom: 20rpx;
}.sheet-title {font-size: 30rpx;font-weight: bold;margin-bottom: 15rpx;display: block;
}.sheet-questions {display: flex;flex-wrap: wrap;gap: 15rpx;max-height: 200rpx;overflow-y: auto;padding-bottom: 10rpx;
}.sheet-item {width: 80rpx;height: 80rpx;border-radius: 50%;display: flex;justify-content: center;align-items: center;font-size: 28rpx;
}.sheet-item.current {border: 2rpx solid #FF5722;box-sizing: border-box;
}.result-header {background-color: #2196F3;color: white;padding: 20rpx;border-radius: 10rpx;margin-bottom: 20rpx;
}.result-title {font-size: 36rpx;font-weight: bold;display: block;margin-bottom: 10rpx;
}.result-summary {display: flex;justify-content: space-between;font-size: 28rpx;margin-bottom: 15rpx;
}.score {color: #FFEB3B;font-weight: bold;
}.evaluation {font-style: italic;
}.back-btn {background-color: #FF5722;color: white;border: none;padding: 15rpx 30rpx;border-radius: 8rpx;font-size: 28rpx;
}.result-detail {flex: 1;overflow-y: auto;
}.filter-controls {display: flex;justify-content: space-around;margin-bottom: 20rpx;flex-wrap: wrap;gap: 10rpx;
}.filter-btn {background-color: #e0e0e0;border: none;padding: 15rpx 20rpx;border-radius: 8rpx;font-size: 26rpx;
}.filter-btn.active {background-color: #2196F3;color: white;
}.questions-detail {display: flex;flex-direction: column;gap: 20rpx;
}.question-detail-item {background-color: #fff;border-radius: 15rpx;padding: 25rpx;box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);border-left: 8rpx solid;
}.question-detail-item.correct {border-left-color: #4CAF50;
}.question-detail-item.wrong {border-left-color: #F44336;
}.question-detail-item.partial {border-left-color: #FFC107;
}.question-header {display: flex;justify-content: space-between;margin-bottom: 15rpx;font-size: 28rpx;
}.question-num {font-weight: bold;color: #333;
}.question-status {padding: 5rpx 15rpx;border-radius: 20rpx;font-size: 24rpx;
}.question-detail-item.correct .question-status {background-color: #e8f5e9;color: #2e7d32;
}.question-detail-item.wrong .question-status {background-color: #ffebee;color: #c62828;
}.question-detail-item.partial .question-status {background-color: #fff8e1;color: #ff8f00;
}.question-content {font-size: 28rpx;margin-bottom: 15rpx;line-height: 1.6;
}.question-score {font-size: 24rpx;color: #666;margin-bottom: 15rpx;font-style: italic;
}.options-detail {display: flex;flex-direction: column;gap: 15rpx;
}.detail-option {padding: 15rpx;border-radius: 8rpx;font-size: 26rpx;display: flex;flex-wrap: wrap;align-items: center;
}.option-prefix {font-weight: bold;margin-right: 10rpx;
}.option-content {flex: 1;
}.correct-mark {color: #4CAF50;font-size: 24rpx;margin-left: 10rpx;font-weight: bold;
}.wrong-mark {color: #F44336;font-size: 24rpx;margin-left: 10rpx;font-weight: bold;
}
</style>
单选题JSON数据库
题库内容涉及敏感词无法放置格式如下
[{"id": 1,"type": "single","question": "","options": ["规章制度","自治条例","国家法规政策","地方性法规"],"answer": [2]},{"id": 2,"type": "single","question": "","options": ["规范","平稳","有序","安全"],"answer": [3]}
]  
多选题JSON数据库
题库内容涉及敏感词无法放置格式如下
[{"id": 401,"type": "multi","question": "《安全生产管理知识》指导用书把施工过程中的安全生产管理知识分别设置为( )。","options": ["管理","实操","法规","法律","政策"],"answer": [0,2,4]},{"id": 402,"type": "multi","question": "安全生产管理一般包括( )。","options": ["","安全生产管理的理论学说","安全生产管理的经济学说","安全生产管理的实践和经验",""],"answer": [0,1,3,4]}
]
判断题JSON数据库
题库内容涉及敏感词无法放置格式如下
[{"id": 601,"type": "judge","question": "安全生产管理一般包括四个部分,再往外延伸还会涉及安全生产管理的甲方服务等。","options": ["正确", "错误"],"answer": [1]},{"id": 602,"type": "judge","question": "","options": ["正确", "错误"],"answer": [1]}
]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/937464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年上海律师服务最新权威推荐榜:经侦律师,民事/刑事律师,经济/婚姻律师,法务律师,负债律师事务所专业解析

2025年上海律师服务最新权威推荐榜:经侦律师,民事/刑事律师,经济/婚姻律师,法务律师,负债律师事务所专业解析一、行业发展趋势与法律服务新格局随着上海国际金融中心建设的深入推进,法律服务市场正经历着前所未有…

2025 年永磁电机厂家推荐台州市台成机电,单相永磁电机,三相永磁电机,变频永磁电机,稀土永磁电机,直流永磁电机,无刷永磁电机,风机永磁电机,节能永磁电机,高效永磁电机公司推荐

行业背景2025 年成为电机行业转型关键节点,新国标将高压电机最低能效要求提升至 IE4 水平,高效节能电机占比需超 70%。永磁同步电机凭借 IE4/IE5 级能效优势,在专用电动机市场份额预计超 65%,广泛应用于工业、石化…

2025年实验室净化/手术室净化/洁净室工程厂家最新权威推荐榜:专业建设与无尘车间装修一站式解决方案

2025年实验室净化/手术室净化/洁净室工程厂家最新权威推荐榜:专业建设与无尘车间装修一站式解决方案行业背景与发展趋势随着生命科学、生物医药、精密制造等行业的快速发展,对洁净环境的需求日益增长。实验室净化、手…

GoBox:跨境电商AI智能选品与运营助手

GoBox:跨境电商AI智能选品与运营助手跨境电商作为全球零售的新兴力量,正在以前所未有的速度增长。然而,面对全球市场的多样化需求与复杂的运营管理,传统的选品与运营方式已难以满足卖家日益增长的需求。为此,跨境…

深入理解 `itertools`:分类解析常用函数 (Effective Python 第36条) - 教程

深入理解 `itertools`:分类解析常用函数 (Effective Python 第36条) - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fami…

linux配置环境变量

1、首先打开.bashrc文件 2、添加信息到文件末尾:export JAVA_HOME="/www/server/java/jdk1.8.0_371" export MVN="/root/app/apache-maven-3.9.11" export PATH=$JAVA_HOME/bin:$MVN/bin:$PATH3、…

assert的基本用法

一、assert 是什么 assert 是 Python 提供的一种 调试语句,用于在程序运行时做“自检”, 验证一个表达式是否为真(True)。 它的作用就像一个 自动检测点(checkpoint): 如果条件为 True → 程序继续执行 如果条件…

1688代发铺货规格匹配设置

自动匹配有问题时,可以手动匹配,位置在"分销"->"已铺货商品"页面,商品行"更多"中"匹配规格"操作

2025年整平机厂家最新推荐排行榜,精密整平机,数控整平机,自动整平机,板材整平机源头厂家专业选购指南

2025年整平机厂家最新推荐排行榜:精密整平机、数控整平机、自动整平机、板材整平机源头厂家专业选购指南行业背景与发展趋势随着制造业向智能化、精密化方向快速发展,整平机作为金属板材加工领域的关键设备,其技术水…

task2

任务1#include <stdio.h> #include <stdlib.h>#include <time.h> #define N 5 #define N1 80 #define N2 35 int main(){int cnt;int random_major,random_no;srand(time(NULL));cnt=0;while(cnt &l…

KiloCode 与 Claude Code 在长上下文文件写入操作中的稳定性差异深度解析

KiloCode 与 Claude Code 在长上下文文件写入操作中的稳定性差异深度解析 在人工智能辅助编程领域,工具的稳定性和可靠性对于开发者而言至关重要。随着项目规模和复杂性的增加,开发者越来越依赖这些工具来处理繁琐的…

2025年机械加工厂家最新权威推荐榜:钣金/焊接/零件/非标自动化/精密金属加工,专业定制与技术创新实力解析

2025年机械加工厂家最新权威推荐榜:钣金/焊接/零件/非标自动化/精密金属加工,专业定制与技术创新实力解析随着制造业向智能化、精密化方向加速转型,机械加工行业正经历着深刻的技术变革。在工业4.0和智能制造的推动…

2025年10月15号随笔

2025年10月15号随笔今年是一个崭新的一年也是极具挑战的一年,做了几年的电动车业务也全面转型了,虽然转型路上不是那么平坦。但是也是到了不得不的程度,希望是一个很好的开始,并且我坚信我可以再别的类目茁壮成长发…

Ubuntu20.04安装NVIDIA显卡驱动、CUDA Toolkit、cuDNN步骤(二) - 指南

Ubuntu20.04安装NVIDIA显卡驱动、CUDA Toolkit、cuDNN步骤(二) - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: …

java基础7-ArrayList

1.集合的基本使用: (1)集合和数组的对比:长度:数组长度固定,集合长度可变 存储类型:数组可以存储基本数据类型也可以存引用数据类型,但集合只能存储引用数据类型,如果需要存储基本数据类型需要把它们变成对应的…

两个用于从B站下载视频的Shell脚本

我在下载四郎讲棋的、最多收藏的、时长低于10分钟的 用于人工收集URL的:prev= while clipnotify; do t=`xclip -o` if ! [[ "$t" =~ ^http ]]; then continue; fi if [[ "$t" == $prev ]]; then …

2025年冲压件厂家最新权威推荐榜:新能源/光伏/精密/异形/五金/铝/汽配/不锈钢/家具冲压件源头厂商深度解析

2025年冲压件厂家最新权威推荐榜:新能源/光伏/精密/异形/五金/铝/汽配/不锈钢/家具冲压件源头厂商深度解析行业背景与发展趋势冲压件作为现代制造业的基础零部件,其技术水平和产品质量直接影响着终端产品的性能与可靠…

P3800 Power 收集和单调队列优化dp小总结

前言 历尽千辛万苦,终于在自己和老师的帮助下把P3800 Power 收集给过了,有一些trick要讲 P3800 Power 题目背景 据说在红雾异变时,博丽灵梦单身前往红魔馆,用十分强硬的手段将事件解决了。 然而当时灵梦在 Power 达…

微信群机器人接口

微信群机器人接口、个微协议/微信号二次开发/ipad协议、个人微信号二次开发api协议、用API开发个人微信聊天机器人 可实现微信绝大部分的功能;微信收藏、微信标签等! 消息群发、通过好友请求、微信建群、微信拉人进群…

2025 年杭州品牌策划公司机构推荐榜:餐饮品牌策划/家电品牌策划聚焦实战力与适配性,这家杭州本土机构值得关注

随着 “AI+” 国家战略的深入推进及数字经济的加速发展,品牌策划已从企业 “可选服务” 升级为 “核心竞争力刚需”。2025 年,无论是新消费品牌的孵化、传统企业的数字化转型,还是细分赛道的差异化突围,企业对专业…