dedsudiyu 5 days ago
parent
commit
fa0fb6d236

+ 18 - 0
pages.json

@@ -1221,6 +1221,24 @@
 					}
 				},
 				{
+					"path": "views/exam/incorrectQuestionsList",
+					"style": {
+						"navigationBarTitleText": "历史错题"
+					}
+				},
+				{
+					"path": "views/exam/scoresList",
+					"style": {
+						"navigationBarTitleText": "考试记录"
+					}
+				},
+				{
+					"path": "views/exam/scoreDetail",
+					"style": {
+						"navigationBarTitleText": "答题详情"
+					}
+				},
+				{
 					"path": "views/study/index",
 					"style": {
 						"navigationBarTitleText": "知识学习"

+ 8 - 0
pages_safetyEducationExamination/api/index.js

@@ -220,4 +220,12 @@ export const examElExamAttemptAnswerAttemptAnswer = (data) => {
 		method: 'POST',
 		data: { ...data }
 	})
+};
+//考试记录列表
+export const examElUserExamAttemptAttemptRecord = (data) => {
+	return apiResquest({
+		url: `/exam/elUserExamAttempt/attemptRecord`,
+		method: 'POST',
+		data: { ...data }
+	})
 };

+ 292 - 2
pages_safetyEducationExamination/views/exam/incorrectQuestionsList.vue

@@ -1,8 +1,298 @@
+<!-- 历史错题 -->
 <template>
+	<view class="safetyEducationExamination-incorrectQuestions">
+		<!-- 顶部知识点标签 -->
+		<!-- <scroll-view scroll-x class="tag-scroll">
+			<view class="tag-scroll-inner">
+				<text
+					v-for="(tag, index) in typeTags"
+					:key="index"
+					class="filter-tag"
+					:class="{ 'filter-tag-active': activeTagIndex === index }"
+					@click="onTagChange(index)"
+				>{{ tag.label }}</text>
+			</view>
+		</scroll-view> -->
+
+		<!-- 错题列表 -->
+		<scroll-view scroll-y class="scroll-content" @scrolltolower="onScrollToLower">
+			<view class="panel">
+				<view v-for="(item, index) in questionList" :key="index" class="question-card">
+					<view class="row-between mb8">
+						<view class="q-head">
+							<text class="q-type-badge">{{ questionTypeName(item.questionType) }}</text>
+							<text v-if="examName" class="text-xs text-gray q-exam-name">{{ examName }}</text>
+						</view>
+						<text class="tag text-xs" :class="item.errorCount >= 3 ? 'tag-red' : 'tag-orange'">答错{{ item.errorCount || 0 }}次</text>
+					</view>
+					<view class="q-content">
+						<text class="text-sm">{{ item.questionContent }}</text>
+					</view>
+					<view v-if="item.userAnswer || item.correctAnswer" class="answer-box">
+						<view v-if="item.userAnswer" class="mb4">
+							<text class="text-xs text-gray">我的答案:</text>
+							<text class="text-xs text-red fw500">{{ item.userAnswer }}</text>
+						</view>
+						<view v-if="item.correctAnswer">
+							<text class="text-xs text-gray">正确答案:</text>
+							<text class="text-xs text-green fw500">{{ item.correctAnswer }}</text>
+						</view>
+					</view>
+					<view v-if="item.analysis" class="analysis-box">
+						<text>解析:{{ item.analysis }}</text>
+					</view>
+				</view>
+				<view v-if="!questionList[0] && !loading" class="empty-tip">暂无错题记录</view>
+				<view v-if="loading" class="loading-tip">加载中...</view>
+				<view v-if="noMore && questionList[0]" class="loading-tip">没有更多了</view>
+			</view>
+		</scroll-view>
+	</view>
 </template>
 
 <script>
