Explorar el Código

Merge remote-tracking branch 'origin/2025北京诚祥-家政版' into 2025北京诚祥-家政版

# Conflicts:
#	pages/client/Lianxi/list.vue
tanxue hace 1 mes
padre
commit
0740afd24b

+ 1 - 1
components/custom-tabbar/custom-tabbar-client.vue

@@ -42,7 +42,7 @@
 					// 同页面不刷新
 					return;
 				}
-				uni.navigateTo({url: path});
+				uni.reLaunch({url: path});
 			},
 		},
 	}

+ 91 - 0
components/zhuapaiConfirm/myVideo.vue

@@ -0,0 +1,91 @@
+<template>
+  <view class="camera-container">
+    <!-- camera组件,全屏显示 -->
+    <camera class="camera" device-position="back" flash="off" @error="error" style="width: 200px;height: 200px"></camera>
+    
+    <!-- 自定义底部操作栏 -->
+    <view class="controls">
+      <button @tap="switchCamera">切换摄像头</button>
+      <!-- 长按录制视频,点击拍照 -->
+      <button @touchstart="startRecord" @touchend="stopRecord">按住录制</button>
+      <button @tap="takePhoto">拍照</button>
+    </view>
+    
+    <!-- 用于显示拍下的照片 -->
+    <image v-if="photoPath" :src="photoPath" mode="widthFix"></image>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      cameraContext: null,
+      photoPath: '', // 拍照得到的图片路径
+      isRecording: false
+    };
+  },
+  onReady() {
+    // 页面渲染完成后,创建相机上下文
+    this.cameraContext = uni.createCameraContext();
+  },
+  methods: {
+    // 拍照
+    takePhoto() {
+      this.cameraContext.takePhoto({
+        quality: 'high',
+        success: (res) => {
+          this.photoPath = res.tempImagePath;
+          console.log('拍照成功,照片路径:', this.photoPath);
+        },
+        fail: (err) => {
+          console.error('拍照失败:', err);
+        }
+      });
+    },
+    
+    // 开始录制视频
+    startRecord() {
+      this.isRecording = true;
+      this.cameraContext.startRecord({
+        success: () => {
+          console.log('开始录制');
+        }
+      });
+    },
+    
+    // 结束录制视频
+    stopRecord() {
+      if (!this.isRecording) return;
+      this.isRecording = false;
+      this.cameraContext.stopRecord({
+        success: (res) => {
+          console.log('录制完成,视频路径:', res.tempThumbPath); // 封面
+          console.log('视频文件路径:', res.tempVideoPath);
+          // res.tempVideoPath 是视频的临时文件路径
+        }
+      });
+    },
+    
+    // 切换前后摄像头
+    switchCamera() {
+      // 需要通过变量动态改变camera组件的device-position属性
+      // 这部分逻辑需要你在data中定义变量并切换'front'和'back'
+    }
+  }
+};
+</script>
+
+<style>
+.camera {
+  width: 100%;
+  height: 100vh; /* 全屏 */
+}
+.controls {
+  position: fixed;
+  bottom: 50px;
+  width: 100%;
+  display: flex;
+  justify-content: space-around;
+}
+</style>

+ 171 - 0
components/zhuapaiConfirm/useQieping.js

