Parcourir la source

Merge branch 'gzu_1st_main_20260908(教育考试)' into SCHOOL-XiBeiNongLin

dedsudiyu il y a 5 jours
Parent
commit
51fc112168

+ 8 - 2
pages.json

@@ -1205,7 +1205,10 @@
 				{
 					"path": "views/exam/onlineExamPlay",
 					"style": {
-						"navigationBarTitleText": "考试中"
+						"navigationBarTitleText": "考试中",
+						"mp-weixin": {
+							"swipeBackAsBackPress": true
+						}
 					}
 				},
 				{
@@ -1217,7 +1220,10 @@
 				{
 					"path": "views/exam/mockExamPlay",
 					"style": {
-						"navigationBarTitleText": "模拟考试中"
+						"navigationBarTitleText": "模拟考试中",
+						"mp-weixin": {
+							"swipeBackAsBackPress": true
+						}
 					}
 				},
 				{

+ 8 - 0
pages_safetyEducationExamination/api/index.js

@@ -292,4 +292,12 @@ export const examUserExamMockDetail = (data) => {
 		method: 'POST',
 		data: { ...data }
 	})
+};
+//二次确认考试是否已结束
+export const examUserExamAttemptFinished = (data) => {
+	return apiResquest({
+		url: `/exam/user/exam/attempt/finished`,
+		method: 'POST',
+		data: { ...data }
+	})
 };

+ 128 - 42
pages_safetyEducationExamination/utils/index.js

