test.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. <template>
  2. <page-meta :root-font-size="fontsize+'px'" style="display: block;">
  3. <view style="position: relative;">
  4. <Headers></Headers>
  5. <view class="project">
  6. <view style="width: 12rem;margin: auto;">{{paper.title}}</view>
  7. </view>
  8. <view class="video-box" id="video-box" v-show="showVideo">
  9. <video id="video" style="width: 100%;height: 100%;" :autoplay="true" :controls="false" :show-center-play-btn="false"></video>
  10. <canvas id="canvas" style="width: 100%;height: 100%;position: absolute;top:100000px;" canvas-id="canvas"></canvas>
  11. </view>
  12. <!-- <img :src="imgUrl" ></img> -->
  13. <view class="project-content ddflex">
  14. <!-- 考试区域 -->
  15. <view class="test-box">
  16. <view class="test-box-title">
  17. <text style="font-weight: bold;margin-right: 0.10rem;">考试题目</text>
  18. <text>{{ cIndex + 1 }}</text><text style="color: #999999;">/{{questTotal}}</text>
  19. </view>
  20. <view class="test-question-box">
  21. <view class="test-question ddflex">
  22. <view>{{ cIndex + 1 }}</view>
  23. <view>【{{getQuestType()}}】</view>
  24. <view>
  25. <rich-text class="hh" :nodes="addTitleType(quest.title,quest.qtype)"></rich-text>
  26. </view>
  27. </view>
  28. <!-- 简答题 -->
  29. <view class="short" v-if="quest.qtype == 4 || quest.qtype == 5">
  30. <textarea
  31. v-model="quest.answerTxt"
  32. maxlength="-1"
  33. ref="inputText"
  34. placeholder="在此输入答案"
  35. placeholder-class="placeholder"
  36. class="text-ans"
  37. @click="intAnswer(qitem, idx)"
  38. ></textarea>
  39. </view>
  40. <view class="test-question-answer" v-else-if="quest.qtype == 1 || quest.qtype == 2 || quest.qtype == 3">
  41. <view class="answer-item ddflex" v-for="(qitem, idx) in quest.questJson" :key="idx" @click="setAnswer(qitem, idx)">
  42. <!-- answer-checked -->
  43. <view :class="qitem.cssStr ? 'answer-checked' : 'answer-unchecked'"></view>
  44. <view class="answer-text ddflex">
  45. <view style="margin-right: 0.1rem;">{{ qitem.num }} </view>
  46. <rich-text class="hh" :nodes="qitem.title"></rich-text>
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. <view style="height: 0.82rem;width: 100%;"> </view>
  52. <view class="test-btn-group ddflex">
  53. <view v-if="cIndex != 0" class="test-btn" @click="prev()">上一题</view>
  54. <view v-if="cIndex + 1 != questTotal" class="test-btn" @click="next()">下一题</view>
  55. </view>
  56. </view>
  57. <!-- 答题卡区域 -->
  58. <view class="test-info ddflex">
  59. <view class="test-people ddflex">
  60. <image :src="userInfo.avatar"></image>
  61. <view class="test-people-info ddflex">
  62. <view class="ddflex">
  63. <view class="test-people-label">考生姓名</view>
  64. <view class="fflex">{{userInfo.userName}}</view>
  65. </view>
  66. <view class="ddflex">
  67. <view class="test-people-label">身份证号</view>
  68. <view class="fflex" style="word-break: break-all;">{{userInfo.idNumber}}</view>
  69. </view>
  70. <view class="ddflex">
  71. <view class="test-people-label">考试科目</view>
  72. <view class="fflex">{{paper.title}}</view>
  73. </view>
  74. </view>
  75. </view>
  76. <view class="test-date">
  77. <view class="test-date-title">距考试结束还有</view>
  78. <view class="test-date-group ddflex">
  79. <view class="test-date-item">{{times.hour}}</view>
  80. <text class="test-date-symbol">:</text>
  81. <view class="test-date-item">{{times.minutes}}</view>
  82. <text class="test-date-symbol">:</text>
  83. <view class="test-date-item">{{times.seconds}}</view>
  84. </view>
  85. </view>
  86. <view class="test-answer fflex">
  87. <view class="test-answer-title">答题卡</view>
  88. <view class="test-answer-info ddflex">
  89. <view class="">共 {{quests.length}} 题</view>
  90. <view class="test-answer-info-symbol"></view>
  91. <view class="">总计 100 分</view>
  92. </view>
  93. <view class="test-answer-box ddflex">
  94. <view :class="'test-answer-item '+(cIndex == index?'test-answer-item-curActive':item.isSubmit?'test-answer-item-active':'')" v-for="item,index in quests"
  95. @click="toQuestTip(index)">
  96. {{index+1}}
  97. </view>
  98. </view>
  99. <view class="test-answer-number ddflex">
  100. <view class="ddflex">
  101. <view class="test-isanswer"></view>
  102. <view>已做 {{quests.filter(item=>{return item.isSubmit}).length}}</view>
  103. </view>
  104. <view class="ddflex">
  105. <view class="test-unanswer"></view>
  106. <view>未做 {{quests.filter(item=>{return !item.isSubmit}).length}}</view>
  107. </view>
  108. </view>
  109. <view style="height: 0.82rem;"></view>
  110. <view class="submit-btn" @click="open">交 卷</view>
  111. </view>
  112. </view>
  113. </view>
  114. <uni-popup ref="popup" type="bottom" :is-mask-click='false'>
  115. <view class="popup-box" v-if="state==1">
  116. <view class="popup-top ddflex">
  117. <view>您正在结束作答</view>
  118. <image src="/static/images/close.png" v-if="time > 0" @click="close()"></image>
  119. </view>
  120. <!-- {{(quests.filter(item=>{return item.isSubmit}).length<quests.length)?'您还有未完成的考题,是否确定交卷?':'您已完成全部考题,是否确定交卷?'}} -->
  121. <view class="popup-content">{{(quests.filter(item=>{return item.isSubmit}).length < quests.length ? '您还有未完成的考题,是否确定交卷?':'您已完成全部考题,是否确定交卷?')}}</view>
  122. <view class="popup-bottom ddflex">
  123. <view class="popup-btn1" @click="close()" v-if="time > 0">继续考试</view>
  124. <view class="popup-btn2" @click="submit">确定交卷</view>
  125. </view>
  126. </view>
  127. <view class="popup-box" v-if="state==2">
  128. <view class="popup-top ddflex">
  129. <view>考试结果</view>
  130. <image src="/static/images/close.png" @click="close(true)"></image>
  131. </view>
  132. <view class="popup-content" style="margin-top: 0.23rem;">
  133. <view class="chufen-title">{{result.title}}</view>
  134. <view class="chufen-number" :style="result.score>=paper.passScore?'color:green;':''">
  135. {{result.score}}<text style="font-size: 0.18rem;margin-left: 0.1rem;">分</text>
  136. <view style="font-size: 0.16rem;">{{result.score>=paper.passScore?'考试合格':'考试不合格'}}</view>
  137. </view>
  138. </view>
  139. <view class="popup-bottom ddflex" style="margin-top: 0.3rem;margin-bottom: 0.6rem;padding-left: 0.3rem">
  140. <view>
  141. <view class="popup-bottom-label">总题数</view>
  142. <view class="popup-bottom-value">{{result.questToal}}</view>
  143. </view>
  144. <view class="line"></view>
  145. <view>
  146. <view class="popup-bottom-label">考试用时</view>
  147. <view class="popup-bottom-value">
  148. {{timeChangeHMS(result.duration)}}
  149. </view>
  150. </view>
  151. </view>
  152. </view>
  153. </uni-popup>
  154. <Foot></Foot>
  155. </view>
  156. </page-meta>
  157. </template>
  158. <script>
  159. const app = getApp();
  160. const req = require('../../utils/request.js');
  161. const utils = require('../../utils/util.js');
  162. const api = require('../../utils/api.js')
  163. import html2canvas from "../../utils/html2canvas.min.js";
  164. export default {
  165. data() {
  166. return {
  167. state:1,
  168. userInfo:{},
  169. testId:null,//考试项目ID
  170. paperId: '', //考试题目
  171. paperTitle: '', //考试标题
  172. paperType: '', //1每日一练 2模拟试题 3大厂真题 5企业真题
  173. type: '', //标识类型 1试卷 2章节 3热门考点 4错题集 5高频错题
  174. systems: {},
  175. isMockExam: true, //是否是模拟考试
  176. isShowTip: false, //是否展示温馨提示
  177. isShowSheet: false, //
  178. paper: '', //试卷详情
  179. time: 0, //允许做题的时间
  180. times: 0, //做题时分秒倒计时
  181. quest: '', //当前选中的题目
  182. quests: [], //题目数据
  183. questTotal: 0, //题目条数
  184. cIndex: 0, //当前做题下标
  185. answerTxt: '', //填空题、简答题答案
  186. isGoBack: false,
  187. isShowView: false,
  188. result:null,//考试结果
  189. buffer:null,
  190. video:{src:null},
  191. canvas:null,
  192. ctx:null,
  193. imgUrl:null,
  194. showVideo:false,
  195. takePhotoTime:10*60,//抓拍时间间隔,单位s
  196. fontsize:'100px'
  197. }
  198. },
  199. onReady() {},
  200. async onLoad(options) {
  201. let srceenNunber = 19.2; //因设计图是1920所有把设计图分为19.2份,所以html字体大小为100px
  202. let that = this;
  203. //窗体改变大小触发事件
  204. uni.onWindowResize((res) => {
  205. console.log('变化后的窗口宽度=', res.size.windowWidth);
  206. that.fontsize = parseFloat(res.size.windowWidth) / srceenNunber;
  207. })
  208. //打开获取屏幕大小
  209. uni.getSystemInfo({
  210. success(res) {
  211. console.log('设备信息:', res);
  212. that.fontsize = res.screenWidth / srceenNunber;
  213. console.log('字体大小:', that.fontsize);
  214. }
  215. })
  216. this.paperId = options.paperId;
  217. this.testId = options.testId
  218. if(this.IsPC()){
  219. // 摄像头
  220. const query = uni.createSelectorQuery().in(this);
  221. this.video = query.select('#video')
  222. this.canvas = query.select('#canvas');
  223. await this.test()
  224. }
  225. console.log(this.userInfo)
  226. // 需要先判断用户是否还能考试
  227. await this.getQuests();
  228. // 考生信息
  229. this.getUserInfo()
  230. // 考试数据
  231. this.getPaper();
  232. // this.getQuests();
  233. },
  234. onUnload() {
  235. // uni.clearStorageSync();
  236. clearTimeout(this.ptime);
  237. },
  238. methods: {
  239. IsPC() {
  240. var userAgentInfo = navigator.userAgent;
  241. var Agents = ["Android", "iPhone",
  242. "SymbianOS", "Windows Phone",
  243. "iPad", "iPod"];
  244. var flag = true;
  245. for (var v = 0; v < Agents.length; v++) {
  246. if (userAgentInfo.indexOf(Agents[v]) > 0) {
  247. flag = false;
  248. break;
  249. }
  250. }
  251. return flag;
  252. },
  253. // 考试信息
  254. getUserInfo(){
  255. req.getRequest('/api/v3/exam/user/manager/info',{},res=>{
  256. this.userInfo = res
  257. })
  258. },
  259. getQuestType() {
  260. // <!-- 1单选 2多选 3 判断 4 填空 5 简答', -->
  261. var text = '';
  262. switch (this.quest.qtype) {
  263. case 1:
  264. text = '单选题';
  265. break;
  266. case 2:
  267. text = '多选题';
  268. break;
  269. case 3:
  270. text = '判断题';
  271. break;
  272. case 4:
  273. text = '填空题';
  274. break;
  275. case 5:
  276. text = '简答题';
  277. break;
  278. default:
  279. break;
  280. }
  281. return text;
  282. },
  283. stylesText(text){
  284. return utils.preText(text);
  285. },
  286. addTitleType(title, qtype) {
  287. var stitle=this.stylesText(title);
  288. return stitle;
  289. },
  290. //获取考试对象
  291. getPaper() {
  292. var _ts = this;
  293. req.getRequest(api.paper_detail + _ts.paperId, {}, res => {
  294. if (res) {
  295. if (res.time > 0 && _ts.paperType != 3 && _ts.paperType != 5) {
  296. _ts.time = res.time * 60;
  297. _ts.setTime();
  298. }
  299. _ts.paperType = res.paperType * 1;
  300. _ts.paper = res;
  301. }
  302. });
  303. },
  304. getQuests(){
  305. let _ts = this
  306. return new Promise((r,j)=>{
  307. req.getRequest(api.get_exam_question + _ts.paperId,{},quests=>{
  308. console.log(quests)
  309. if (quests && quests.length > 0) {
  310. _ts.questTotal = quests.length;
  311. for (var k = 0; k < quests.length; k++) {
  312. var quest = quests[k];
  313. quest.eitype = _ts.etype;
  314. quest.title = quest.title.replace(/<code class="(.*?)"/gi, '<code class="Java" ');
  315. }
  316. if (_ts.questTotal > 0) {
  317. _ts.quest = _ts.setQuest(quests[0]);
  318. }
  319. _ts.quests = quests;
  320. r()
  321. }else{
  322. uni.showModal({
  323. title:'提示',
  324. content:'您已参加过该考试',
  325. showCancel:false,
  326. success:(res)=> {
  327. if(res.confirm){
  328. uni.redirectTo({
  329. url:'/pages/selectTest/selectTest?paperId='+this.testId,
  330. success() {
  331. console.log('您已参加过该考试')
  332. }
  333. })
  334. }
  335. }
  336. })
  337. }
  338. })
  339. })
  340. },
  341. //考试时间倒计时
  342. setTime() {
  343. var _ts = this;
  344. var time = _ts.time;
  345. if (time <= 0) {
  346. //考试时间到,交卷
  347. if (_ts.ptime) {
  348. clearTimeout(_ts.ptime);
  349. }
  350. _ts.open()
  351. // _ts.submitTest();
  352. return;
  353. }
  354. var times = {};
  355. times.hour = _ts.cut22(Math.floor(time / 3600));
  356. var leave2 = time % 3600;
  357. times.minutes = _ts.cut22(Math.floor(leave2 / 60));
  358. var leave3 = leave2 % 60;
  359. times.seconds = _ts.cut22(Math.round(leave3));
  360. _ts.times = times;
  361. _ts.ptime = setTimeout(function() {
  362. _ts.time = time - 1;
  363. if(((_ts.paper.time*60)-(_ts.time))%(_ts.takePhotoTime)==0){
  364. if(_ts.IsPC()){
  365. _ts.takePhoto()
  366. }
  367. }
  368. _ts.setTime();
  369. }, 1000);
  370. // if(((_ts.paper.time*60) -(_ts.time*60)%(10))%(10)==0){
  371. // let a = (_ts.paper.time*60) -(_ts.time*60)%(10)
  372. // console.log((_ts.paper.time*60) -(_ts.time*60),'拍照')
  373. // }
  374. }, //下次继续
  375. // 秒转换时分秒
  376. timeChangeHMS(time){
  377. let _ts = this
  378. var times = {};
  379. times.hour = _ts.cut22(Math.floor(time / 3600));
  380. var leave2 = time % 3600;
  381. times.minutes = _ts.cut22(Math.floor(leave2 / 60));
  382. var leave3 = leave2 % 60;
  383. times.seconds = _ts.cut22(Math.round(leave3));
  384. return times.hour+":"+times.minutes+":"+times.seconds
  385. },
  386. cut22(n) {
  387. n = n.toString();
  388. return n[1] ? n : '0' + n;
  389. },
  390. //将答案和问题转成数组或对象
  391. setQuest(quest) {
  392. console.log('quest.iscv==', quest.iscv);
  393. if (quest.iscv) {
  394. return quest;
  395. }
  396. quest.iscv = true;
  397. var answers = [];
  398. var qjs = [];
  399. try {
  400. var qJsons = JSON.parse(quest.questJson);
  401. if (quest.qtype == 1 || quest.qtype == 3) {
  402. //'单选题'
  403. answers = quest.answerJson ? [quest.answerJson] : [];
  404. } else if (quest.qtype == 2) {
  405. if (quest.answerJson) {
  406. answers = JSON.parse(quest.answerJson);
  407. }
  408. }
  409. for (var m = 0; m < qJsons.length; m++) {
  410. var num = this.getNum(m);
  411. qjs.push({
  412. num: num,
  413. title: qJsons[m],
  414. cssStr: ''
  415. });
  416. }
  417. } catch (e) {}
  418. quest.answerJson = answers;
  419. quest.questJson = qjs;
  420. console.log('下一题中');
  421. quest.answerTxt = '';
  422. this.answerTxt = '';
  423. return quest;
  424. },
  425. getNum: function(ix) {
  426. var keys = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N'];
  427. return keys[ix];
  428. },
  429. intAnswer(item, idx) {
  430. var _ts = this;
  431. var qes = this.quest;
  432. qes.isSubmit = true;
  433. _ts.quest = qes;
  434. },
  435. setAnswer(item, idx) {
  436. // <!-- 1单选 2多选 3 判断 4 填空 5 简答', -->
  437. var _ts = this;
  438. idx = idx + 1;
  439. var qes = this.quest;
  440. /*修改答案选中项*/
  441. var sItems = qes.sItems;
  442. if (qes.qtype == 2) {
  443. var isflg = false;
  444. if (sItems && sItems.length > 0) {
  445. var cidx = sItems.indexOf(idx);
  446. if (cidx > -1) {
  447. sItems.splice(cidx, 1);
  448. isflg = true;
  449. }
  450. } else {
  451. sItems = [];
  452. }
  453. if (!isflg) {
  454. sItems.push(idx);
  455. }
  456. } else if (qes.qtype == 1 || qes.qtype == 3) {
  457. sItems = [idx];
  458. }
  459. qes.sItems = sItems.sort();
  460. qes.isSubmit = true;
  461. //修改Css
  462. qes = _ts.setCss(qes);
  463. _ts.quest = qes;
  464. _ts.quests[_ts.cIndex] = qes;
  465. },
  466. //设置页面选中样式
  467. setCss(qes) {
  468. var qjson = qes.questJson;
  469. var answers = qes.answerJson; //答案
  470. var sItems = qes.sItems;
  471. if (!sItems) {
  472. sItems = [];
  473. }
  474. for (var m = 0; m < qjson.length; m++) {
  475. var qitem = qjson[m];
  476. var cidx = sItems.indexOf(m + 1);
  477. if (cidx > -1) {
  478. qitem.cssStr = ' active';
  479. } else {
  480. qitem.cssStr = '';
  481. }
  482. }
  483. qes.questJson = qjson;
  484. qes.cssStr = ' active';
  485. return qes;
  486. },
  487. //上一题
  488. prev() {
  489. var _ts = this;
  490. var cIndex = _ts.cIndex;
  491. if (cIndex <= 0) {
  492. return;
  493. }
  494. _ts.cIndex = cIndex - 1;
  495. _ts.quest = _ts.setQuest(_ts.quests[_ts.cIndex]);
  496. }, //下一题
  497. next() {
  498. var _ts = this;
  499. var cIndex = _ts.cIndex;
  500. if (cIndex >= _ts.questTotal - 1) {
  501. return;
  502. }
  503. _ts.cIndex = cIndex + 1;
  504. console.log('下一题');
  505. _ts.quest = _ts.setQuest(_ts.quests[_ts.cIndex]);
  506. console.log('下一题后');
  507. console.log('_ts.quest==', _ts.quest);
  508. console.log('_ts.quest.answerTxt==', _ts.quest.answerTxt);
  509. _ts.$forceUpdate();
  510. console.log('quests==', _ts.quests);
  511. },
  512. toQuestTip(index) {
  513. var _ts = this;
  514. _ts.cIndex = index;
  515. _ts.quest = _ts.setQuest(_ts.quests[_ts.cIndex]);
  516. // _ts.closeCeng();
  517. // });
  518. },
  519. //提交
  520. submitQuest() {
  521. var msg = '';
  522. if (this.paperType == 2) {
  523. if (this.times.hour > 0) {
  524. msg = '距离考试结束还剩' + this.times.hour + '小时' + this.times.minutes + '分钟,确定要结束考试吗?';
  525. } else {
  526. if (this.times.minutes > 0) {
  527. msg = '距离考试结束还剩' + this.times.minutes + '分钟,确定要结束考试吗?';
  528. } else {
  529. msg = '确定要交卷结束考试吗?';
  530. }
  531. }
  532. } else {
  533. msg = '确定要交卷结束做题吗?';
  534. }
  535. this.$showModal({ title: '温馨提示', content: msg, cancelText: '继续作答', confirmText: '交卷' })
  536. .then(res => {
  537. this.submitTest();
  538. })
  539. .catch(err => {
  540. // 点击取消按钮的操作
  541. });
  542. },
  543. async submitTest(){
  544. if (!this.paper.isScore) {//手动阅卷
  545. //订阅成绩通知
  546. await requsetmessage.resultsTest().then(res => {});
  547. }
  548. this.submit();
  549. },
  550. //提交考试+答案
  551. submit() {
  552. var _ts = this;
  553. if (_ts.ptime) {
  554. clearTimeout(_ts.ptime);
  555. }
  556. var dataP = {
  557. uniqueId: _ts.paperId,
  558. uniqueType: 1
  559. };
  560. //组装考试题目
  561. dataP.questionList = this.quests.map(it => {
  562. // <!-- 1单选 2多选 3 判断 4 填空 5 简答', -->
  563. var data = {
  564. id: it.id, //记录ID
  565. paperQuestionId: it.pqId, //试卷题目ID
  566. questionId: it.questId //题目ID
  567. };
  568. var answerJson = '';
  569. if (it.qtype == 1 || it.qtype == 3) {
  570. answerJson = it.sItems ? it.sItems.join('') : '';
  571. } else if (it.qtype == 2) {
  572. answerJson = it.sItems ? JSON.stringify(it.sItems) : '';
  573. } else if (it.qtype == 4) {
  574. var list = [];
  575. list.push(it.answerTxt ? it.answerTxt : '');
  576. answerJson = JSON.stringify(list);
  577. } else if (it.qtype == 5) {
  578. answerJson = it.answerTxt ? it.answerTxt : '';
  579. }
  580. data.answerJson = answerJson;
  581. return data;
  582. });
  583. console.log(dataP);
  584. req.postRequest(
  585. api.complete_exam,
  586. dataP,
  587. res => {
  588. if (!_ts.paper.isScore) {//手动阅卷
  589. uni.redirectTo({
  590. url:'/pages/scoreQuery/scoreQuery?paperId='+ _ts.paperId+'&admissionNo='+_ts.userInfo.admissionNo
  591. })
  592. return
  593. } else {//自动阅卷
  594. // uni.redirectTo({
  595. // url: '/learn/result/index?id=' + res.id
  596. // });
  597. this.state=2
  598. this.result = res
  599. }
  600. },
  601. true
  602. );
  603. },
  604. open() {
  605. this.$refs.popup.open('center')
  606. },
  607. close(back) {
  608. let _ts = this
  609. this.$refs.popup.close()
  610. if(back){
  611. uni.redirectTo({
  612. url:'/pages/selectTest/selectTest?paperId='+this.testId,
  613. })
  614. }
  615. },
  616. takePhoto() {
  617. let _this = this
  618. //获得Canvas对象
  619. const query = uni.createSelectorQuery().in(this);
  620. let video = document.querySelector('video');
  621. let canvas = document.querySelector('canvas')
  622. let ctx = canvas.getContext('2d');
  623. let videoInfo = {}
  624. query.select('video').boundingClientRect(result => {
  625. console.log('videoInfo',result)
  626. videoInfo={
  627. width:result.width,
  628. height:result.height
  629. }
  630. }).exec();
  631. ctx.drawImage(video, 0, 0, videoInfo.width, videoInfo.height);
  632. // console.log(this.dataURLtoBlob(canvas.toDataURL(),'11'))
  633. req.uploadFile('/api/upload', canvas.toDataURL(), res => {
  634. let userPhotoData = {examId:this.testId,image:res.src,paperId:this.paperId}
  635. req.postRequest('/api/v3/exam/user/manager/image',userPhotoData,res=>{
  636. })
  637. },false);
  638. },
  639. dataURLtoBlob(dataurl) {
  640. var arr = dataurl.split(','),
  641. mime = arr[0].match(/:(.*?);/)[1],
  642. bstr = atob(arr[1]),
  643. n = bstr.length,
  644. u8arr = new Uint8Array(n);//8位无符号整数,长度1个字节
  645. console.log(mime)
  646. while (n--) {
  647. u8arr[n] = bstr.charCodeAt(n);
  648. }
  649. // console.log(JSON.stringify(u8arr));
  650. return new Blob([u8arr], {
  651. type: mime
  652. });
  653. },
  654. base64toFile (dataurl, filename){
  655. var arr = dataurl.split(','),
  656. mime = arr[0].match(/:(.*?);/)[1],
  657. bstr = atob(arr[1]),
  658. n = bstr.length,
  659. u8arr = new Uint8Array(n);
  660. while (n--) {
  661. u8arr[n] = bstr.charCodeAt(n);
  662. }
  663. return new File([u8arr], filename, {
  664. type: mime
  665. });
  666. },
  667. test(){
  668. return new Promise((r,j)=>{
  669. // var video = document.querySelector('video');
  670. const query = uni.createSelectorQuery().in(this);
  671. var video = query.select('#video')
  672. console.log('video',video)
  673. // 兼容代码
  674. window.URL = (window.URL || window.webkitURL || window.mozURL || window.msURL);
  675. // 获取媒体属性,旧版本浏览器可能不支持mediaDevices,我们首先设置一个空对象
  676. if (navigator.mediaDevices === undefined) {
  677. navigator.mediaDevices = {};
  678. }
  679. console.log('navigator.mediaDevices',navigator.mediaDevices)
  680. // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
  681. // 使用getUserMedia,因为它会覆盖现有的属性。
  682. // 这里,如果缺少getUserMedia属性,就添加它。
  683. if (navigator.mediaDevices.getUserMedia === undefined) {
  684. navigator.mediaDevices.getUserMedia = function(constraints) {
  685. // 首先获取现存的getUserMedia(如果存在)
  686. var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
  687. // 有些浏览器不支持,会返回错误信息
  688. // 保持接口一致
  689. if (!getUserMedia) { //不存在则报错
  690. return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
  691. }
  692. // 否则,使用Promise将调用包装到旧的navigator.getUserMedia
  693. return new Promise(function(resolve, reject) {
  694. getUserMedia.call(navigator, constraints, resolve, reject);
  695. });
  696. }
  697. }
  698. //摄像头调用配置
  699. var mediaOpts = {
  700. audio: false,
  701. video: { facingMode: "user"}
  702. }
  703. let that=this;
  704. navigator.mediaDevices.getUserMedia(mediaOpts).then(function(stream) {
  705. that.mediaStreamTrack = stream;
  706. video = document.querySelector('video');
  707. console.log('video = document.querySelector(video);',video)
  708. // 旧的浏览器可能没有srcObject
  709. if ("srcObject" in video) {
  710. video.srcObject = stream
  711. that.video.srcObject = stream
  712. } else {
  713. // 避免在新的浏览器中使用它,因为它正在被弃用。
  714. video.src = window.URL && window.URL.createObjectURL(stream) || stream
  715. that.video.src = window.URL && window.URL.createObjectURL(stream) || stream
  716. }
  717. video.play();
  718. that.showVideo = true
  719. r()
  720. }).catch(function (err) {
  721. console.log(err)
  722. uni.showModal({
  723. title:'提示',
  724. content:'未找到摄像头',
  725. showCancel:false,
  726. success() {
  727. uni.navigateBack({
  728. })
  729. }
  730. })
  731. });
  732. })
  733. },
  734. },
  735. }
  736. </script>
  737. <style>
  738. @import url('./test.css');
  739. </style>