selectTest.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <template>
  2. <view>
  3. <Headers></Headers>
  4. <view class="project">
  5. <view style="width: 1200px;margin: auto;">职业技能在线考试系统</view>
  6. </view>
  7. <view class="project-content ddflex">
  8. <!-- 成绩区域 -->
  9. <view class="test-box">
  10. <view class="test-title">选择考试试卷</view>
  11. <view>
  12. <view class="note-item ddflex" style="font-size: 16px;" v-for="item,index in testList" :key="index+'test'">
  13. <view class="note-item-content" @click="item.resultId?'':toTest(item)">{{index+1}}. {{item.title}}</view>
  14. <view style="color: green;font-size: 14rpx;" v-if="item.resultId" @click="queryScore(item)">成绩查询</view>
  15. </view>
  16. </view>
  17. <!-- <uni-pagination style="margin-top: 30px;" title="标题文字" show-icon="true" total="50" current="2"></uni-pagination> -->
  18. <!-- <navigator class="back-btn ddflex" hover-class="none" open-type="navigateBack">
  19. <image src="/static/images/back3.png"></image>
  20. <text>返回上一页</text>
  21. </navigator> -->
  22. </view>
  23. <!-- 答题卡区域 -->
  24. <view class="test-info ddflex" v-if="false">
  25. <view class="test-people ddflex">
  26. <image :src="examResult.avatar"></image>
  27. <view class="test-people-info ddflex">
  28. <view class="ddflex">
  29. <view class="test-people-label">考生姓名</view>
  30. <view class="fflex">{{examResult.userName}}</view>
  31. </view>
  32. <view class="ddflex">
  33. <view class="test-people-label">准考证号</view>
  34. <view class="fflex" style="word-break: break-all;">{{examResult.admissionNo}}</view>
  35. </view>
  36. <view class="ddflex">
  37. <view class="test-people-label">考试科目</view>
  38. <view class="fflex">{{examResult.title}}</view>
  39. </view>
  40. </view>
  41. </view>
  42. <view class="note-box">
  43. <view class="note-title ddflex">
  44. <image class="note-title-icon" src="/static/images/ksgg.png"></image>
  45. <view>考试公告</view>
  46. </view>
  47. <view>
  48. <view class="note-item ddflex" v-for="item,index in otherInfo" :key="index" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
  49. <view class="note-item-content">{{item.title}}</view>
  50. </view>
  51. </view>
  52. </view>
  53. <!-- 注意事项 -->
  54. <view class="note-box">
  55. <view class="note-title ddflex">
  56. <image class="note-title-icon" src="/static/images/zysx.png"></image>
  57. <view>注意事项</view>
  58. </view>
  59. <view>
  60. <view class="note-item ddflex" v-for="item,index in noteInfo" :key="index+'zy'" @click="jumpUrl('/pages/contentDetail/contentDetail?id='+item.id)">
  61. <view class="note-item-content">{{item.title}}</view>
  62. </view>
  63. </view>
  64. </view>
  65. </view>
  66. </view>
  67. <uni-popup ref="popup" type="bottom" :is-mask-click='false'>
  68. <view class="popup-box">
  69. <view class="popup-top ddflex">
  70. <view>身份验证</view>
  71. <image src="/static/images/close.png" @click="close()"></image>
  72. </view>
  73. <view class="popup-content">您好!考试前需要进行身份验证,请手持准考证,对准摄像头进行拍照,示例如下</view>
  74. <view style="width: 400px;height: 243px;margin:30px auto;" v-show="!isPhotoing&&!imageUrl">
  75. <img style="width: 100%;height: 100%" src="/static/images/sfyz.png"/>
  76. </view>
  77. <view v-show="isPhotoing" style="width: 400px;height: 243px;margin:30px auto;position: relative;" id="video-box">
  78. <video id="video" style="width: 100%;height: 100%;" object-fit='fill' :autoplay="true" :controls="false" :show-center-play-btn="false"></video>
  79. <canvas id="canvas" style="width: 100%;height: 100%;position: absolute;top:100000px;" canvas-id="canvas"></canvas>
  80. <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>
  81. </view>
  82. <view style="width: 400px;height: 243px;margin:30px auto;" v-show="!isPhotoing&&imageUrl">
  83. <img style="width: 100%;height: 100%" :src="imageUrl"/>
  84. </view>
  85. <view class="popup-bottom ddflex">
  86. <view class="popup-btn2" @click="yanzheng" v-show="!isPhotoing">{{imageUrl?'重新验证':'立即验证'}}</view>
  87. <view class="popup-btn2" v-if="imageUrl" @click="yanzhengSubmit">确认</view>
  88. </view>
  89. </view>
  90. </uni-popup>
  91. <Foot></Foot>
  92. </view>
  93. </template>
  94. <script>
  95. const app = getApp();
  96. const req = require('../../utils/request.js');
  97. const utils = require('../../utils/util.js');
  98. const api = require('../../utils/api.js')
  99. export default {
  100. data() {
  101. return {
  102. paperId:null,
  103. admissionNo:null,
  104. paper: '', //试卷详情
  105. examResult:{},//考试详情
  106. otherInfo:[],
  107. noteInfo:[],
  108. testList:[],
  109. userInfo:{},
  110. item:{},
  111. isPhotoing:false,//是否正在验证身份
  112. imageUrl:'',
  113. timeText:3,
  114. timeInterval:null
  115. }
  116. },
  117. onReady() {},
  118. onLoad(options) {
  119. this.paperId = options.paperId;
  120. this.admissionNo = options.admissionNo;
  121. },
  122. onShow() {
  123. this.getTestProject()
  124. this.getUserInfo()
  125. },
  126. methods: {
  127. jumpUrl(url){
  128. uni.navigateTo({
  129. url:url
  130. })
  131. },
  132. // 考试信息
  133. getUserInfo(){
  134. req.getRequest('/api/v3/exam/user/manager/info',{},res=>{
  135. this.userInfo = res
  136. })
  137. },
  138. // 获取考试项目
  139. getTestProject(){
  140. req.getRequest('/api/v3/exam/user/manager/paper',{examId:this.paperId},res=>{
  141. this.testList = res
  142. })
  143. },
  144. // 去考试
  145. toTest(item){
  146. uni.showModal({
  147. title:'提示',
  148. content:`确定开始 ${item.title}?`,
  149. success: (res) => {
  150. if(res.confirm){
  151. // 如果进行身份验证
  152. this.item = item
  153. this.open()
  154. // 直接考试,是否允许考试
  155. // uni.navigateTo({
  156. // // url: '/pages/test/test?paperId='+this.paperId,
  157. // url: '/pages/test/test?paperId='+item.id+'&testId='+this.paperId,
  158. // success:()=> {
  159. // this.close()
  160. // }
  161. // });
  162. }else{
  163. }
  164. }
  165. })
  166. },
  167. open() {
  168. this.$refs.popup.open('center')
  169. },
  170. close() {
  171. this.$refs.popup.close()
  172. clearInterval(this.timeInterval)
  173. this.isPhotoing = false
  174. this.imageUrl = ''
  175. },
  176. // 验证提交
  177. yanzhengSubmit(){
  178. let userPhotoData = {examId:this.paperId,image:this.imageUrl,paperId:this.item.id}
  179. req.postRequest('/api/v3/exam/user/manager/image',userPhotoData,res=>{
  180. uni.navigateTo({
  181. // url: '/pages/test/test?paperId='+this.paperId,
  182. url: '/pages/test/test?paperId='+this.item.id+'&testId='+this.paperId,
  183. success:()=> {
  184. this.close()
  185. }
  186. });
  187. })
  188. },
  189. // 身份验证
  190. yanzheng(){
  191. this.imageUrl = ''
  192. this.test()
  193. },
  194. takePhoto() {
  195. let _this = this
  196. //获得Canvas对象
  197. const query = uni.createSelectorQuery().in(this);
  198. let video = document.querySelector('video');
  199. let canvas = document.querySelector('canvas')
  200. let ctx = canvas.getContext('2d');
  201. let videoInfo = {}
  202. query.select('video').boundingClientRect(result => {
  203. console.log('videoInfo',result)
  204. videoInfo={
  205. width:result.width,
  206. height:result.height
  207. }
  208. }).exec();
  209. ctx.drawImage(video, 0, 0, videoInfo.width, videoInfo.height);
  210. // console.log(this.dataURLtoBlob(canvas.toDataURL(),'11'))
  211. req.uploadFile('/api/upload', canvas.toDataURL(), res => {
  212. // req.msg('图片上传成功');
  213. console.log('图片上传成功',res)
  214. // _this.imageUrl = canvas.toDataURL()
  215. _this.imageUrl = res.src
  216. _this.isPhotoing = false
  217. },false);
  218. },
  219. test(){
  220. // var video = document.querySelector('video');
  221. const query = uni.createSelectorQuery().in(this);
  222. var video = query.select('#video')
  223. console.log('video',video)
  224. // 兼容代码
  225. window.URL = (window.URL || window.webkitURL || window.mozURL || window.msURL);
  226. // 获取媒体属性,旧版本浏览器可能不支持mediaDevices,我们首先设置一个空对象
  227. if (navigator.mediaDevices === undefined) {
  228. navigator.mediaDevices = {};
  229. }
  230. console.log('navigator.mediaDevices',navigator.mediaDevices)
  231. // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
  232. // 使用getUserMedia,因为它会覆盖现有的属性。
  233. // 这里,如果缺少getUserMedia属性,就添加它。
  234. if (navigator.mediaDevices.getUserMedia === undefined) {
  235. navigator.mediaDevices.getUserMedia = function(constraints) {
  236. // 首先获取现存的getUserMedia(如果存在)
  237. var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
  238. // 有些浏览器不支持,会返回错误信息
  239. // 保持接口一致
  240. if (!getUserMedia) { //不存在则报错
  241. return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
  242. }
  243. // 否则,使用Promise将调用包装到旧的navigator.getUserMedia
  244. return new Promise(function(resolve, reject) {
  245. getUserMedia.call(navigator, constraints, resolve, reject);
  246. });
  247. }
  248. }
  249. //摄像头调用配置
  250. var mediaOpts = {
  251. audio: false,
  252. video: { facingMode: "user"}
  253. }
  254. let that=this;
  255. navigator.mediaDevices.getUserMedia(mediaOpts).then(function(stream) {
  256. that.mediaStreamTrack = stream;
  257. video = document.querySelector('video');
  258. console.log('video = document.querySelector(video);',video)
  259. // 旧的浏览器可能没有srcObject
  260. if ("srcObject" in video) {
  261. video.srcObject = stream
  262. } else {
  263. // 避免在新的浏览器中使用它,因为它正在被弃用。
  264. video.src = window.URL && window.URL.createObjectURL(stream) || stream
  265. }
  266. video.play();
  267. that.isPhotoing = true
  268. that.timeText = 3
  269. that.timeInterval = setInterval(()=>{
  270. if(that.timeText>0){
  271. that.timeText--
  272. }else{
  273. clearInterval(that.timeInterval)
  274. that.timeInterval= null
  275. that.takePhoto()
  276. }
  277. },1000)
  278. }).catch(function (err) {
  279. console.log(err)
  280. uni.showModal({
  281. title:'提示',
  282. content:'未找到摄像头',
  283. showCancel:false,
  284. success() {
  285. // uni.navigateBack({
  286. // })
  287. }
  288. })
  289. });
  290. },
  291. // 成绩查询
  292. queryScore(item){
  293. var formP = {
  294. admission: this.userInfo.admissionNo,
  295. paperId: item.id
  296. };
  297. console.log(this.userInfo)
  298. req.getRequest('/api/v3/exam/user/manager/result', formP, res => {
  299. uni.navigateTo({
  300. url:'/pages/scoreQuery/scoreQuery?paperId='+ item.id+'&admissionNo='+this.userInfo.admissionNo
  301. })
  302. });
  303. },
  304. }
  305. }
  306. </script>
  307. <style>
  308. @import url('./selectTest.css');
  309. </style>