@@ -2,6 +2,7 @@ import {
 	examUserExamStart,
 	examUserExamCommitmentConfirm,
 	examUserCoursewareLearningRestart,
+	examUserExamAttemptFinished,
 } from '@/pages_safetyEducationExamination/api/index.js'
 
 /**
@@ -41,44 +42,36 @@ export async function goToExamPage(item, callbacks = {}) {
 		const currentPage = pages[pages.length - 1];
 		const currentRoute = currentPage.route; 
 		if (item.mockExamId) {
-			const examData = encodeURIComponent(JSON.stringify(item));
-			uni.navigateTo({
-				url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${item.mockExamId}`,
-			});
-			// if(currentRoute == 'pages_safetyEducationExamination/views/exam/mockExamInfo' || currentRoute == 'pages_safetyEducationExamination/views/exam/onlineExamInfo'){
-			// 	uni.navigateTo({
-			// 		url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${item.mockExamId}`,
-			// 	});
-			// }else{
-			// 	uni.navigateTo({
-			// 		url: `/pages_safetyEducationExamination/views/exam/mockExamInfo?examData=${examData}`,
-			// 	});
-			// }
+			// const examData = encodeURIComponent(JSON.stringify(item));
+			// uni.navigateTo({
+			// 	url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${item.mockExamId}`,
+			// });
+			let obj = {
+				examId: item.examId,
+				attemptId: item.mockExamId,
+			};
+			console.log('goToExamPage-obj',obj)
+			examUserExamAttemptFinishedPlay(2,obj);
 		} else {
-			try {
-				const { data } = await examUserExamStart({ examId: item.examId, examScene: 2 });
-				if (data.code === 200 && data.data && data.data.attemptId) {
-					const filled = { ...item, attemptId: data.data.attemptId };
-					const examData = encodeURIComponent(JSON.stringify(filled));
-					uni.navigateTo({
-						url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${data.data.attemptId}`,
-					});
-					// if(currentRoute == 'pages_safetyEducationExamination/views/exam/mockExamInfo'){
-					// 	uni.navigateTo({
-					// 		url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${data.data.attemptId}`,
-					// 	});
-					// }else{
-					// 	uni.navigateTo({
-					// 		url: `/pages_safetyEducationExamination/views/exam/mockExamInfo?examData=${examData}`,
-					// 	});
-					// }
-				} else {
-					uni.showToast({ title: data.message || '获取模拟试卷失败', icon: 'none' });
-				}
-			} catch (e) {
-				console.error('获取模拟试卷失败', e);
-				uni.showToast({ title: '获取模拟试卷失败', icon: 'none' });
-			}
+			let obj = {
+				examId: item.examId,
+			};
+			examUserExamStartPlay(2,obj);
+			// try {
+			// 	const { data } = await examUserExamStart({ examId: item.examId, examScene: 2 });
+			// 	if (data.code === 200 && data.data && data.data.attemptId) {
+			// 		const filled = { ...item, attemptId: data.data.attemptId };
+			// 		const examData = encodeURIComponent(JSON.stringify(filled));
+			// 		uni.navigateTo({
+			// 			url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${data.data.attemptId}`,
+			// 		});
+			// 	} else {
+			// 		uni.showToast({ title: data.message || '获取模拟试卷失败', icon: 'none' });
+			// 	}
+			// } catch (e) {
+			// 	console.error('获取模拟试卷失败', e);
+			// 	uni.showToast({ title: '获取模拟试卷失败', icon: 'none' });
+			// }
 		}
 
 	} else if (item.conditionType == 4) {
@@ -99,7 +92,13 @@ export async function resolveExamEntry(item, callbacks = {}) {
 		}
 	} else {
 		// 不需要承诺书,直接进入考试
-		await startFormalExam(item);
+		// await startFormalExam(item);
+		if(item.attemptId){
+			console.log('resolveExamEntry-item',item)
+			examUserExamAttemptFinishedPlay(1,item);
+		}else{
+			examUserExamStartPlay(1,item);
+		}
 	}
 }
 
@@ -128,15 +127,22 @@ export async function confirmCommitmentAndStart(preData, onClose) {
 	}
 
 	try {
-		console.log('确认1')
 		const { data } = await examUserExamCommitmentConfirm({ attemptId });
-		console.log('确认2')
 		if (data.code === 200) {
-			console.log('确认3')
 			if (typeof onClose === 'function') {
 				onClose();
 			} else {
-				navigateToExamPlay(preData.examId, attemptId, preData.examKind);
+				// navigateToExamPlay(preData.examId, attemptId, preData.examKind);
+				let obj = {
+					examId: preData.examId,
+					attemptId: attemptId,
+					type: '1',
+					examKind:preData.examKind,
+					attemptLimit:preData.attemptLimit,
+					attemptCount:preData.attemptCount,
+				}
+				console.log('confirmCommitmentAndStart-obj',obj)
+				examUserExamAttemptFinishedPlay(1,obj);
 			}
 		} else {
 			uni.showToast({ title: data.message || '确认失败', icon: 'none' });
@@ -211,3 +217,83 @@ export async function examUserCoursewareLearningRestartFunction(coursewareId ) {
 		}
 	} catch (e) { console.error('重新开始学习课件失败', e); }
 }
+
+
+//二次确认考试是否已结束
+export async function examUserExamAttemptFinishedPlay(type,item) {
+	console.log('调用了')
+	try {
+		console.log('item',item)
+		const { data } = await examUserExamAttemptFinished({ attemptId:item.attemptId });
+		if (data.code == 200) {
+			if(data.data){
+				//已结束
+				if(type == 1){
+					//在线考试
+					if(item.attemptLimit == 0 || item.attemptCount < item.attemptLimit){
+						//获取新试卷
+						examUserExamStartPlay(type,item)
+					}else{
+						//无可用次数 提示并刷新数据列表
+						uni.showToast({ title: '当前考试已结束', icon: 'none' });
+						checkData();
+					}
+				}else if(type == 2){
+					//模拟考试-获取新试卷
+					examUserExamStartPlay(type,item)
+				}
+			}else{
+				//未结束
+				confirmTitle(type,item);
+			}
+		}
+	} catch (e) { console.error('二次确认考试是否已结束失败', e);}
+}
+//获取新试卷
+export async function examUserExamStartPlay(type,item) {
+	try {
+		const { data } = await examUserExamStart({ examId: item.examId, examScene: type });
+		if (data.code === 200 && data.data && data.data.attemptId) {
+			let obj = {
+				examId: item.examId,
+				attemptId: data.data.attemptId,
+			};
+			if(type == 1){
+				obj.type = 1;
+				obj.examKind = item.examKind;
+			}
+			confirmTitle(type,obj);
+		} else {
+			uni.showToast({ title: data.message || '获取试卷失败', icon: 'none' });
+		}
+	} catch (e) {
+		console.error('获取试卷失败', e);
+		uni.showToast({ title: '获取试卷失败', icon: 'none' });
+	}
+}
+//弹窗确认
+export async function confirmTitle(type,item) {
+	if(type == 1){
+		uni.showModal({
+			title: '进入考试页面',
+			content: `考试期间未完成答题请勿离开页面,离开后倒计时仍会继续,超时将自动交卷并记录成绩,扣除 1 次考试机会。`,
+			confirmText:'确定进入',
+			success: async (res) => {
+				if (res.confirm) {
+					uni.navigateTo({
+						url: `/pages_safetyEducationExamination/views/exam/onlineExamPlay?examId=${encodeURIComponent(item.examId)}&attemptId=${encodeURIComponent(item.attemptId)}&examKind=${encodeURIComponent(item.examKind || '')}`,
+					});
+				}
+			}
+		});
+	}else if(type == 2){
+		uni.navigateTo({
+			url: `/pages_safetyEducationExamination/views/exam/mockExamPlay?examId=${item.examId}&attemptId=${item.attemptId}`,
+		});
+	}
+}
+
+//监听数据刷新事件
+export function checkData(data) {
+    uni.$emit('onDataCheckSuccess', data)
+}

+ 1 - 1
pages_safetyEducationExamination/views/exam/index.vue

@@ -66,7 +66,7 @@
 						</view>
 						<view class="info-item">
 							<text class="info-label">可考次数</text>
-							<text class="info-value text-gray">{{ item.attemptLimit === 0 ? '不限' : item.attemptLimit + ' 次' }}</text>
+							<text class="info-value text-gray">{{ item.attemptLimit === 0 ? '不限' : item.attemptCount+'/'+item.attemptLimit + ' 次' }}</text>
 						</view>
 					</view>
 				</view>

+ 10 - 1
pages_safetyEducationExamination/views/exam/mockExamInfo.vue

@@ -65,7 +65,8 @@
 		data() {
 			return {
 				examData: {},
-				newData: {}
+				newData: {},
+				successHandler:null,
 			}
 		},
 		computed: {
@@ -81,6 +82,14 @@
 					console.error('解析模拟考试数据失败', e);
 				}
 			}
+			this.successHandler = (res) => {
+				this.examUserExamMockDetail();
+			}
+			uni.$on('onDataCheckSuccess', this.successHandler)
+		},
+		onUnload() {
+			// 组件销毁时也必须移除监听
+			uni.$off('onDataCheckSuccess', this.successHandler)
 		},
 		onShow(){
 			this.examUserExamMockDetail();

+ 51 - 1
pages_safetyEducationExamination/views/exam/mockExamPlay.vue

@@ -3,7 +3,8 @@
 	<view class="exam-screen">
 		<!-- 顶部栏 -->
 		<view class="topbar">
-			<view class="topbar-left" @click="requestSubmit(false)">‹ 交卷</view>
+			<!-- <view class="topbar-left" @click="requestSubmit(false)">‹ 交卷</view> -->
+			<view class="topbar-left"></view>
 			<view class="topbar-title">{{ examInfo.examName || '模拟考试' }}</view>
 			<view class="topbar-right" @click="toggleQuestionNav">题号</view>
 		</view>
@@ -154,6 +155,16 @@
 				</view>
 			</view>
 		</view>
+		<!-- #ifdef MP-WEIXIN -->
+		<view v-if="showContainer">
+			<page-container
+				:show="showContainer"
+				:overlay="false"
+				@beforeleave="handleBeforeLeave"
+				:close-on-slide-down="false"
+			></page-container>
+		</view>
+		<!-- #endif -->
 	</view>
 </template>
 
@@ -188,6 +199,7 @@
 				showResult: false,
 				resultData: {},
 				viewAnswerShowType: false,
+				showContainer: true    // page-container 显隐控制
 			}
 		},
 		computed: {
@@ -489,6 +501,8 @@
 					this.clearTimer()
 					this.buildResult(response.data.data)
 					this.showResult = true
+					//关闭拦截
+					this.handleSubmit();
 				} catch (e) {
 					uni.showToast({ title: '交卷失败', icon: 'none' })
 				}
@@ -523,6 +537,7 @@
 						this.isSubmitting = false
 						this.hasWarnedTime = false
 						this.viewAnswerShowType = false
+						this.showContainer = true
 						this.$set(this, 'questionTypeGroups', [])
 						this.$set(this, 'currentQuestion', {})
 						this.groupIndex = 0
@@ -551,6 +566,41 @@
 					return item
 				}).join(',')
 			},
+			// 交卷后调用此方法关闭拦截
+			handleSubmit() {
+				this.hasSubmitted = true
+				// #ifdef MP-WEIXIN
+				wx.disableAlertBeforeUnload()
+				// #endif
+			},
+			// 封装弹窗逻辑,供多处复用
+			showConfirmModal() {
+				this.showContainer = false
+				uni.showModal({
+					title: '您还有题目未作答',
+					content: '离开页面后倒计时仍会继续,超时将自动交卷并记录成绩,扣除 1 次考试机会。',
+					confirmText: '确认离开',
+					cancelText: '继续答题',
+					success: (res) => {
+						if (res.confirm) {
+							setTimeout(() => {
+								uni.navigateBack()
+								this.showContainer = true
+							}, 100)
+						} else {
+							this.showContainer = true
+						}
+					}
+				})
+			},
+			// page-container 核心拦截逻辑
+			handleBeforeLeave() {
+				if (this.hasSubmitted) {
+					uni.navigateBack()
+					return
+				}
+				this.showConfirmModal()
+			}
 		}
 	}
 </script>

+ 12 - 1
pages_safetyEducationExamination/views/exam/onlineExamInfo.vue

@@ -23,7 +23,7 @@
 					</view>
 					<view class="info-item">
 						<view class="text-xs text-gray mb4">考试次数</view>
-						<view class="text-333 fw500">{{ examData.attemptLimit === 0 ? '不限' : examData.attemptLimit+' 次' }}</view>
+						<view class="text-333 fw500">{{ examData.attemptLimit === 0 ? '不限' : examData.attemptCount+'/'+examData.attemptLimit+' 次' }}</view>
 					</view>
 				</view>
 				<!-- 考前任务要求 -->
@@ -105,6 +105,7 @@
 				richTextExamId:'',
 				preData: null,
 				itemData:{},
+				successHandler:null,
 			}
 		},
 		computed: {
@@ -122,6 +123,14 @@
 					console.error('解析考试数据失败', e);
 				}
 			}
+			this.successHandler = (res) => {
+				this.examUserExamDetail();
+			}
+			uni.$on('onDataCheckSuccess', this.successHandler)
+		},
+		onUnload() {
+			// 组件销毁时也必须移除监听
+			uni.$off('onDataCheckSuccess', this.successHandler)
 		},
 		onShow(){
 			this.examUserExamDetail();
@@ -161,8 +170,10 @@
 			},
 			async startExam() {
 				this.$set(this,'itemData',this.examData);
+				console.log('this.itemData',this.itemData);
 				goToExamPage(this.examData, {
 					openCommitmentDialog: (examItem) => {
+						console.log('openCommitmentDialog',examItem);
 						this.preButtonType = examItem.commitmentPrintEnabled == 1;
 						this.richTextHtml = examItem.commitmentContent;
 						this.richTextExamId = examItem.examId;

+ 48 - 0
pages_safetyEducationExamination/views/exam/onlineExamPlay.vue

@@ -160,6 +160,16 @@
 				</view>
 			</view>
 		</view>
+		<!-- #ifdef MP-WEIXIN -->
+		<view v-if="showContainer">
+			<page-container
+				:show="showContainer"
+				:overlay="false"
+				@beforeleave="handleBeforeLeave"
+				:close-on-slide-down="false"
+			></page-container>
+		</view>
+		<!-- #endif -->
 	</view>
 </template>
 
@@ -195,6 +205,7 @@
 				hasSubmitted: false,
 				showResult: false,
 				resultData: {},
+				showContainer: true    // page-container 显隐控制
 			}
 		},
 		computed: {
@@ -491,6 +502,8 @@
 						this.clearTimer();
 						this.buildResult(data.data);
 						this.showResult = true;
+						//关闭拦截
+						this.handleSubmit();
 					} else {
 						uni.showToast({ title: data.message || '交卷失败', icon: 'none' });
 					}
@@ -600,6 +613,41 @@
 					}
 				});
 			},
+			// 交卷后调用此方法关闭拦截
+			handleSubmit() {
+				this.hasSubmitted = true
+				// #ifdef MP-WEIXIN
+				wx.disableAlertBeforeUnload()
+				// #endif
+			},
+			// 封装弹窗逻辑,供多处复用
+			showConfirmModal() {
+				this.showContainer = false
+				uni.showModal({
+					title: '您还有题目未作答',
+					content: '离开页面后倒计时仍会继续,超时将自动交卷并记录成绩,扣除 1 次考试机会。',
+					confirmText: '确认离开',
+					cancelText: '继续答题',
+					success: (res) => {
+						if (res.confirm) {
+							setTimeout(() => {
+								uni.navigateBack()
+								this.showContainer = true
+							}, 100)
+						} else {
+							this.showContainer = true
+						}
+					}
+				})
+			},
+			// page-container 核心拦截逻辑
+			handleBeforeLeave() {
+				if (this.hasSubmitted) {
+					uni.navigateBack()
+					return
+				}
+				this.showConfirmModal()
+			}
 		}
 	}
 </script>

+ 9 - 1
pages_safetyEducationExamination/views/home/component/examTask.vue

@@ -66,10 +66,18 @@
 				richTextExamId:'',
 				preData: null,
 				itemData:{},
+				successHandler:null,
 			}
 		},
 		mounted() {
-			// this.loadTaskList();
+			this.successHandler = (res) => {
+				this.loadTaskList();
+			}
+			uni.$on('onDataCheckSuccess', this.successHandler)
+		},
+		beforeDestroy() {
+			// 组件销毁时也必须移除监听
+			uni.$off('onDataCheckSuccess', this.successHandler)
 		},
 		methods: {
 			resetMethod(){

+ 9 - 1
pages_safetyEducationExamination/views/home/component/specialExam.vue

@@ -87,13 +87,21 @@
 				richTextExamId:'',
 				preData: null,
 				itemData:{},
+				successHandler:null,
 			}
 		},
 		created() {
 			
 		},
 		mounted() {
-			// this.examUserExamStudyTaskList();
+			this.successHandler = (res) => {
+				this.examUserExamStudyTaskList();
+			}
+			uni.$on('onDataCheckSuccess', this.successHandler)
+		},
+		beforeDestroy() {
+			// 组件销毁时也必须移除监听
+			uni.$off('onDataCheckSuccess', this.successHandler)
 		},
 		methods: {
 			resetMethod(){