+	import {
+		examElKnowledgePointTreeList,
+		examElExamAttemptAnswerAttemptAnswer,
+	} from '@/pages_safetyEducationExamination/api/index.js'
+
+	// 题型映射
+	const QUESTION_TYPE_MAP = {
+		1: '单选题',
+		2: '多选题',
+		3: '判断题',
+	};
+
+	export default {
+		data() {
+			return {
+				attemptId: '',
+				examName: '',
+				typeTags: [{ label: '全部', value: null }],
+				activeTagIndex: 0,
+				questionList: [],
+				loading: false,
+				page: 1,
+				pageSize: 10,
+				total: 0,
+				noMore: false,
+			}
+		},
+		onLoad(options) {
+			this.attemptId = options.attemptId || '';
+			this.examName = options.examName ? decodeURIComponent(options.examName) : '';
+			if (!this.attemptId) {
+				uni.showToast({ title: '考试信息无效', icon: 'none' });
+				setTimeout(() => uni.navigateBack(), 1500);
+				return;
+			}
+			// this.loadTypeTags();
+			this.loadQuestionList();
+		},
+		methods: {
+			questionTypeName(type) {
+				return QUESTION_TYPE_MAP[type] || '';
+			},
+			// 知识点标签
+			async loadTypeTags() {
+				try {
+					const { data } = await examElKnowledgePointTreeList({ dataScope: 1 });
+					if (data.code === 200 && data.data) {
+						const tags = (data.data || []).map(item => ({
+							label: item.knowledgePointName,
+							value: item.id,
+						}));
+						this.$set(this, 'typeTags', [{ label: '全部', value: null }, ...tags]);
+					}
+				} catch (e) {
+					console.error('获取知识点类型失败', e);
+				}
+			},
+			onTagChange(index) {
+				if (this.activeTagIndex === index) return;
+				this.activeTagIndex = index;
+				this.resetList();
+			},
+			resetList() {
+				this.page = 1;
+				this.noMore = false;
+				this.$set(this, 'questionList', []);
+				this.loadQuestionList();
+			},
+			// 错题列表(分页)
+			async loadQuestionList() {
+				if (this.loading || this.noMore) return;
+				this.loading = true;
+				try {
+					// const tag = this.typeTags[this.activeTagIndex];
+					const { data } = await examElExamAttemptAnswerAttemptAnswer({
+						page: this.page,
+						pageSize: this.pageSize,
+						attemptId: this.attemptId,
+						// knowledgePointId: tag ? tag.value : undefined,
+					});
+					if (data.code === 200) {
+						const records = data.data.records || [];
+						const newList = this.page === 1 ? records : [...this.questionList, ...records];
+						this.$set(this, 'questionList', newList);
+						this.total = data.data.total;
+						if (newList.length >= this.total) {
+							this.noMore = true;
+						} else {
+							this.page++;
+						}
+					}
+				} catch (e) {
+					console.error('获取错题列表失败', e);
+				} finally {
+					this.loading = false;
+				}
+			},
+			onScrollToLower() {
+				this.loadQuestionList();
+			},
+		},
+	}
 </script>
 
