xionghaojie 3 年之前
父节点
当前提交
90e853a21f
共有 10 个文件被更改,包括 762 次插入144 次删除
  1. 41 24
      pages/contentDetail/contentDetail.vue
  2. 57 0
      pages/login/login.css
  3. 259 28
      pages/login/login.vue
  4. 108 34
      pages/scoreQuery/scoreQuery.vue
  5. 11 0
      pages/test/test.css
  6. 235 45
      pages/test/test.vue
  7. 二进制
      static/images/sfyz.png
  8. 16 0
      utils/api.js
  9. 19 0
      utils/html2canvas.min.js
  10. 16 13
      utils/request.js

+ 41 - 24
pages/contentDetail/contentDetail.vue

@@ -8,12 +8,12 @@
 		<view class="project-content ddflex">
 			<!-- 成绩区域 -->
 			<view class="test-box">
-				<view class="test-title">二手车评估师考试</view>
+				<view class="test-title">{{content.title}}</view>
 				<view class="test-time">
 					<text style="color: #999999;margin-right: 10px;">发布日期</text>
-					<text>2022-08-25 09:30:00</text>
+					<text>{{content.time}}</text>
 				</view>
-				<mp-html :content="content" :lazy-load="true" @imgtap="choose"></mp-html>
+				<mp-html :content="content.text" :lazy-load="true" @imgtap="choose"></mp-html>
 				
 			</view>
 			<!-- 答题卡区域 -->
@@ -24,17 +24,8 @@
 						<view>考试公告</view>
 					</view>
 					<view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
-						</view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
-						</view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
+						<view class="note-item ddflex" v-for="item,index in otherInfo" :key="index" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
+							<view class="note-item-content">{{item.title}}</view>
 						</view>
 					</view>
 				</view>
@@ -45,13 +36,8 @@
 						<view>注意事项</view>
 					</view>
 					<view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
-						</view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
+						<view class="note-item ddflex" v-for="item,index in noteInfo" :key="index+'zy'" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
+							<view class="note-item-content">{{item.title}}</view>
 						</view>
 					</view>
 				</view>