@@ -0,0 +1,171 @@
+import {
+	ref,
+	onUnmounted,
+	nextTick,
+	computed
+} from "vue";
+import {
+	getClientQiepingCheat,
+	getClientQiepingTimes
+} from "@/api/kaoshi.js"
+import {
+	useZhuapaiStore
+} from "@/store/zhuapai.js"
+import {
+	onLoad,
+	onShow,
+	onHide
+} from "@dcloudio/uni-app";
+
+
+export function useQiePing() {
+
+
+	const zhuapaiStore = useZhuapaiStore();
+
+	// const emits = defineEmits(['zhuapai', 'forceSubmit'])
+	const zhuapaiFlag = ref(false);
+	const zhuapaixiaoshi = ref(0);
+	const leaveTime = ref('');
+	const toggleScreenSecond = ref(0);
+	const toggleScreenFlag = ref(0);
+	const ksId = ref('')
+	let visibilityState = 'hidden'
+	let initStatus = false;
+
+	let zhuapaiFn = null;
+	let forceSubmitFn = null;
+	let qiepingToastFn = null;
+
+	function cheatingFun() {
+		// 用户离开了当前页面
+		if (visibilityState === 'hidden') {
+			console.log('页面不可见');
+			if (zhuapaiFlag.value) {
+				// 此时 切出后台 发生抓拍情况下 要传递固定图片
+				console.log('切出去zhuapaixiaoshi 恢复正常');
+				zhuapaixiaoshi.value = 1
+				zhuapaiStore.setStatus(1)
+			}
+			//  计时
+			leaveTime.value = new Date().getTime();
+		} else if (visibilityState === 'visible') {
+			console.log('切回来 恢复正常');
+			// 用户打开或回到页面
+			if (zhuapaiFlag.value) {
+				zhuapaixiaoshi.value = 0
+				zhuapaiStore.setStatus(0)
+				// emits('zhuapai') // 重置抓拍
+				zhuapaiFn && zhuapaiFn()
+			}
+			zhuapaixiaoshi.value = 0
+			zhuapaiStore.setStatus(0)
+			console.log('页面可见');
+			let nowTime = new Date().getTime();
+			if (Number(nowTime) - Number(leaveTime.value) > toggleScreenSecond.value + '000') {
+				let req = {
+					ksId: ksId.value,
+				};
+				getClientQiepingCheat(req).then(res => {
+					//cutScreenDialog   是否超限 true:超过限制 false:未超过限制 ,
+					if (res.code === 0 && res.data.flag) {
+						// emits('forceSubmit') // 强制交卷
+						forceSubmitFn && forceSubmitFn();
+					} else {
+						emits('qiepingToast', res.data.times) // 提示警告
+						qiepingToastFn && qiepingToastFn(res.data.times)
+					}
+				});
+			}
+		}
+	}
+
+	function zhuapaiFun() {
+		if (visibilityState === 'hidden') {
+			console.log('页面不可见');
+			zhuapaixiaoshi.value = 1
+			zhuapaiStore.setStatus(1)
+		} else if (visibilityState === 'visible') {
+			zhuapaixiaoshi.value = 0
+			zhuapaiStore.setStatus(0)
+			// emits('zhuapai')
+			zhuapaiFn && zhuapaiFn()
+		}
+	}
+
+	function init(options, {
+		zhuapai,
+		forceSubmit,
+		qiepingToast
+	}) {
+		initStatus = true;
+		zhuapaiFn = zhuapai;
+		forceSubmitFn = forceSubmit;
+		qiepingToastFn = qiepingToast;
+
+		toggleScreenFlag.value = options.toggleScreenFlag;
+		toggleScreenSecond.value = options.toggleScreenSecond;
+		zhuapaiFlag.value = options.zhuapaiFlag;
+		ksId.value = options.ksId;
+		if (toggleScreenFlag.value !== 0) {
+			console.log("有切屏");
+			cheatingNumberSearch();
+		}
+		if (zhuapaiFlag.value && toggleScreenFlag.value == 0) {
+			console.log("有抓拍 无切屏");
+		}
+	}
+
+	function cheatingNumberSearch() {
+		let req = {
+			ksId: ksId.value,
+		};
+		getClientQiepingTimes(req).then(res => {
+			if (res.code === 0) {
+				if (res.data.times > 0 && res.data.times <= res.data.toggleScreenFlag) {
+					// emits('qiepingToast', res.data.times) // 提示警告
+					qiepingToastFn && qiepingToastFn(res.data.times)
+				} else if (res.data.times > 0 && res.data.times >= res.data.toggleScreenFlag) {
+					// emits('forceSubmit') // 强制交卷
+					forceSubmitFn && forceSubmitFn()
+				}
+			}
+		});
+	}
+
+	onShow(() => {
+		if (!initStatus) {
+			return
+		}
+		visibilityState = 'visible'
+		if (toggleScreenFlag.value !== 0) {
+			console.log("有切屏");
+			cheatingFun();
+		}
+		if (zhuapaiFlag.value && toggleScreenFlag.value == 0) {
+			console.log("有抓拍 无切屏");
+			// 有抓拍 没有切屏  此方法是 解决切到后台,抓拍停留一帧的问题
+			zhuapaiFun();
+		}
+	})
+
+	onHide(() => {
+		if (!initStatus) {
+			return
+		}
+		visibilityState = 'hidden'
+		if (toggleScreenFlag.value !== 0) {
+			console.log("有切屏");
+			cheatingFun();
+		}
+		if (zhuapaiFlag.value && toggleScreenFlag.value == 0) {
+			console.log("有抓拍 无切屏");
+			// 有抓拍 没有切屏  此方法是 解决切到后台,抓拍停留一帧的问题
+			zhuapaiFun();
+		}
+	})
+
+	return {
+		init
+	}
+}

+ 10 - 2
components/zhuapaiConfirm/zhuapai.vue

@@ -2,13 +2,13 @@
 	<view class="zhuapai-drop-container" id="Drop" ref="DropRef" :style="style" @touchmove="touchmove($event)"
 		@touchstart="touchstart($event)">
 		<view class="phone-camera-box-zhuapai">
-			<camera class="camera" device-position="back" flash="off" style="width: 200px;height: 200px" :class="myClass" @initdone="onVideoSuccess"></camera>
+			<camera class="camera" device-position="front" flash="off" style="width: 200px;height: 200px" :class="myClass" @initdone="onVideoSuccess" @error="handleError" @stop="handleStop"></camera>
 			<!-- 用于抓拍切出去传递固定img-->
 			<img :src="imgUrl" alt="" v-show="false">
 			<!-- 测试抓拍使用 -->
 			<!-- <button @click="handleZhua">抓拍</button> -->
 		</view>