-<style>
-</style>
+<style lang="stylus" scoped>
+	.safetyEducationExamination-incorrectQuestions
+		height 100%
+		display flex
+		flex-direction column
+		overflow hidden
+		background-color #f0f4fb
+
+	// 知识点标签横滚
+	.tag-scroll
+		background #fff
+		white-space nowrap
+		padding 20rpx 24rpx 16rpx
+		flex-shrink 0
+
+	.tag-scroll-inner
+		display inline-flex
+		gap 16rpx
+
+	.filter-tag
+		display inline-block
+		font-size 22rpx
+		padding 10rpx 24rpx
+		border-radius 40rpx
+		background #f4f6fa
+		color #8896a7
+		white-space nowrap
+
+	.filter-tag-active
+		background #1857d4
+		color #fff
+		box-shadow 0 4rpx 16rpx rgba(24,87,212,0.3)
+
+	// 滚动区
+	.scroll-content
+		flex 1
+		overflow hidden
+
+	.panel
+		padding 16rpx 24rpx 32rpx
+
+	// 错题卡片
+	.question-card
+		background #fff
+		border 1rpx solid #dde3ed
+		border-radius 20rpx
+		padding 28rpx
+		margin-bottom 20rpx
+
+	.row-between
+		display flex
+		align-items center
+		justify-content space-between
+
+	.q-head
+		display flex
+		align-items center
+
+	.q-type-badge
+		background #1857d4
+		color #fff
+		font-size 24rpx
+		padding 4rpx 16rpx
+		border-radius 8rpx
+
+	.q-exam-name
+		margin-left 16rpx
+
+	.q-content
+		line-height 1.7
+		margin-bottom 20rpx
+
+	// 答案区
+	.answer-box
+		background #f4f6fa
+		border-radius 16rpx
+		padding 20rpx
+		margin-bottom 20rpx
+
+	// 解析区
+	.analysis-box
+		background #f6ffed
+		border-radius 16rpx
+		padding 20rpx
+		font-size 26rpx
+		line-height 1.7
+		color #4a5568
+
+	// 标签
+	.tag
+		display inline-flex
+		align-items center
+		padding 4rpx 16rpx
+		border-radius 40rpx
+		font-weight 500
+
+	.tag-orange
+		background #fef6ec
+		color #e67e22
+
+	.tag-red
+		background #fff5f5
+		color #e53e3e
+
+	.text-xs
+		font-size 22rpx
+
+	.text-sm
+		font-size 24rpx
+
+	.text-gray
+		color #8896a7
+
+	.text-green
+		color #0d8f6f
+
+	.text-red
+		color #e53e3e
+
+	.fw500
+		font-weight 500
+
+	.mb4
+		margin-bottom 8rpx
+
+	.mb8
+		margin-bottom 16rpx
+
+	// 提示
+	.empty-tip
+		text-align center
+		line-height 80rpx
+		color #aaa
+		font-size 24rpx
+
+	.loading-tip
+		text-align center
+		line-height 60rpx
+		color #aaa
+		font-size 22rpx
+</style>

+ 6 - 3
pages_safetyEducationExamination/views/exam/index.vue

@@ -139,7 +139,7 @@
 					v-for="(item, index) in scoreList"
 					:key="index"
 					class="score-card"
-					@click="goToScoreDetail(item)"
+					@click="goToScoreList(item)"
 				>
 					<view class="card-top">
 						<text class="tag tag-blue">{{ item.examTypeName }}</text>
@@ -372,8 +372,11 @@
 					url: `/pages_safetyEducationExamination/views/exam/mockExamInfo?examData=${examData}`
 				});
 			},