@@ -70,20 +56,51 @@
 		data() {
 			return {
 				id:null,
-				content:''
+				content:'',
+				otherInfo:[],
+				noteInfo:[]
 			}
 		},
 		onReady() {},
 		onLoad(options) {
 			this.id = options.id
 			this.getDetail()
+			this.getTextOtherInfo()
+			this.getTextNoteInfo()
 		},
 		methods: {
+			jumpUrl(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
 			getDetail(){
-				req.getRequest('https://mock.apifox.cn/m1/2171629-0-default/api/content/detail',{},res=>{
+				req.getRequest('/api/content/detail',{id:this.id},res=>{
 					this.content = res
 				})
-			}
+			},
+			// 考试公告
+			getTextOtherInfo(){
+				let form={
+					code:req.public.ksggCode,
+					page:1,
+					limit:3
+				}
+				req.getRequest('/api/content/list',form,res=>{
+					this.otherInfo = res
+				})
+			},
+			// 注意事项
+			getTextNoteInfo(){
+				let form={
+					code:req.public.zysxCode,
+					page:1,
+					limit:3
+				}
+				req.getRequest('/api/content/list',form,res=>{
+					this.noteInfo = res
+				})
+			},
 		}
 	}
 

+ 57 - 0
pages/login/login.css

@@ -257,3 +257,60 @@
 .note-item-date {
 	color: #999999;
 }
+
+
+/* 弹窗 */
+.popup-box{
+	width: 722px;
+	min-height: 326px;
+	background: #FFFFFF;
+	box-shadow: 0px 0px 10px 1px rgba(81,0,0,0.1);
+	border-radius: 6px 6px 6px 6px;
+	opacity: 1;
+	border: 3px solid #FFFFFF;
+}
+.popup-top{
+	height: 65px;
+	line-height: 65px;
+	padding: 0 25px 0 17px;
+	border-bottom: 1px solid #EBEBEB;
+	font-size: 14px;
+	justify-content: space-between;
+}
+.popup-top image{
+	width: 29px;
+	height: 29px;
+	cursor: pointer;
+}
+.popup-content{
+	text-align: center;
+	font-size: 16px;
+	margin-top: 30px;
+}
+.popup-bottom{
+	margin-top: 30px;
+	justify-content: center;
+	margin-bottom: 30px;
+}
+.popup-btn1{
+	width: 146px;
+	height: 42px;
+	line-height: 42px;
+	border-radius: 21px 21px 21px 21px;
+	opacity: 1;
+	border: 1px solid #999999;
+	text-align: center;
+	cursor: pointer;
+}
+.popup-btn2{
+	width: 146px;
+	height: 42px;
+	line-height: 42px;
+	border-radius: 21px 21px 21px 21px;
+	opacity: 1;
+	border: 1px solid #DF230F;
+	margin-left: 30px;
+	text-align: center;
+	color: #DF230F;
+	cursor: pointer;
+}

+ 259 - 28
pages/login/login.vue

@@ -5,27 +5,21 @@
 		<view class="ddflex" style="flex-direction: column;">
 			<view class="register ddflex">
 				<view class="re-con-pic">
-					<!-- <view class="re-con-pic-title">在线考试系统</view> -->
 					<image src="/static/images/loginBg1.png"></image>
 				</view>
 				<view class="re-con">
 					<view class="re-tit">考生登录</view>
 					<view class="re-li dflex">
-						<!-- <image src="../../static/images/sjico.png" class="sjico"></image> -->
-						<input v-model="testNumber" type="number" placeholder="准考证号" maxlength="11" placeholder-class="placeholder" class="re-ipt flex" />
+						<input v-model="testNumber" type="number" placeholder="准考证号" maxlength="-1" placeholder-class="placeholder" class="re-ipt flex" />
 					</view>
 					<uni-data-select
-					      v-model="testProject"
+					      v-model="paperId"
 					      :localdata="range"
 					      @change="changeTestPro"
 						  placeholder="考试项目"
 					    ></uni-data-select>
-					<!-- <view class="re-li re-li-yzm dflex">
-						<input v-model="code" maxlength="4" placeholder="请输入验证码" placeholder-class="placeholder" class="re-ipt" />
-						<view class="yzm-box" @click="refresh()"><canvas :style="{ width: width + 'px', height: height + 'px' }" canvas-id="imgcanvas"></canvas></view>
-					</view> -->
 					<view class="re-btn" @click="okLogin()">登录</view>
-					<view class="re-btn-text" @click="jumpUrl('/pages/scoreQuery/scoreQuery')">考生成绩查询</view>
+					<view class="re-btn-text" @click="queryScore">考生成绩查询</view>
 				</view>
 			</view>
 			<view class="bottom-content ddflex">
@@ -38,7 +32,7 @@
 					<view>
 						<view class="note-item ddflex" v-for="item,index in otherInfo" :key="index" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
 							<view class="note-item-content">{{item.title}}</view>
-							<view class="note-item-date">{{dateFormat(item.creatDate)}}</view>
+							<view class="note-item-date">{{dateFormat(item.createDate)}}</view>
 						</view>
 					</view>
 				</view>
@@ -51,12 +45,38 @@
 					<view>
 						<view class="note-item ddflex" v-for="item,index in noteInfo" :key="index+'zy'" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
 							<view class="note-item-content">{{item.title}}</view>
-							<view class="note-item-date">{{dateFormat(item.creatDate)}}</view>
+							<view class="note-item-date">{{dateFormat(item.createDate)}}</view>
 						</view>
 					</view>
 				</view>
 			</view>
 		</view>
+		
+		<uni-popup ref="popup" type="bottom" :is-mask-click='false'>
+			<view class="popup-box">
+				<view class="popup-top ddflex">
+					<view>身份验证</view>
+					<image src="/static/images/close.png" @click="close()"></image>
+				</view>
+				<view class="popup-content">您好!考试前需要进行身份验证,请手持准考证,对准摄像头进行拍照,示例如下</view>
+				<view style="width: 400px;height: 243px;margin:30px auto;" v-show="!isPhotoing&&!imageUrl">
+					<img style="width: 100%;height: 100%" src="/static/images/sfyz.png"/>
+				</view>
+				<view v-show="isPhotoing" style="width: 400px;height: 243px;margin:30px auto;position: relative;" id="video-box">
+					<video id="video" style="width: 100%;height: 100%;" object-fit='fill' :autoplay="true" :controls="false" :show-center-play-btn="false"></video>
+					<canvas id="canvas" style="width: 100%;height: 100%;position: absolute;top:100000px;"  canvas-id="canvas"></canvas>
+					<view style="position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);z-index: 100;font-size: 50px;color: #1AA1E6;font-weight: bold;">{{timeText}}</view>
+				</view>
+				<view style="width: 400px;height: 243px;margin:30px auto;" v-show="!isPhotoing&&imageUrl">
+					<img style="width: 100%;height: 100%" :src="imageUrl"/>
+				</view>
+				<view class="popup-bottom ddflex">
+					<view class="popup-btn2" @click="yanzheng" v-show="!isPhotoing">{{imageUrl?'重新验证':'立即验证'}}</view>
+					<view class="popup-btn2" v-if="imageUrl" @click="yanzhengSubmit">确认</view>
+				</view>
+			</view>
+		</uni-popup>
+		
 		<Foot :havaBackground='false'></Foot>
 	</view>
 </template>
@@ -65,6 +85,7 @@
 const app = getApp();
 const req = require('../../utils/request.js');
 const utils = require('../../utils/util.js');
+const api = require('../../utils/api.js');
 export default {
 	data() {
 		return {
@@ -72,12 +93,18 @@ export default {
 			width: 115,
 			height: 36,
 			testNumber: '',
-			testProject:'',
 			code: '',
 			isCheckArg: true,
 			range:[],
 			otherInfo:[],//考试公告
 			noteInfo:[],//注意事项
+			
+			paperId:'',//考试id
+			
+			isPhotoing:false,//是否正在验证身份
+			imageUrl:'',
+			timeText:3,
+			timeInterval:null
 		};
 	},
 	onLoad() {
@@ -85,9 +112,8 @@ export default {
 		this.getTestProject()
 		this.getTextOtherInfo()
 		this.getTextNoteInfo()
-		setTimeout(function() {
-			tha.initCode();
-		}, 200);
+	},
+	onShow() {
 	},
 	methods: {
 		jumpUrl(url){
@@ -102,7 +128,7 @@ export default {
 		// 获取考试项目
 		getTestProject(){
 			console.log('11')
-			req.getRequest('https://mock.apifox.cn/m1/2171629-0-default/api/test/project',{},res=>{
+			req.getRequest('/api/v3/exam/user/manager',{},res=>{
 				console.log('this.range',res)
 				this.range = res.map(item=>{return {text:item.title,value:item.id}})
 				console.log(this.range)
@@ -110,33 +136,92 @@ export default {
 		},
 		// 考试公告
 		getTextOtherInfo(){
-			req.getRequest('https://mock.apifox.cn/m1/2171629-0-default/api/ksgg',{},res=>{
+			let form={
+				code:req.public.ksggCode,
+				page:1,
+				limit:3
+			}
+			req.getRequest('/api/content/list',form,res=>{
 				this.otherInfo = res
 			})
 		},
-		// 考试公告
+		// 注意事项
 		getTextNoteInfo(){
-			req.getRequest('https://mock.apifox.cn/m1/2171629-0-default/api/ksgg',{type:2},res=>{
+			let form={
+				code:req.public.zysxCode,
+				page:1,
+				limit:3
+			}
+			req.getRequest('/api/content/list',form,res=>{
 				this.noteInfo = res
 			})
 		},
+		
+		open() {
+			this.$refs.popup.open('center')
+		},
+		close() {
+			this.$refs.popup.close()
+			clearInterval(this.timeInterval)
+			this.isPhotoing = false
+			this.imageUrl = ''
+		},
+		
 		okLogin() {
 			if (!this.testNumber) {
 				return req.msg('请输入准考证号');
 			}
-			if (!this.testProject) {
+			if (!this.paperId) {
+				return req.msg('请选择考试项目');
+			}
+			var formP = {
+				admission: this.testNumber,
+				paperId: this.paperId
+			};
+			req.postRequest('/api/admission/login', formP, data => {
+				if(data){
+					req.setStorage('userInfo', data);
+					// 如果进行身份验证
+					// this.open()
+					
+					// 直接考试,是否允许考试
+					req.getRequest(api.get_exam_question + this.paperId,{},quests=>{
+						if (quests && quests.length > 0) {
+							uni.navigateTo({
+								url: '/pages/test/test?paperId='+this.paperId,
+								success:()=> {
+									this.close()
+								}
+							});
+						}
+					})
+				}
+			});
+		},
+		queryScore(){
+			if (!this.testNumber) {
+				return req.msg('请输入准考证号');
+			}
+			if (!this.paperId) {
 				return req.msg('请选择考试项目');
 			}
 			var formP = {
-				testNumber: this.testNumber,
-				testProject: this.testProject
+				admission: this.testNumber,
+				paperId: this.paperId
 			};
-			uni.reLaunch({
-				url: '/pages/test/test'
+			req.getRequest('/api/v3/exam/user/manager/result', formP, res => {
+					uni.navigateTo({
+						url:'/pages/scoreQuery/scoreQuery?paperId='+ this.paperId+'&admissionNo='+this.testNumber
+					})
 			});
-			return false
-			req.postRequest('/api/v3/hy/account/pwdLogin', formP, data => {
-				this.getUserInfo();
+		},
+		// 验证提交
+		yanzhengSubmit(){
+			uni.navigateTo({
+				url: '/pages/test/test?paperId='+this.paperId,
+				success:()=> {
+					this.close()
+				}
 			});
 		},
 		// 时间格式化
@@ -162,8 +247,154 @@ export default {
 				true
 			);
 		},
-
 		
+		// 身份验证
+		yanzheng(){
+			this.imageUrl = ''
+			this.test()
+		},
+		takePhoto() {
+			let _this = this
+		    //获得Canvas对象
+			const query = uni.createSelectorQuery().in(this);
+		    let video = document.querySelector('video');
+			let canvas = document.querySelector('canvas')
+		    let ctx = canvas.getContext('2d');
+			let videoInfo = {}
+			query.select('video').boundingClientRect(result => {
+				console.log('videoInfo',result)
+				videoInfo={
+					width:result.width,
+					height:result.height
+				}
+			}).exec();
+		    ctx.drawImage(video, 0, 0, videoInfo.width, videoInfo.height);
+			// console.log(this.dataURLtoBlob(canvas.toDataURL(),'11'))
+			// req.uploadFile('/api/upload', canvas.toDataURL(), res => {
+			// 	req.msg('图片上传成功');
+			// 	console.log('图片上传成功',res)
+			// });
+			_this.imageUrl = canvas.toDataURL()
+			_this.isPhotoing = false
+			// setTimeout(() => {
+			// 	uni.canvasToTempFilePath({
+			// 		canvasId: 'canvas',
+			// 		destWidth: 60,
+			// 		destHeight: 40,
+			// 		success: function (res) {
+			// 			console.log(res.tempFilePath) //图片路径
+			// 			// resolve(res.tempFilePath)
+			// 			_this.imgUrl = res.tempFilePath
+			// 		},
+			// 		fail: function (res) {
+			// 			console.log(res.errMsg)
+			// 		}
+			// 	})
+			// }, 100)
+		},
+		dataURLtoBlob(dataurl) {
+		    var arr = dataurl.split(','),
+		            mime = arr[0].match(/:(.*?);/)[1],
+		            bstr = atob(arr[1]),
+		            n = bstr.length,
+		            u8arr = new Uint8Array(n);//8位无符号整数,长度1个字节
+		        console.log(mime)
+		        while (n--) {
+		            u8arr[n] = bstr.charCodeAt(n);
+		        }
+		        // console.log(JSON.stringify(u8arr));
+		        return new Blob([u8arr], {
+		            type: mime
+		        });
+		    },
+		base64toFile (dataurl, filename){
+			var arr = dataurl.split(','),
+			    mime = arr[0].match(/:(.*?);/)[1],
+			    bstr = atob(arr[1]),
+			    n = bstr.length,
+			    u8arr = new Uint8Array(n);
+			while (n--) {
+			    u8arr[n] = bstr.charCodeAt(n);
+			}
+			return new File([u8arr], filename, {
+			    type: mime
+			});
+		},
+		test(){
+			// var video = document.querySelector('video');
+			const query = uni.createSelectorQuery().in(this);
+			var video = query.select('#video')
+			console.log('video',video)	
+			// 兼容代码
+			window.URL = (window.URL || window.webkitURL || window.mozURL || window.msURL);
+			// 获取媒体属性,旧版本浏览器可能不支持mediaDevices,我们首先设置一个空对象
+			if (navigator.mediaDevices === undefined) {
+			    navigator.mediaDevices = {};
+			}
+			console.log('navigator.mediaDevices',navigator.mediaDevices)
+			// 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
+			// 使用getUserMedia,因为它会覆盖现有的属性。
+			// 这里,如果缺少getUserMedia属性,就添加它。
+			if (navigator.mediaDevices.getUserMedia === undefined) {
+				navigator.mediaDevices.getUserMedia = function(constraints) {
+					// 首先获取现存的getUserMedia(如果存在)
+				    var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+					// 有些浏览器不支持,会返回错误信息
+					// 保持接口一致
+				    if (!getUserMedia) { //不存在则报错
+				        return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
+				    }
+					// 否则,使用Promise将调用包装到旧的navigator.getUserMedia
+				    return new Promise(function(resolve, reject) {
+				        getUserMedia.call(navigator, constraints, resolve, reject);
+				    });
+				}
+			}
+			//摄像头调用配置
+			var mediaOpts = {
+			    audio: false,
+			    video: { facingMode: "user"} 
+			    
+			}
+						 
+			let that=this;
+			navigator.mediaDevices.getUserMedia(mediaOpts).then(function(stream) {
+			    that.mediaStreamTrack = stream;
+			    video = document.querySelector('video');
+				console.log('video = document.querySelector(video);',video)
+					// 旧的浏览器可能没有srcObject
+			    if ("srcObject" in video) {
+			        video.srcObject = stream
+			    } else {
+					// 避免在新的浏览器中使用它,因为它正在被弃用。
+			        video.src = window.URL && window.URL.createObjectURL(stream) || stream
+			    }
+			    video.play();
+				that.isPhotoing = true
+				that.timeText = 3
+				that.timeInterval = setInterval(()=>{
+					if(that.timeText>0){
+						that.timeText--
+					}else{
+						clearInterval(that.timeInterval)
+						that.timeInterval= null
+						that.takePhoto()
+					}
+				},1000)
+			}).catch(function (err) {
+			    console.log(err)
+				uni.showModal({
+					title:'提示',
+					content:'未找到摄像头',
+					showCancel:false,
+					success() {
+						// uni.navigateBack({
+							
+						// })
+					}
+				})
+			});
+		},
 	}
 };
 </script>

+ 108 - 34
pages/scoreQuery/scoreQuery.vue

@@ -8,53 +8,59 @@
 		<view class="project-content ddflex">
 			<!-- 成绩区域 -->
 			<view class="test-box">
-				<view class="test-title">二手车评估师考试</view>
+				<view class="test-title">{{examResult.title}}</view>
 				<view class="test-time">
 					<text style="color: #999999;margin-right: 10px;">考试时间</text>
-					<text>2022-08-25 09:30:00</text>
+					<text>{{examResult.createDate}}</text>
 				</view>
-				
-				<view v-if="!true">
+				<!--  用户考试状态 0未交卷 1已交卷 2阅卷中 3缺考 4 阅卷完成 -->
+				<view v-if="examResult.isPapers!=4">
 					<image class="yj-icon" src="/static/images/yuejuan.png"></image>
-					<view class="yj-label">正在阅卷中... </view>
+					<view class="yj-label">
+						<block v-if="examResult.state==0">未交卷</block>
+						<block v-if="examResult.state==1">已交卷,等待阅卷中</block>
+						<block v-if="examResult.state==3">缺考</block>
+					</view>
 				</view>
-				<view v-else class="chufen-number">
-					84<text style="font-size: 18px;margin-left: 10px;">分</text>
-					<view style="font-size: 16px;">考试不合格</view>
+				<view v-else class="chufen-number" :style="examResult.score>=paper.passScore?'color:green;':''">
+					{{examResult.score}}<text style="font-size: 18px;margin-left: 10px;">分</text>
+					<view style="font-size: 16px;">
+						{{examResult.score>=paper.passScore?'考试合格':'考试不合格'}}
+					</view>
 				</view>
 				
 				<view class="score ddflex" >
 					<view>
 						<view class="score-label">总题数</view>
-						<view class="score-value">23</view>
+						<view class="score-value">{{examResult.questionCount?examResult.questionCount:0}}</view>
 					</view>
 					<view class="line"></view>
 					<view>
 						<view class="score-label">考试用时</view>
-						<view class="score-value">20:42:14</view>
+						<view class="score-value">{{examResult.time?timeChangeHMS(examResult.time):'00:00:00'}}</view>
 					</view>
 				</view>
-				<view class="back-btn ddflex">
+				<navigator class="back-btn ddflex" hover-class="none" open-type="navigateBack">
 					<image src="/static/images/back3.png"></image>
 					<text>返回上一页</text>
-				</view>
+				</navigator>
 			</view>
 			<!-- 答题卡区域 -->
 			<view class="test-info ddflex">
 				<view class="test-people ddflex">
-					<image></image>
+					<image :src="userInfo.avatar"></image>
 					<view class="test-people-info ddflex">
 						<view class="ddflex">
 							<view class="test-people-label">考生姓名</view>
-							<view class="fflex">李田</view>
+							<view class="fflex">{{userInfo.userName}}</view>
 						</view>
 						<view class="ddflex">
 							<view class="test-people-label">准考证号</view>
-							<view class="fflex" style="word-break: break-all;">4512541244</view>
+							<view class="fflex" style="word-break: break-all;">{{userInfo.admissionNo}}</view>
 						</view>
 						<view class="ddflex">
 							<view class="test-people-label">考试科目</view>
-							<view class="fflex">二手车评估师</view>
+							<view class="fflex">{{userInfo.cateName}}</view>
 						</view>
 					</view>
 				</view>
@@ -64,17 +70,8 @@
 						<view>考试公告</view>
 					</view>
 					<view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
-						</view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
-						</view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
+						<view class="note-item ddflex" v-for="item,index in otherInfo" :key="index" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
+							<view class="note-item-content">{{item.title}}</view>
 						</view>
 					</view>
 				</view>
@@ -85,13 +82,8 @@
 						<view>注意事项</view>
 					</view>
 					<view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
-						</view>
-						<view class="note-item ddflex">
-							<view class="note-item-content">2022年“技能+学历”双证教育班培训简章</view>
-							<!-- <view class="note-item-date">2022-08-25</view> -->
+						<view class="note-item ddflex" v-for="item,index in noteInfo" :key="index+'zy'" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
+							<view class="note-item-content">{{item.title}}</view>
 						</view>
 					</view>
 				</view>
@@ -103,13 +95,95 @@
 </template>
 
 <script>
+	const app = getApp();
+	const req = require('../../utils/request.js');
+	const utils = require('../../utils/util.js');
+	const api = require('../../utils/api.js')
 	export default {
 		data() {
 			return {
+				userInfo:{},
+				paperId:null,
+				admissionNo:null,
+				paper: '', //试卷详情
+				examResult:{},//考试详情
+				
+				otherInfo:[],
+				noteInfo:[]
 			}
 		},
 		onReady() {},
+		onLoad(options) {
+			this.paperId = options.paperId;
+			this.admissionNo = options.admissionNo;
+			
+			this.getUserInfo()
+			this.getPaper()
+			this.getTextOtherInfo()
+			this.getTextNoteInfo()
+		},
 		methods: {
+			jumpUrl(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+			getUserInfo(){
+				req.getRequest('/api/v3/exam/user/manager/info',{},res=>{
+					this.userInfo = res
+				})
+			},
+			//获取考试对象
+			getPaper() {
+				var _ts = this;
+				req.getRequest(api.paper_detail + _ts.paperId, {}, res => {
+						_ts.paper = res;
+						this.getResult()
+				});
+			},
+			// 获取考试对象的答题记录
+			getResult(){
+				req.getRequest('/api/v3/exam/user/manager/result', {admission:this.admissionNo,paperId:this.paperId}, res => {
+						this.examResult = res;
+				});
+			},
+			// 考试公告
+			getTextOtherInfo(){
+				let form={
+					code:req.public.ksggCode,
+					page:1,
+					limit:3
+				}
+				req.getRequest('/api/content/list',form,res=>{
+					this.otherInfo = res
+				})
+			},
+			// 注意事项
+			getTextNoteInfo(){
+				let form={
+					code:req.public.zysxCode,
+					page:1,
+					limit:3
+				}
+				req.getRequest('/api/content/list',form,res=>{
+					this.noteInfo = res
+				})
+			},
+			// 秒转换时分秒
+			timeChangeHMS(time){
+				let _ts = this
+				var times = {};
+				times.hour = _ts.cut22(Math.floor(time / 3600));
+				var leave2 = time % 3600;
+				times.minutes = _ts.cut22(Math.floor(leave2 / 60));
+				var leave3 = leave2 % 60;
+				times.seconds = _ts.cut22(Math.round(leave3));
+				return times.hour+":"+times.minutes+":"+times.seconds
+			},
+			cut22(n) {
+				n = n.toString();
+				return n[1] ? n : '0' + n;
+			},
 		}
 	}
 

+ 11 - 0
pages/test/test.css

@@ -339,4 +339,15 @@
 	color: #DEDEDE;
 	background-color: #DEDEDE;
 	margin: 0 30px;
+}
+
+@media only screen and (max-width: 1400px) {
+    .video-box{
+		width: 180px;height: 100px;position: fixed;top:0;right: 0;z-index: 500;
+	}
+}
+@media only screen and (min-width: 1401px) {
+    .video-box{
+    	width: 280px;height: 200px;position: fixed;top:0;right: 0;z-index: 500;
+    }
 }

+ 235 - 45
pages/test/test.vue

@@ -1,10 +1,15 @@
 <template>
-	<view>
+	<view style="position: relative;">
 		<Headers></Headers>
 		<view class="project">
-			<view style="width: 1200px;margin: auto;">二手车评估师考试</view>
+			<view style="width: 1200px;margin: auto;">{{paper.title}}</view>
 		</view>
 		
+		<view class="video-box" id="video-box" @click="takePhoto">
+			<video id="video" style="width: 100%;height: 100%;" :autoplay="true" :controls="true" :show-center-play-btn="false"></video>
+			<canvas id="canvas" style="width: 100%;height: 100%;"  canvas-id="canvas"></canvas>
+		</view>
+		<img  :src="imgUrl" ></img>
 		<view class="project-content ddflex">
 			<!-- 考试区域 -->
 			<view class="test-box">
@@ -54,19 +59,19 @@
 			<!-- 答题卡区域 -->
 			<view class="test-info ddflex">
 				<view class="test-people ddflex">
-					<image></image>
+					<image :src="userInfo.avatar"></image>
 					<view class="test-people-info ddflex">
 						<view class="ddflex">
 							<view class="test-people-label">考生姓名</view>
-							<view class="fflex">李田</view>
+							<view class="fflex">{{userInfo.userName}}</view>
 						</view>
 						<view class="ddflex">
 							<view class="test-people-label">准考证号</view>
-							<view class="fflex" style="word-break: break-all;">4512541244</view>
+							<view class="fflex" style="word-break: break-all;">{{userInfo.admissionNo}}</view>
 						</view>
 						<view class="ddflex">
 							<view class="test-people-label">考试科目</view>
-							<view class="fflex">二手车评估师</view>
+							<view class="fflex">{{userInfo.cateName}}</view>
 						</view>
 					</view>
 				</view>
@@ -114,12 +119,12 @@
 			<view class="popup-box" v-if="state==1">
 				<view class="popup-top ddflex">
 					<view>您正在结束作答</view>
-					<image src="/static/images/close.png" @click="close()"></image>
+					<image src="/static/images/close.png" v-if="time > 0" @click="close()"></image>
 				</view>
 				<!-- {{(quests.filter(item=>{return item.isSubmit}).length<quests.length)?'您还有未完成的考题,是否确定交卷?':'您已完成全部考题,是否确定交卷?'}} -->
 				<view class="popup-content">{{(quests.filter(item=>{return item.isSubmit}).length < quests.length ? '您还有未完成的考题,是否确定交卷?':'您已完成全部考题,是否确定交卷?')}}</view>
 				<view class="popup-bottom ddflex">
-					<view class="popup-btn1" @click="close()">继续考试</view>
+					<view class="popup-btn1" @click="close()" v-if="time > 0">继续考试</view>
 					<view class="popup-btn2" @click="submit">确定交卷</view>
 				</view>
 			</view>
@@ -129,21 +134,23 @@
 					<image src="/static/images/close.png" @click="close()"></image>
 				</view>
 				<view class="popup-content" style="margin-top: 23px;">
-					<view class="chufen-title">二手车评估师考试</view>
-					<view class="chufen-number">
-						84<text style="font-size: 18px;margin-left: 10px;">分</text>
-						<view style="font-size: 16px;">考试不合格</view>
+					<view class="chufen-title">{{result.title}}</view>
+					<view class="chufen-number" :style="result.score>=paper.passScore?'color:green;':''">
+						{{result.score}}<text style="font-size: 18px;margin-left: 10px;">分</text>
+						<view style="font-size: 16px;">{{result.score>=paper.passScore?'考试合格':'考试不合格'}}</view>
 					</view>
 				</view>
 				<view class="popup-bottom ddflex" style="margin-top: 30px;margin-bottom: 60px;padding-left: 30px">
 					<view>
 						<view class="popup-bottom-label">总题数</view>
-						<view class="popup-bottom-value">23</view>
+						<view class="popup-bottom-value">{{result.questToal}}</view>
 					</view>
 					<view class="line"></view>
 					<view>
 						<view class="popup-bottom-label">考试用时</view>
-						<view class="popup-bottom-value">20:42:14</view>
+						<view class="popup-bottom-value">
+							{{timeChangeHMS(result.duration)}}
+						</view>
 					</view>
 				</view>
 			</view>
@@ -157,11 +164,15 @@
 	const app = getApp();
 	const req = require('../../utils/request.js');
 	const utils = require('../../utils/util.js');
+	const api = require('../../utils/api.js')
+	import html2canvas from "../../utils/html2canvas.min.js";
 	export default {
 		data() {
 			return {
 				state:1,
 				
+				userInfo:{},
+				
 				paperId: '', //考试题目
 				paperTitle: '', //考试标题
 				paperType: '', //1每日一练 2模拟试题 3大厂真题 5企业真题
@@ -182,14 +193,51 @@
 				
 				answerTxt: '', //填空题、简答题答案
 				isGoBack: false,
-				isShowView: false
+				isShowView: false,
+				
+				isClearLogin:true,//是否离开页面清除缓存
+				
+				result:null,//考试结果
+				
+				buffer:null,
+				video:{src:null},
+				canvas:null,
+				ctx:null,
+				imgUrl:null
 			}
 		},
 		onReady() {},
-		onLoad() {
-			this.getQuests()
+		async onLoad(options) {
+			this.paperId = options.paperId;
+
+			console.log(this.userInfo)
+			// 需要先判断用户是否还能考试
+			await this.getQuests();
+			
+			// 摄像头
+			const query = uni.createSelectorQuery().in(this);
+			this.video = query.select('#video')
+			this.canvas = query.select('#canvas');
+			this.test()
+			
+			
+			// 考生信息
+			this.getUserInfo()
+			// 考试数据
+			this.getPaper();
+			// this.getQuests();
+		},
+		onUnload() {
+			if(this.isClearLogin)
+				uni.clearStorageSync();
 		},
 		methods: {
+			// 考试信息
+			getUserInfo(){
+				req.getRequest('/api/v3/exam/user/manager/info',{},res=>{
+					this.userInfo = res
+				})
+			},
 			getQuestType() {
 				// <!-- 1单选 2多选 3 判断 4 填空 5 简答', -->
 				var text = '';
@@ -241,20 +289,23 @@
 
 			getQuests(){
 				let _ts = this
-				req.getRequest('https://mock.apifox.cn/m1/2171629-0-default/api/paper/quest/init/2834',{},quests=>{
-					console.log(quests)
-					if (quests && quests.length > 0) {
-						_ts.questTotal = quests.length;
-						for (var k = 0; k < quests.length; k++) {
-							var quest = quests[k];
-							quest.eitype = _ts.etype;
-							quest.title = quest.title.replace(/<code class="(.*?)"/gi, '<code class="Java" ');
-						}
-						if (_ts.questTotal > 0) {
-							_ts.quest = _ts.setQuest(quests[0]);
+				return new Promise((r,j)=>{
+					req.getRequest(api.get_exam_question + _ts.paperId,{},quests=>{
+						console.log(quests)
+						if (quests && quests.length > 0) {
+							_ts.questTotal = quests.length;
+							for (var k = 0; k < quests.length; k++) {
+								var quest = quests[k];
+								quest.eitype = _ts.etype;
+								quest.title = quest.title.replace(/<code class="(.*?)"/gi, '<code class="Java" ');
+							}
+							if (_ts.questTotal > 0) {
+								_ts.quest = _ts.setQuest(quests[0]);
+							}
+							_ts.quests = quests;
+							r()
 						}
-						_ts.quests = quests;
-					}
+					})
 				})
 			},
 			
@@ -267,7 +318,8 @@
 					if (_ts.ptime) {
 						clearTimeout(_ts.ptime);
 					}
-					_ts.submitTest();
+					_ts.open()
+					// _ts.submitTest();
 					return;
 				}
 				var times = {};
@@ -282,7 +334,17 @@
 					_ts.setTime();
 				}, 1000);
 			}, //下次继续
-						
+			// 秒转换时分秒
+			timeChangeHMS(time){
+				let _ts = this
+				var times = {};
+				times.hour = _ts.cut22(Math.floor(time / 3600));
+				var leave2 = time % 3600;
+				times.minutes = _ts.cut22(Math.floor(leave2 / 60));
+				var leave3 = leave2 % 60;
+				times.seconds = _ts.cut22(Math.round(leave3));
+				return times.hour+":"+times.minutes+":"+times.seconds
+			},
 			cut22(n) {
 				n = n.toString();
 				return n[1] ? n : '0' + n;
@@ -416,10 +478,9 @@
 						
 			toQuestTip(index) {
 				var _ts = this;
-				var _ts = this;
 				_ts.cIndex = index;
 				_ts.quest = _ts.setQuest(_ts.quests[_ts.cIndex]);
-				_ts.closeCeng();
+				// _ts.closeCeng();
 				// });
 			},
 						
@@ -460,17 +521,13 @@
 						
 			//提交考试+答案
 			submit() {
-				uni.redirectTo({
-					url:'/pages/scoreQuery/scoreQuery'
-				})
-				return
 				var _ts = this;
 				if (_ts.ptime) {
 					clearTimeout(_ts.ptime);
 				}
 				var dataP = {
 					uniqueId: _ts.paperId,
-					uniqueType: _ts.type
+					uniqueType: 1
 				};
 				//组装考试题目
 				dataP.questionList = this.quests.map(it => {
@@ -501,13 +558,17 @@
 					dataP,
 					res => {
 						if (!_ts.paper.isScore) {//手动阅卷
+							_ts.isClearLogin = false
 							uni.redirectTo({
-								url: '/learn/marking/index?id=' + res.id
-							});
+								url:'/pages/scoreQuery/scoreQuery?paperId='+ _ts.paperId+'&admissionNo='+_ts.userInfo.admissionNo
+							})
+							return
 						} else {//自动阅卷
-							uni.redirectTo({
-								url: '/learn/result/index?id=' + res.id
-							});
+							// uni.redirectTo({
+							// 	url: '/learn/result/index?id=' + res.id
+							// });
+							this.state=2
+							this.result = res
 						}
 					},
 					true
@@ -520,7 +581,136 @@
 			close() {
 				this.$refs.popup.close()
 			},
-		}
+			
+			
+			takePhoto() {
+				let _this = this
+			    //获得Canvas对象
+				const query = uni.createSelectorQuery().in(this);
+			    let video = document.querySelector('video');
+				let canvas = document.querySelector('canvas')
+			    let ctx = canvas.getContext('2d');
+				let videoInfo = {}
+				query.select('video').boundingClientRect(result => {
+					console.log('videoInfo',result)
+					videoInfo={
+						width:result.width,
+						height:result.height
+					}
+				}).exec();
+			    ctx.drawImage(video, 0, 0, videoInfo.width, videoInfo.height);
+				console.log(this.dataURLtoBlob(canvas.toDataURL(),'11'))
+				// setTimeout(() => {
+				// 	uni.canvasToTempFilePath({
+				// 		canvasId: 'canvas',
+				// 		destWidth: 60,
+				// 		destHeight: 40,
+				// 		success: function (res) {
+				// 			console.log(res.tempFilePath) //图片路径
+				// 			// resolve(res.tempFilePath)
+				// 			_this.imgUrl = res.tempFilePath
+				// 		},
+				// 		fail: function (res) {
+				// 			console.log(res.errMsg)
+				// 		}
+				// 	})
+				// }, 100)
+			},
+			dataURLtoBlob(dataurl) {
+			                            var arr = dataurl.split(','),
+			                                mime = arr[0].match(/:(.*?);/)[1],
+			                                bstr = atob(arr[1]),
+			                                n = bstr.length,
+			                                u8arr = new Uint8Array(n);//8位无符号整数,长度1个字节
+			                            console.log(mime)
+			                            while (n--) {
+			                                u8arr[n] = bstr.charCodeAt(n);
+			                            }
+			                            // console.log(JSON.stringify(u8arr));
+			                            return new Blob([u8arr], {
+			                                type: mime
+			                            });
+			                        },
+			base64toFile (dataurl, filename){
+				var arr = dataurl.split(','),
+				    mime = arr[0].match(/:(.*?);/)[1],
+				    bstr = atob(arr[1]),
+				    n = bstr.length,
+				    u8arr = new Uint8Array(n);
+				while (n--) {
+				    u8arr[n] = bstr.charCodeAt(n);
+				}
+				return new File([u8arr], filename, {
+				    type: mime
+				});
+			},
+			test(){
+				// var video = document.querySelector('video');
+				const query = uni.createSelectorQuery().in(this);
+				var video = query.select('#video')
+				console.log('video',video)	
+				// 兼容代码
+				window.URL = (window.URL || window.webkitURL || window.mozURL || window.msURL);
+				// 获取媒体属性,旧版本浏览器可能不支持mediaDevices,我们首先设置一个空对象
+				if (navigator.mediaDevices === undefined) {
+				    navigator.mediaDevices = {};
+				}
+				console.log('navigator.mediaDevices',navigator.mediaDevices)
+				// 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
+				// 使用getUserMedia,因为它会覆盖现有的属性。
+				// 这里,如果缺少getUserMedia属性,就添加它。
+				if (navigator.mediaDevices.getUserMedia === undefined) {
+					navigator.mediaDevices.getUserMedia = function(constraints) {
+						// 首先获取现存的getUserMedia(如果存在)
+					    var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+						// 有些浏览器不支持,会返回错误信息
+						// 保持接口一致
+					    if (!getUserMedia) { //不存在则报错
+					        return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
+					    }
+						// 否则,使用Promise将调用包装到旧的navigator.getUserMedia
+					    return new Promise(function(resolve, reject) {
+					        getUserMedia.call(navigator, constraints, resolve, reject);
+					    });
+					}
+				}
+				//摄像头调用配置
+				var mediaOpts = {
+				    audio: false,
+				    video: { facingMode: "user"} 
+				    
+				}
+							 
+				let that=this;
+				navigator.mediaDevices.getUserMedia(mediaOpts).then(function(stream) {
+				    that.mediaStreamTrack = stream;
+				    video = document.querySelector('video');
+					console.log('video = document.querySelector(video);',video)
+						// 旧的浏览器可能没有srcObject
+				    if ("srcObject" in video) {
+				        video.srcObject = stream
+						that.video.srcObject = stream
+				    } else {
+						// 避免在新的浏览器中使用它,因为它正在被弃用。
+				        video.src = window.URL && window.URL.createObjectURL(stream) || stream
+						that.video.src = window.URL && window.URL.createObjectURL(stream) || stream
+				    }
+				    video.play();
+				}).catch(function (err) {
+				    console.log(err)
+					uni.showModal({
+						title:'提示',
+						content:'未找到摄像头',
+						showCancel:false,
+						success() {
+							// uni.navigateBack({
+								
+							// })
+						}
+					})
+				});
+			},
+		},
 	}
 
 </script>

二进制
static/images/sfyz.png


+ 16 - 0
utils/api.js

@@ -0,0 +1,16 @@
+module.exports = {
+	content_list: '/api/content/list', //首页
+	exam_total: '/api/exam/total', //考试统计
+	paper_page: '/api/paper/page', //试卷列表
+	paper_detail: '/api/paper/', //试卷详情
+	paper_create: '/api/paper/create', //生成试卷订单
+	p_result: '/api/result/', //考试详情
+	// get_exam_question: '/api/paper/quest/init/', //考卷试题列表
+	get_exam_question:'/api/paper/quest/get_exam_question/',
+	judge_question: '/api/answer/quest/judge_question', //提交单个答案
+	complete_exam: '/api/v3/result/complete_exam', //试卷交卷统一提交答案
+	result_page: '/api/result/page', //做题记录列表
+	answer_quest_get: '/api/answer/quest/get/', //重做获取题目
+};
+
+

文件差异内容过多而无法显示
+ 19 - 0
utils/html2canvas.min.js


+ 16 - 13
utils/request.js

@@ -1,23 +1,23 @@
 const app = getApp();
 const env = {
-	NODE_ENV: 'prd',
+	NODE_ENV: 'dev',
 	dev: {
-		apiUrl: 'http://zhly.tongyu99.com'
+		apiUrl: 'https://apitest.tongyu99.com'
 	},
 	prd: {
-		apiUrl: 'https://wlapi.zhlc.liucheng.gov.cn'
+		apiUrl: 'https://apimall.zhiqiyun.com'
 		// apiUrl: 'https://wlapi-pro-smartcity.123cx.com'
 	},
 	pota: {
 		// apiUrl: 'http://192.168.110.248:7012' //y
-		apiUrl: 'http://192.168.110.97:7012' ,//w
+		// apiUrl: 'http://192.168.110.97:7012' ,//w
 		apiUrl:'http://192.168.110.109:7012',//w
 	}
 }
 
 var header = {
 	'content-type': 'application/x-www-form-urlencoded',
-	'appId': 'ZQV47ZfNRAcKDWn',
+	'appId': 'ZQfMJBmiKkUKE0g',
 }
 
 /**小程序配置 */
@@ -42,6 +42,9 @@ const publicss = {
 
 	//信息发布code
 	'zwtz': 'zwtz_2', //政务通知
+	
+	'ksggCode':'003',//考试公告
+	'zysxCode':'005'//注意事项
 
 }
 /*
@@ -120,8 +123,8 @@ const baseRequest = (url, data, method, success, isLoad) => {
 			header.authorityToken = getStorage('admin_authToken');
 		}
 		uni.request({
-			// url: env[env.NODE_ENV].apiUrl + url,
-			url: url,
+			url: env[env.NODE_ENV].apiUrl + url,
+			// url: url,
 			data: clearValueEmpty(data),
 			method: method,
 			header: header,
@@ -134,13 +137,13 @@ const baseRequest = (url, data, method, success, isLoad) => {
 					removeStorage('AUTH_TOKENS');
 					return redirectIndex();
 				}
-				if (getStorage('userInfo').type != 1 && json.data.code === 25001) { //未认证
-					uni.redirectTo({
-						url: '/pages/company/company'
-					});
-					return;
+				if(json.data.code!==0&&json.data.msg=='答题次数已用完'){
+					console.log('答题次数已用完')
+					return alertMsg(json.data.msg,()=>{
+						removeStorage('AUTH_TOKENS');
+						redirectIndex();
+					})
 				}
-
 				if (json.data.code !== 0) {
 					return msg(json.data.msg)
 				}

部分文件因为文件数量过多而无法显示