selectTest.vue 12 KB

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