exam.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <template>
  2. <view class="phone-kaoshi-page">
  3. <!-- 导航区域 -->
  4. <view class="icon-title-navBar-box">
  5. <view @click="handleBack" class="nav-bar-icon"></view>
  6. <text class="nav-bar-title">{{data.ksName}}</text>
  7. </view>
  8. <!-- 第一行 -->
  9. <view class="kaoshi-page-title">
  10. <!-- 倒计时 -->
  11. <view v-if="!!data.endSecond">
  12. <text>考试倒计时:</text>
  13. <uni-countdown :show-day="true" :second="1000" @timeup="onTimeUp" :start="startCountDown"></uni-countdown>
  14. </view>
  15. <view v-if="activeSt" class="title-types">{{dlName}}</view>
  16. <view>100分钟</view>
  17. </view>
  18. <view class="kaoshi-shiti-content">
  19. <!-- 内容区域 -->
  20. <!-- 试题区域 -->
  21. <view v-if="activeSt">
  22. <template v-if="activeSt.stTypeId == 1">
  23. <!-- 单选 -->
  24. <danxuan :question="activeSt" :key="activeSt.stId"></danxuan>
  25. </template>
  26. <template v-if="activeSt.stTypeId == 2" >
  27. <!-- 多选 -->
  28. <duoxuan :question="activeSt" :key="activeSt.stId"></duoxuan>
  29. </template>
  30. <template v-if="activeSt.stTypeId == 3">
  31. <!-- 判断 -->
  32. <panduan :question="activeSt" :key="activeSt.stId"></panduan>
  33. </template>
  34. <template v-if="activeSt.stTypeId == 4">
  35. <!-- 填空 -->
  36. <tiankong :question="activeSt" :key="activeSt.stId"></tiankong>
  37. </template>
  38. </view>
  39. </view>
  40. <view class="kaoshi-bottom-box">
  41. <button class="phone-green-btn bj-btn" hover-class="none" type="default" size="mini" @click="handleBiaoji">标记</button>
  42. <view @click="showAnswerCard" class="shiti-num-box">
  43. <icon class="shiti-num-icon"></icon>
  44. <text class="active-num">{{activeSt ? activeSt.onlyNum: 0}}</text>/<text>{{data.StListForSearch.length}}</text>
  45. </view>
  46. </view>
  47. <template v-if="activeSt">
  48. <button type="default" size="mini" hover-class="none" class="phone-green-btn ks-btn-prev" @click="handlePrev" v-if="!isFistStId">上一题</button>
  49. <button type="default" size="mini" hover-class="none"class="phone-green-btn ks-btn-next" @click="handleNext" v-if="!isLastStId">下一题</button>
  50. <button type="default" size="mini" hover-class="none"class="phone-green-btn ks-btn-next" @click="handleBack" v-if="isLastStId">交卷</button>
  51. </template>
  52. <!-- 答题卡 -->
  53. <uni-popup ref="popupRef" background-color="#fff" :is-mask-click="false" :mask-click="false">
  54. <view class="answer-card-popup">
  55. <view class="icon-title-bjcolor-navBar-box">
  56. <view @click="handlePopupBack" class="nav-bar-icon"> </view>
  57. <text class="nav-bar-title">答题卡</text>
  58. </view>
  59. <view class="answer-card-content" v-for="(paragraph,paragraphIndex) in questionData" :key="paragraphIndex">
  60. <view class="paragraph-title">
  61. {{paragraph.name}}
  62. </view>
  63. <view class="paragraph-qa" v-for="(qa,qaIndex) in paragraph.qas" :key="qaIndex"
  64. :class="getQaClass(qa)" @click="answerCardItemClick(qa)">{{qa.onlyNum}}
  65. </view>
  66. </view>
  67. </view>
  68. </uni-popup>
  69. <zhuapaiConfirm ref="zhuapaiConfirmRef"
  70. @success="zpConfirmSuccess"
  71. @error="zpConfirmError"
  72. @cancel="zpConfirmCancel"
  73. ></zhuapaiConfirm>
  74. <!-- 抓拍 -->
  75. <zhuapaiVue ref="zhuapaiRef"
  76. @error="zpError"
  77. ></zhuapaiVue>
  78. <!-- 切屏 -->
  79. <qiepingVue ref="qiepingRef"></qiepingVue>
  80. </view>
  81. </template>
  82. <script setup>
  83. import {
  84. ref,
  85. reactive,
  86. computed,
  87. watch,
  88. nextTick
  89. } from "vue";
  90. import zhuapaiVue from "@/components/zhuapaiConfirm/zhuapai.vue";
  91. import qiepingVue from "@/components/zhuapaiConfirm/qieping.vue";
  92. import zhuapaiConfirm from "@/components/zhuapaiConfirm/index.vue"
  93. import {
  94. onLoad
  95. } from "@dcloudio/uni-app";
  96. import * as ksApi from "@/api/kaoshi.js"
  97. import danxuan from "@/components/questions/danxuan.vue";
  98. import duoxuan from "@/components/questions/duoxuan.vue";
  99. import tiankong from "@/components/questions/tiankong.vue";
  100. import panduan from "@/components/questions/panduan.vue";
  101. import {useQuestionTools} from "@/components/questions/useQuestionTools.js";
  102. const {
  103. checkDanxuanReply,
  104. checkDuoxuanReply,
  105. checkPanduanReply,
  106. checkTiankongReply,
  107. getLetterByIndex
  108. } = useQuestionTools();
  109. onLoad((option) => {
  110. data.ksId = option.ksId;
  111. data.zhuapai = option.zhuapai
  112. if (data.zhuapai) {
  113. // 考试前确认摄像头
  114. nextTick(() => {
  115. initBeforKaoshi();
  116. })
  117. } else {
  118. initKaoshi();
  119. }
  120. })
  121. const popupRef = ref(null)
  122. const zhuapaiRef = ref(null)
  123. const qiepingRef = ref(null)
  124. const zhuapaiConfirmRef = ref(null)
  125. const startCountDown = ref(false);
  126. const data = reactive({
  127. ksId: null,
  128. ksName: '',
  129. stTotal: 0,
  130. stScore: 0,
  131. biaoji: null,
  132. endSecond: 0,
  133. pageSize: 0,
  134. toggleScreenFlag: 0,
  135. toggleScreenSecond: 0,
  136. zhuapai: 0,
  137. duanluo: [],
  138. markDB: [],
  139. StListForSearch: [],
  140. })
  141. const questionData = ref([]);
  142. const progress = reactive({
  143. dlIndex: 0,
  144. dtIndex: 0
  145. })
  146. const dlName = computed(() => {
  147. if (data.StListForSearch && activeSt.value) {
  148. return data.StListForSearch[activeSt.value.onlyNum].paragraphName
  149. } else {
  150. return ''
  151. }
  152. })
  153. watch(() => data.duanluo, (newVal) => {
  154. // 计算已答试题数量
  155. }, {
  156. deep: true
  157. })
  158. const activeSt = computed(() => {
  159. if (questionData.value.length) {
  160. return questionData.value.length && questionData.value[progress.dlIndex].qas[progress.dtIndex];
  161. } else {
  162. return null
  163. }
  164. })
  165. const isFistStId = computed(() => {
  166. if (data.StListForSearch.length) {
  167. return data.StListForSearch[0].stId == activeSt.value.stId
  168. } else {
  169. return false
  170. }
  171. });
  172. const isLastStId = computed(() => {
  173. if (data.StListForSearch.length) {
  174. return data.StListForSearch[data.StListForSearch.length - 1].stId == activeSt.value.stId
  175. } else {
  176. return false
  177. }
  178. });
  179. // 摄像头抓拍相关功能 start
  180. function zpSuccess() {
  181. }
  182. function zpError() {
  183. uni.showToast({
  184. title: '摄像头唤起异常'
  185. })
  186. uni.redirectTo({
  187. url: '/pages/client/Kaoshi/list'
  188. })
  189. }
  190. // 摄像头抓拍相关功能 end
  191. // 摄像头确认相关功能 start
  192. function zpConfirmSuccess() {
  193. initKaoshi();
  194. }
  195. function zpConfirmError() {
  196. uni.showToast({
  197. title: '摄像头唤起异常'
  198. })
  199. uni.redirectTo({
  200. url: '/pages/client/Kaoshi/list'
  201. })
  202. }
  203. function zpConfirmCancel() {
  204. uni.redirectTo({
  205. url: '/pages/client/Kaoshi/list'
  206. })
  207. }
  208. // 摄像头确认相关功能 end
  209. function getQaClass(qa) {
  210. if (qa.marked && qa.marked === true) {
  211. return 'paragraph-qa-block-mark';
  212. } else {
  213. if (qa.stTypeId == 1) {
  214. if (checkDanxuanReply(qa)) {
  215. return 'paragraph-qa-block-done';
  216. } else {
  217. return 'paragraph-qa-block-init';
  218. }
  219. } else if (qa.stTypeId == 2) {
  220. if (checkDuoxuanReply(qa)) {
  221. return 'paragraph-qa-block-done';
  222. } else {
  223. return 'paragraph-qa-block-init';
  224. }
  225. } else if (qa.stTypeId == 3) {
  226. if (checkPanduanReply(qa)) {
  227. return 'paragraph-qa-block-done';
  228. } else {
  229. return 'paragraph-qa-block-init';
  230. }
  231. } else if (qa.stTypeId == 4) {
  232. if (checkTiankongReply(qa)) {
  233. return 'paragraph-qa-block-done';
  234. } else {
  235. return 'paragraph-qa-block-init';
  236. }
  237. }
  238. }
  239. }
  240. function skipQuestion(dlIndex, dtIndex) {
  241. progress.dlIndex = dlIndex;
  242. progress.dtIndex = dtIndex;
  243. handlePopupBack()
  244. }
  245. function answerCardItemClick(qa) {
  246. const actQa = data.StListForSearch.find(item => item.stId == qa.stId);
  247. skipQuestion(actQa.dlIndex, actQa.dtIndex)
  248. }
  249. function handleBack() {
  250. uni.redirectTo({
  251. url: "/pages/admin/Kaoshi/list"
  252. })
  253. }
  254. function onTimeUp() {
  255. console.log('end')
  256. }
  257. function showAnswerCard() {
  258. popupRef.value.open('bottom')
  259. }
  260. function handlePopupBack() {
  261. popupRef.value.close()
  262. }
  263. function handlePrev() {
  264. const qa = data.StListForSearch.find(item => item.stId == activeSt.value.stId);
  265. const index = qa.onlyNum - 1;
  266. if (index > 0) {
  267. const result = data.StListForSearch[index - 1];
  268. progress.dlIndex = result.dlIndex;
  269. progress.dtIndex = result.dtIndex
  270. }
  271. }
  272. function handleNext() {
  273. const qa = data.StListForSearch.find(item => item.stId == activeSt.value.stId);
  274. const index = qa.onlyNum - 1;
  275. if (index < data.StListForSearch.length) {
  276. const result = data.StListForSearch[index + 1];
  277. progress.dlIndex = result.dlIndex;
  278. progress.dtIndex = result.dtIndex
  279. }
  280. }
  281. function formatDuanluoList(dlData) {
  282. let uIndex = 0; // 试题onlyNum
  283. let iDuanluo = 0; // 段落onlyNum
  284. let result = [];
  285. for (const duanluo of data.duanluo) {
  286. let paragraph = {
  287. qas: [],
  288. };
  289. paragraph.name = duanluo.name;
  290. let iQa = 0; // 当前试题序号
  291. let order = 0; // 当前题型中第几题
  292. for (const iDanxuan of duanluo.danxuan) {
  293. iDanxuan.type = 'danxuan';
  294. iDanxuan.marked = false;
  295. iDanxuan.onlyNum = uIndex + 1;
  296. iDanxuan.order = order;
  297. iDanxuan.iQa = iQa;
  298. paragraph.qas.push(iDanxuan);
  299. uIndex++;
  300. order++;
  301. iQa++;
  302. data.StListForSearch.push({
  303. stId: iDanxuan.stId,
  304. paragraphName: paragraph.name,
  305. dlIndex: iDuanluo,
  306. dtIndex: iDanxuan.iQa,
  307. onlyNum: iDanxuan.onlyNum
  308. })
  309. }
  310. order = 0;
  311. for (const iDuoxuan of duanluo.duoxuan) {
  312. iDuoxuan.type = 'duoxuan';
  313. iDuoxuan.marked = false;
  314. iDuoxuan.onlyNum = uIndex + 1;
  315. iDuoxuan.order = order;
  316. paragraph.qas.push(iDuoxuan);
  317. iDuoxuan.iQa = iQa;
  318. uIndex++;
  319. order++;
  320. iQa++;
  321. data.StListForSearch.push({
  322. stId: iDuoxuan.stId,
  323. paragraphName: paragraph.name,
  324. dlIndex: iDuanluo,
  325. dtIndex: iDuoxuan.iQa,
  326. onlyNum: iDuoxuan.onlyNum
  327. })
  328. }
  329. order = 0;
  330. for (const iPanduan of duanluo.panduan) {
  331. iPanduan.type = 'panduan';
  332. iPanduan.marked = false;
  333. iPanduan.onlyNum = uIndex + 1;
  334. iPanduan.order = order;
  335. paragraph.qas.push(iPanduan);
  336. iPanduan.iQa = iQa;
  337. uIndex++;
  338. order++;
  339. iQa++;
  340. data.StListForSearch.push({
  341. stId: iPanduan.stId,
  342. paragraphName: paragraph.name,
  343. dlIndex: iDuanluo,
  344. dtIndex: iPanduan.iQa,
  345. onlyNum: iPanduan.onlyNum
  346. })
  347. }
  348. order = 0;
  349. for (const iTiankong of duanluo.tiankong) {
  350. iTiankong.type = 'tiankong';
  351. iTiankong.marked = false;
  352. iTiankong.onlyNum = uIndex + 1;
  353. iTiankong.order = order;
  354. paragraph.qas.push(iTiankong);
  355. iTiankong.iQa = iQa;
  356. uIndex++;
  357. order++;
  358. iQa++;
  359. data.StListForSearch.push({
  360. stId: iTiankong.stId,
  361. paragraphName: paragraph.name,
  362. dlIndex: iDuanluo,
  363. dtIndex: iTiankong.iQa,
  364. onlyNum: iTiankong.onlyNum
  365. })
  366. }
  367. iDuanluo++;
  368. questionData.value.push(paragraph)
  369. console.log('1',questionData.value)
  370. console.log('2',data.StListForSearch)
  371. }
  372. }
  373. function handleBiaoji() {
  374. activeSt.value.marked = !activeSt.value.marked;
  375. }
  376. function saveKsCache() {}
  377. function getKsCache() {}
  378. function removeKsCache() {}
  379. function formatKaoshiData() {}
  380. // 摄像头确认初始化
  381. function initBeforKaoshi() {
  382. console.log(zhuapaiConfirmRef.value)
  383. zhuapaiConfirmRef.value.showDialog()
  384. }
  385. function initKaoshi() {
  386. ksApi.getKaoshiInfo({
  387. ksId: data.ksId
  388. }).then(res => {
  389. const {
  390. ksId,
  391. ksName,
  392. stTotal,
  393. stScore,
  394. biaoji,
  395. endSecond,
  396. pageSize,
  397. toggleScreenFlag,
  398. toggleScreenSecond,
  399. zhuapai,
  400. duanluoList
  401. } = res.data;
  402. data.ksId = ksId;
  403. data.ksName = ksName;
  404. data.stTotal = stTotal;
  405. data.stScore = stScore;
  406. data.biaoji = biaoji;
  407. data.endSecond = endSecond;
  408. data.pageSize = pageSize;
  409. data.toggleScreenFlag = toggleScreenFlag;
  410. data.toggleScreenSecond = toggleScreenSecond;
  411. data.zhuapai = zhuapai;
  412. data.duanluo = duanluoList;
  413. formatDuanluoList(data.duanluo);
  414. // 设置缓存
  415. formatKaoshiData();
  416. // 设置抓拍监听
  417. zhuapaiRef.value.init({zhuapai: 1});
  418. // 设置切屏监听
  419. // qiepingRef.value.init()
  420. uni.setNavigationBarTitle({
  421. title: data.ksName
  422. });
  423. startCountDown.value = true;
  424. })
  425. }
  426. </script>