-		<span v-show="showVideo" @click="noShowVideoBtn" class="shiti-video-hidden-btn"><i>隐藏</i></span>
+		<span v-show="showVideo" @click="noShowVideoBtn" class="shiti-video-hidden-btn"><i></i></span>
 		<span v-show="!showVideo" @click="showVideoBtn" class="shiti-video-show-btn"><i></i></span>
 	</view>
 </template>
@@ -237,6 +237,14 @@
 		emits('error')
 	}
 
+  function handleError() {
+    emits('error')
+  }
+
+  function handleStop() {
+    emits('error')
+  }
+
 	// 针对视频通话的监听处理
 
 	function onTimeupdate() {

+ 15 - 4
pages/client/Kaoshi/exam.vue

@@ -413,9 +413,14 @@
 	function zpError() {
 		uni.showToast({
 			title: '抓拍图片异常',
-			icon: 'none'
+			icon: 'none',
+			duration: 3000,
+			mask: true // 防止触摸穿透
 		})
-		handleBack()
+		setTimeout(() => {
+			handleBack()
+		},3000)
+	
 	}
 
 	// 摄像头抓拍相关功能 end
@@ -424,9 +429,13 @@
 		uni.showToast({
 			icon: 'none',
 			title: '摄像头运行环境异常,请重新进入考试',
-			duration: 8000
+			duration: 3000,
+			mask: true // 防止触摸穿透
 		})
-		handleBack();
+		
+		setTimeout(() => {
+			handleBack();
+		},3000)
 	}
 	function zpConfirmSuccess() {
 		initKaoshi();
@@ -634,6 +643,7 @@
 			operId: data.operId,
 			biaoji: JSON.stringify(data.biaoji)
 		}).catch(err => {
+			console.log(22222,err)
 			handleBack()
 		})
 	}
@@ -722,6 +732,7 @@
 			});
 			startCountDown.value = true;
 		}).catch(err => {
+			console.log(33333,err)
       handleBack()
     })
 	}

+ 10 - 2
pages/client/Kaoshi/examCamera.vue

@@ -32,8 +32,16 @@
 		})
 	}
 
-	function zpConfirmError() {
-		handleBack();
+	function zpConfirmError(e) {
+    uni.showToast({
+      icon: 'none',
+      title: '考试环境异常请检测摄像头是否正常',
+      duration: 3000,
+      mask: true // 防止触摸穿透
+    })
+    setTimeout(() => {
+      handleBack();
+    }, 3000)
 	}
 
 	function zpConfirmCancel() {

+ 1 - 1
pages/client/Kaoshi/list.vue

@@ -9,7 +9,7 @@
 			:refresher-threshold="50" refresher-background="transparent" @refresherrefresh="onRefresh" @scrolltolower="onScrolltolower"
 			class="phone-scroll-view">
 			<uni-list class="admin-list-box">
-				<uni-list-item v-for="item in data.list" class="admin-list-item-box">
+				<uni-list-item v-for="item in data.list" class="admin-list-item-box" :key="item.ksId">
 					<template v-slot:body>
 						<!-- 考试项 -->
 						<view class="item-card-row">

+ 1 - 1
pages/client/Lianxi/list.vue

@@ -185,4 +185,4 @@
 
 <style>
 
-</style>
+</style>

+ 6 - 0
readme.md

@@ -0,0 +1,6 @@
+关于缓存
+
+1. 课程 不需要缓存 ,每次打开 从0开始播放
+2. 考试 ---- 按照单机
+3. 登录 ---- 按照单机
+4. 图片资源 ---- 长期缓存

+ 6 - 18
utils/request.js

@@ -57,15 +57,9 @@ const request = config => {
 							if (auth) {
 								cacheManager.clearAll()
 							}
-							if (auth.type == 2 || auth.type == 5) {
-								uni.reLaunch({
-									url: '/pages/Login/index'
-								})
-							} else {
-								uni.reLaunch({
-									url: '/pages/Login/clientIndex'
-								})
-							}
+							uni.reLaunch({
+								url: '/pages/Login/clientIndex'
+							})
 						
 						}
 					})
@@ -77,15 +71,9 @@ const request = config => {
 							if (auth) {
 								cacheManager.clearAll()
 							}
-							if (auth.type == 2 || auth.type == 5) {
-								uni.reLaunch({
-									url: '/pages/Login/index'
-								})
-							} else {
-								uni.reLaunch({
-									url: '/pages/Login/clientIndex'
-								})
-							}
+							uni.reLaunch({
+								url: '/pages/Login/clientIndex'
+							})
 							
 						}