selectTest.vue 12 KB

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