-			goToScoreDetail(item) {
-				// 跳转成绩详情,传递 examId
+			goToScoreList(item) {
+				// 跳转答题详情,传递 attemptId / examId
+				uni.navigateTo({
+					url: `/pages_safetyEducationExamination/views/exam/scoreList?examId=${item.examId}`
+				});
 			},
 			goToCert(item) {
 				// 跳转证书,传递 certificatePath

+ 6 - 2
pages_safetyEducationExamination/views/exam/onlineExamPlay.vue

@@ -146,7 +146,7 @@
 				<view class="result-btns">
 					<view v-if="!resultData.isPass" class="result-btn result-btn-outline" @click="reviewMistakes">查看错题</view>
 					<!-- <view v-if="resultData.canRetake" class="result-btn result-btn-primary" @click="retakeExam">重新考试</view> -->
-					<view v-else class="result-btn result-btn-primary" @click="closeResult">返回</view>
+					<view class="result-btn result-btn-primary" @click="closeResult">返回</view>
 				</view>
 				<view class="result-cert">
 					<text class="result-cert-icon">🏆</text>
@@ -528,7 +528,10 @@
 				uni.navigateBack();
 			},
 			reviewMistakes() {
-				uni.showToast({ title: '错题查看功能开发中', icon: 'none' });
+				const examName = encodeURIComponent(this.examInfo.examName || '');
+				uni.redirectTo({
+					url: `/pages_safetyEducationExamination/views/exam/incorrectQuestionsList?attemptId=${this.attemptId}&examName=${examName}`
+				});
 			},
 			goCertificate(url) {
 				if (!url) {
@@ -995,6 +998,7 @@
 		color #1857d4
 
 	.result-btn-primary
+		border 1rpx solid #1857d4
 		background #1857d4
 		color #fff
 

+ 460 - 0
pages_safetyEducationExamination/views/exam/scoreDetail.vue

@@ -0,0 +1,460 @@
+<!-- 答题详情 -->
+<template>
+	<view class="safetyEducationExamination-scoreDetail">
+		<scroll-view scroll-y class="scroll-content" @scrolltolower="onScrollToLower">
+			<!-- 考试概览 -->
+			<view class="card overview-card">
+				<view class="row-between mb8">
+					<text class="tag tag-blue text-xs">{{ detail.examTypeName }}</text>
+					<text :class="detail.isPass ? 'score-result-pass' : 'score-result-fail'">
+						{{ detail.isPass ? '通过' : '未通过' }}
+					</text>
+				</view>
+				<view class="exam-name">{{ detail.examName }}</view>
+				<view class="overview-grid">
+					<view><text class="text-gray">考试时间:</text>{{ detail.examTime }}</view>
+					<view><text class="text-gray">用时:</text>{{ detail.usedTime }}</view>
+					<view><text class="text-gray">满分/及格:</text>{{ detail.totalScore }} / {{ detail.passScore }}</view>
+					<view><text class="text-gray">考次:</text>{{ detail.attemptCount }} / {{ detail.attemptLimit === 0 ? '不限' : detail.attemptLimit }}</view>
+					<view>
+						<text class="text-gray">最高成绩:</text>
+						<text class="text-blue fw500">{{ detail.bestScore }} 分</text>
+					</view>
+					<view>
+						<text class="text-gray">考试结果:</text>
+						<text class="fw500" :class="detail.isPass ? 'text-green' : 'text-red'">
+							{{ detail.isPass ? '通过' : '未通过' }}
+						</text>
+					</view>
+				</view>
+				<!-- 答题统计 -->
+				<view class="stat-box">
+					<view class="text-sm fw500 mb8">答题统计</view>
+					<view class="stat-grid">
+						<view class="stat-item">
+							<view class="stat-num text-blue">{{ detail.totalQuestionNum || 0 }}</view>
+							<view class="text-xs text-gray">总题数</view>
+						</view>
+						<view class="stat-item">
+							<view class="stat-num text-green">{{ detail.correctCount || 0 }}</view>
+							<view class="text-xs text-gray">正确</view>
+						</view>
+						<view class="stat-item">
+							<view class="stat-num text-red">{{ detail.wrongCount || 0 }}</view>
+							<view class="text-xs text-gray">错误</view>
+						</view>
+						<view class="stat-item">
+							<view class="stat-num text-blue">{{ detail.accuracy }}</view>
+							<view class="text-xs text-gray">正确率</view>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 答题详情 -->
+			<view class="detail-panel">
+				<view class="detail-title">
+					答题详情
+					<text class="text-gray detail-title-sub">(共{{ detail.totalQuestionNum || 0 }}题)</text>
+				</view>
+
+				<view v-for="(item, index) in answerList" :key="index" class="question-card">
+					<view class="row-between mb8">
+						<view class="q-head">
+							<text class="q-type-badge">{{ questionTypeName(item.questionType) }}</text>
+							<text class="text-xs text-gray q-no">第 {{ questionNo(index) }} 题</text>
+							<text class="diff-badge" :class="difficultyClass(item.difficulty)">{{ difficultyName(item.difficulty) }}</text>
+						</view>
+						<text class="tag text-xs" :class="isCorrect(item) ? 'tag-green' : 'tag-orange'">
+							{{ isCorrect(item) ? '答对' : '答错' }}
+						</text>
+					</view>
+					<view class="q-content">{{ item.questionContent }}</view>
+					<view class="answer-box" :class="isCorrect(item) ? 'answer-box-correct' : 'answer-box-wrong'">
+						<view class="mb4">
+							<text class="text-xs text-gray">我的答案:</text>
+							<text class="text-xs fw500" :class="isCorrect(item) ? 'text-green' : 'text-red'">
+								{{ item.userAnswer }}{{ answerSuffix(item) }}
+							</text>
+						</view>
+						<view>
+							<text class="text-xs text-gray">正确答案:</text>
+							<text class="text-xs text-green fw500">{{ item.correctAnswer }}</text>
+						</view>
+					</view>
+					<view v-if="item.analysis" class="analysis-box">解析:{{ item.analysis }}</view>
+				</view>
+
+				<view v-if="!answerList[0] && !listLoading" class="empty-tip">暂无答题记录</view>
+				<view v-if="listLoading" class="loading-tip">加载中...</view>
+				<view v-if="noMore && answerList[0]" class="loading-tip">没有更多了</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	// 题型映射:1-单选题,2-多选题,3-判断题
+	const QUESTION_TYPE_MAP = {
+		1: '单选题',
+		2: '多选题',
+		3: '判断题',
+	};
+
+	// 难度映射:1-简单,2-一般,3-较难,4-极难
+	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 {
+				attemptId: '',
+				examId: '',
+				// 上部分:考试概况
+				detail: {
+					examTypeName: '',
+					examName: '',
+					isPass: false,
+					examTime: '',
+					usedTime: '',
+					totalScore: 0,
+					passScore: 0,
+					attemptCount: 0,
+					attemptLimit: 0,
+					bestScore: 0,
+					totalQuestionNum: 0,
+					correctCount: 0,
+					wrongCount: 0,
+					accuracy: '-',
+				},
+				// 下部分:答题详情列表
+				answerList: [],
+				listLoading: false,
+				page: 1,
+				pageSize: 10,
+				total: 0,
+				noMore: false,
+			}
+		},
+		onLoad(options) {
+			this.attemptId = options.attemptId || '';
+			this.examId = options.examId || '';
+			if (!this.attemptId) {
+				uni.showToast({ title: '考试信息无效', icon: 'none' });
+				setTimeout(() => uni.navigateBack(), 1500);
+				return;
+			}
+			this.loadExamDetail();
+			this.loadAnswerList();
+		},
+		methods: {
+			questionTypeName(type) {
+				return QUESTION_TYPE_MAP[type] || '';
+			},
+			difficultyName(difficulty) {
+				const d = DIFFICULTY_MAP[difficulty];
+				return d ? d.name : '';
+			},
+			difficultyClass(difficulty) {
+				const d = DIFFICULTY_MAP[difficulty];
+				return d ? d.cls : '';
+			},
+			isCorrect(item) {
+				return item.isCorrect === 1 || item.isCorrect === true;
+			},
+			answerSuffix(item) {
+				if (item.answerStatusText) return `(${item.answerStatusText})`;
+				if (item.isPartial) return '(部分正确)';
+				return this.isCorrect(item) ? '(正确)' : '(错误)';
+			},
+			questionNo(index) {
+				const item = this.answerList[index];
+				if (item && item.questionNo) return item.questionNo;
+				if (item && item.sortNo) return item.sortNo;
+				return index + 1;
+			},
+			// 上部分:考试概况(独立详情接口)
+			async loadExamDetail() {
+				try {
+					// TODO 后台接口未提供,先占位;接口就绪后放开以下代码
+					// const { data } = await examElExamAttemptAnswerDetail({ attemptId: this.attemptId });
+					// if (data.code === 200 && data.data) {
+					//     const d = data.data;
+					//     this.detail = {
+					//         examTypeName: d.examTypeName || '',
+					//         examName: d.examName || '',
+					//         isPass: d.examStatus === 1,
+					//         examTime: parseTime(d.startTime, '{y}-{m}-{d}'),
+					//         usedTime: d.myDuration || '-',
+					//         totalScore: d.totalScore || 0,
+					//         passScore: d.passScore || 0,
+					//         attemptCount: d.attemptCount || 0,
+					//         attemptLimit: d.attemptLimit || 0,
+					//         bestScore: d.bestScore || 0,
+					//         totalQuestionNum: d.totalQuestionNum || 0,
+					//         correctCount: d.correctCount || 0,
+					//         wrongCount: d.wrongCount || 0,
+					//         accuracy: d.accuracy || '-',
+					//     };
+					// }
+				} catch (e) {
+					console.error('获取考试详情失败', e);
+				}
+			},
+			resetList() {
+				this.page = 1;
+				this.noMore = false;
+				this.$set(this, 'answerList', []);
+				this.loadAnswerList();
+			},
+			// 下部分:答题详情列表(分页滚动加载)
+			async loadAnswerList() {
+				if (this.listLoading || this.noMore) return;
+				this.listLoading = true;
+				try {
+					// TODO 后台接口未提供,先占位;接口就绪后放开以下代码
+					// const { data } = await examElExamAttemptAnswerAttemptAnswer({
+					//     page: this.page,
+					//     pageSize: this.pageSize,
+					//     attemptId: this.attemptId,
+					// });
+					// if (data.code === 200) {
+					//     const records = data.data.records || [];
+					//     const newList = this.page === 1 ? records : [...this.answerList, ...records];
+					//     this.$set(this, 'answerList', newList);
+					//     this.total = data.data.total;
+					//     if (newList.length >= this.total) {
+					//         this.noMore = true;
+					//     } else {
+					//         this.page++;
+					//     }
+					// }
+				} catch (e) {
+					console.error('获取答题详情失败', e);
+				} finally {
+					this.listLoading = false;
+				}
+			},
+			onScrollToLower() {
+				this.loadAnswerList();
+			},
+		},
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-scoreDetail
+		height 100%
+		display flex
+		flex-direction column
+		overflow hidden
+		background-color #f0f4fb
+
+	.scroll-content
+		flex 1
+		overflow hidden
+
+	.card
+		background #fff
+		border 1rpx solid #dde3ed
+		border-radius 24rpx
+		padding 32rpx
+		margin 24rpx 24rpx 24rpx
+		box-shadow 0 4rpx 24rpx rgba(24,87,212,0.08)
+
+	.exam-name
+		font-size 32rpx
+		font-weight 700
+		color #1a2233
+		margin-bottom 20rpx
+
+	.overview-grid
+		display grid
+		grid-template-columns 1fr 1fr
+		gap 12rpx
+		font-size 26rpx
+		color #1a2233
+		margin-bottom 28rpx
+
+	// 答题统计
+	.stat-box
+		background #f4f6fa
+		border-radius 16rpx
+		padding 24rpx
+		margin-bottom 28rpx
+
+	.stat-grid
+		display grid
+		grid-template-columns 1fr 1fr 1fr 1fr
+		gap 16rpx
+		text-align center
+
+	.stat-num
+		font-size 40rpx
+		font-weight 700
+
+	// 答题详情区
+	.detail-panel
+		padding 0 24rpx 24rpx
+
+	.detail-title
+		font-size 24rpx
+		font-weight 500
+		padding 28rpx 0 20rpx
+
+	.detail-title-sub
+		font-weight 400
+
+	// 题目卡片
+	.question-card
+		background #fff
+		border 1rpx solid #dde3ed
+		border-radius 20rpx
+		padding 28rpx
+		margin-bottom 20rpx
+
+	.row-between
+		display flex
+		align-items center
+		justify-content space-between
+
+	.q-head
+		display flex
+		align-items center
+
+	.q-type-badge
+		background #1857d4
+		color #fff
+		font-size 24rpx
+		padding 4rpx 16rpx
+		border-radius 8rpx
+
+	.q-no
+		margin-left 12rpx
+
+	.diff-badge
+		margin-left 12rpx
+		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
+
+	.q-content
+		font-size 24rpx
+		line-height 1.7
+		margin-bottom 20rpx
+
+	// 答案区
+	.answer-box
+		border-radius 16rpx
+		padding 20rpx
+		margin-bottom 20rpx
+
+	.answer-box-correct
+		background #f6ffed
+		border 1rpx solid #b7eb8f
+
+	.answer-box-wrong
+		background #f4f6fa
+
+	// 解析区
+	.analysis-box
+		background #f6ffed
+		border-radius 16rpx
+		padding 20rpx
+		font-size 26rpx
+		color #4a5568
+		line-height 1.7
+
+	// 标签
+	.tag
+		display inline-flex
+		align-items center
+		padding 4rpx 16rpx
+		border-radius 40rpx
+		font-weight 500
+
+	.tag-blue
+		background #eef3fd
+		color #1857d4
+
+	.tag-green
+		background #edfaf6
+		color #0d8f6f
+
+	.tag-orange
+		background #fef6ec
+		color #e67e22
+
+	.score-result-pass
+		color #10a37f
+		font-weight 600
+
+	.score-result-fail
+		color #e53e3e
+		font-weight 600
+
+	.text-xs
+		font-size 22rpx
+
+	.text-sm
+		font-size 24rpx
+
+	.text-gray
+		color #8896a7
+
+	.text-blue
+		color #1857d4
+
+	.text-green
+		color #10a37f
+
+	.text-red
+		color #e53e3e
+
+	.fw500
+		font-weight 500
+
+	.mb4
+		margin-bottom 8rpx
+
+	.mb8
+		margin-bottom 16rpx
+
+	// 提示
+	.empty-tip
+		text-align center
+		line-height 80rpx
+		color #aaa
+		font-size 24rpx
+
+	.loading-tip
+		text-align center
+		line-height 60rpx
+		color #aaa
+		font-size 22rpx
+</style>

+ 84 - 0
pages_safetyEducationExamination/views/exam/scoresList.vue

@@ -0,0 +1,84 @@
+<template>
+	<view class="scoreList">
+		<scroll-view scroll-y class="scroll-content" @scrolltolower="onScrollToLower">
+			
+			<view v-if="!scoreList[0] && !scoreLoading" class="empty-tip">暂无考试记录</view>
+			<view v-if="scoreLoading" class="loading-tip">加载中...</view>
+			<view v-if="scoreNoMore && scoreList[0]" class="loading-tip">没有更多了</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {
+		parseTime
+	} from '@/component/public.js'
+	import {
+		examElUserExamAttemptAttemptRecord,
+	} from '@/pages_safetyEducationExamination/api/index.js'
+	export default {
+		data() {
+			return {
+				scoreList: [],
+				scoreLoading: false,
+				scorePage: 1,
+				scorePageSize: 10,
+				scoreTotal: 0,
+				scoreNoMore: false,
+			}
+		},
+		created() {
+			
+		},
+		onShow() {
+			
+		},
+		methods: {
+			// 滚动到底部加载更多
+			onScrollToLower() {
+				this.loadScoreList();
+			},
+			// 考试列表(支持分页)
+			async loadScoreList() {
+				if (this.scoreLoading || this.scoreNoMore) return;
+				this.scoreLoading = true;
+				try {
+					const obj = {
+						page: this.scorePage,
+						pageSize: this.scorePageSize,
+						examId: '',
+					};
+					const { data } = await examElUserExamAttemptAttemptRecord(obj);
+					if (data.code === 200) {
+						for(let i=0;i<data.data.records.length;i++){
+							data.data.records[i].startTime = parseTime(data.data.records[i].startTime, "{y}-{m}-{d}")
+						}
+						const records = data.data.records || [];
+						const newList = this.scorePage === 1 ? records : [...this.scoreList, ...records];
+						this.$set(this, 'scoreList', newList);
+						this.scoreTotal = data.data.total;
+						if (newList.length >= this.scoreTotal) {
+							this.scoreNoMore = true;
+						} else {
+							this.scorePage++;
+						}
+					}
+				} catch (e) {
+					console.error('获取成绩列表失败', e);
+				} finally {
+					this.scoreLoading = false;
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.scoreList{
+		height 100%
+		display flex
+		flex-direction column
+		overflow hidden
+		background-color #f0f4fb
+	}
+</style>

+ 5 - 0
pages_safetyEducationExamination/views/home/component/docResource.vue

@@ -332,6 +332,11 @@
 							font-size:24rpx;
 							line-height:24rpx;
 							margin-bottom:10rpx;
+							width:180rpx;
+							display:block;
+							overflow:hidden;
+							text-overflow:ellipsis;
+							white-space:nowrap;
 						}
 						view:nth-child(2){
 							font-size:22rpx;