index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. <template>
  2. <view @touchstart="touchStart" @touchend="touchEnd" class="box">
  3. <nav-bar :title="navTitle"> </nav-bar>
  4. <!-- <view style="text-align:center;">
  5. <van-count-down :time="time"></van-count-down>
  6. </view> -->
  7. <view class="divider"></view>
  8. <view class="problem-box">
  9. <span class="problem-type">{{
  10. problemList[problemListIndex].questionType | questionType
  11. }}</span>
  12. <!-- <text>{{ problemListIndex + 1 }}、</text> -->
  13. <text class="problem-issue">{{
  14. problemList[problemListIndex].issue
  15. }}</text>
  16. <view v-if="problemList[problemListIndex].image" class="problem-img">
  17. <image
  18. mode="widthFix"
  19. :src="problemList[problemListIndex].image"
  20. ></image>
  21. </view>
  22. <!-- 单选 -->
  23. <view
  24. v-if="
  25. problemList[problemListIndex].questionType < 3 &&
  26. !problemList[problemListIndex].isCompleted
  27. "
  28. class="problem-ops"
  29. >
  30. <van-radio-group
  31. @change="changeRadioGroup"
  32. :value="problemList[problemListIndex].userAnswer"
  33. >
  34. <van-radio
  35. @change="changeCheckbox"
  36. :value="item.value"
  37. class="problem-checkbox"
  38. use-icon-slot
  39. v-for="(item, index) in problemList[problemListIndex].optsArr"
  40. :key="index"
  41. :name="item"
  42. >
  43. <text>{{ item.value }}</text>
  44. <view
  45. class="problem-op"
  46. :class="{
  47. 'problem-op_selected': item.selected,
  48. }"
  49. slot="icon"
  50. >{{ numberToLetter(index) }}</view
  51. >
  52. </van-radio>
  53. </van-radio-group>
  54. </view>
  55. <view
  56. v-if="
  57. problemList[problemListIndex].questionType < 3 &&
  58. problemList[problemListIndex].isCompleted
  59. "
  60. >
  61. <view
  62. v-for="(item, index) in problemList[problemListIndex].optsArr"
  63. :key="index"
  64. class="problem-select"
  65. >
  66. <icon
  67. v-if="item.isAnswer && item.selected"
  68. class="icon-box-img"
  69. type="success"
  70. size="75rpx"
  71. ></icon>
  72. <icon
  73. v-if="!item.isAnswer && item.selected"
  74. class="icon-box-img"
  75. type="cancel"
  76. size="75rpx"
  77. ></icon>
  78. <text v-if="!item.isAnswer && !item.selected" class="problem-op">{{
  79. numberToLetter(index)
  80. }}</text>
  81. <text
  82. v-if="item.isAnswer && !item.selected"
  83. class="problem-op_green"
  84. >{{ numberToLetter(index) }}</text
  85. >
  86. <text class="problem-opAnswer">{{ item.value }}</text>
  87. </view>
  88. </view>
  89. <!-- 多选 -->
  90. <view
  91. v-if="
  92. problemList[problemListIndex].questionType == 3 &&
  93. !problemList[problemListIndex].isCompleted
  94. "
  95. class="problem-ops"
  96. >
  97. <van-checkbox-group
  98. @change="changeCheckGroup"
  99. :value="problemList[problemListIndex].userAnswer"
  100. :max="4"
  101. >
  102. <van-checkbox
  103. @change="changeCheckbox"
  104. :value="item.selected"
  105. class="problem-checkbox"
  106. use-icon-slot
  107. v-for="(item, index) in problemList[problemListIndex].optsArr"
  108. :key="index"
  109. :name="item.value"
  110. >
  111. <text>{{ item.value }}</text>
  112. <view
  113. class="problem-op"
  114. :class="{
  115. 'problem-op_selected': problemList[
  116. problemListIndex
  117. ].userAnswer.includes(item.value),
  118. }"
  119. slot="icon"
  120. >{{ numberToLetter(index) }}</view
  121. >
  122. </van-checkbox>
  123. </van-checkbox-group>
  124. <view class="flex-center">
  125. <van-button
  126. @click="confirmMult"
  127. color="#498ef5"
  128. round
  129. custom-style="width:600rpx"
  130. type="primary"
  131. >确定</van-button
  132. >
  133. </view>
  134. </view>
  135. <view
  136. v-if="
  137. problemList[problemListIndex].questionType == 3 &&
  138. problemList[problemListIndex].isCompleted
  139. "
  140. >
  141. <view
  142. v-for="(item, index) in problemList[problemListIndex].optsArr"
  143. :key="index"
  144. class="problem-select"
  145. >
  146. <icon
  147. v-if="item.isAnswer && item.selected"
  148. class="icon-box-img"
  149. type="success"
  150. size="75rpx"
  151. ></icon>
  152. <icon
  153. v-if="!item.isAnswer && item.selected"
  154. class="icon-box-img"
  155. type="cancel"
  156. size="75rpx"
  157. ></icon>
  158. <text v-if="!item.isAnswer && !item.selected" class="problem-op">{{
  159. numberToLetter(index)
  160. }}</text>
  161. <text
  162. v-if="item.isAnswer && !item.selected"
  163. class="problem-op_green"
  164. >{{ numberToLetter(index) }}</text
  165. >
  166. <text class="problem-opAnswer">{{ item.value }}</text>
  167. </view>
  168. </view>
  169. <view class="function-list">
  170. <div class="function-item">
  171. <van-icon name="star-o" size="25px" />
  172. <span>收藏</span>
  173. </div>
  174. <!-- <div class="function-item" @click="answerAudioPlay">
  175. <m-icon type="a-dtda" size="25px" />
  176. <span>读题+答案</span>
  177. </div> -->
  178. <div
  179. @click="readQuestion(problemList[problemListIndex].issuemp3)"
  180. class="function-item"
  181. >
  182. <van-icon name="bullhorn-o" size="25px" />
  183. <span>读题</span>
  184. </div>
  185. <!-- <div class="function-item" @click="currentAnswerIndexBack">
  186. <m-icon type="shangyiti" size="25px" />
  187. <span>上一题</span>
  188. </div>
  189. <div class="function-item" @click="skillsShow = true">
  190. <m-icon type="zongtishu" size="25px" />
  191. <span>1/100</span>
  192. </div>
  193. <div class="function-item" @click="currentAnswerIndexGo">
  194. <m-icon type="xiayiti" size="25px" />
  195. <span>下一题</span>
  196. </div> -->
  197. </view>
  198. <view
  199. v-if="problemList[problemListIndex].isCompleted"
  200. class="look-answer"
  201. >
  202. <view>答案是:{{ problemList[problemListIndex].answer }}</view>
  203. </view>
  204. </view>
  205. <explainJs
  206. @close="
  207. () => {
  208. explainJsVisible = false;
  209. }
  210. "
  211. :explainJs="problemList[problemListIndex].explainJs"
  212. :explainjsmp3="problemList[problemListIndex].explainjsmp3"
  213. :show="explainJsVisible"
  214. ></explainJs>
  215. <van-tabbar>
  216. <van-tabbar-item @click="goBeforeTopics"
  217. ><van-icon
  218. slot="icon"
  219. custom-style="transform: rotate(90deg);"
  220. custom-class="last-subject"
  221. name="down"
  222. size="18px"
  223. />上一题
  224. </van-tabbar-item>
  225. <van-tabbar-item>
  226. <icon slot="icon" class="icon-box-img" type="success" size="18px"></icon
  227. >{{ trueNum }}</van-tabbar-item
  228. >
  229. <van-tabbar-item
  230. ><icon
  231. slot="icon"
  232. class="icon-box-img"
  233. type="cancel"
  234. size="18px"
  235. ></icon>
  236. {{ falseNum }}
  237. </van-tabbar-item>
  238. <van-tabbar-item
  239. ><van-icon slot="icon" size="18px" name="description" />{{
  240. problemListIndex + 1
  241. }}/{{ problemListTotal }}
  242. </van-tabbar-item>
  243. <van-tabbar-item
  244. @click="
  245. () => {
  246. explainJsVisible = true;
  247. }
  248. "
  249. ><icon slot="icon" type="warn" size="18px" />解释
  250. </van-tabbar-item>
  251. <van-tabbar-item @click="goNextTopics"
  252. ><van-icon
  253. slot="icon"
  254. custom-style="transform: rotate(-90deg);"
  255. custom-class="last-subject"
  256. name="down"
  257. size="18px"
  258. />下一题
  259. </van-tabbar-item>
  260. </van-tabbar>
  261. </view>
  262. </template>
  263. <script>
  264. import navBar from "./components/navBar.vue";
  265. import api from "@/api/index";
  266. import utils from "@/utils/index";
  267. import explainJs from "./components/explainJs.vue";
  268. export default {
  269. data() {
  270. return {
  271. query: {
  272. cert: "",
  273. vehicle: "",
  274. subject: "",
  275. title: "",
  276. },
  277. trueNum: 0,
  278. falseNum: 0,
  279. currentOptions: [
  280. {
  281. selected: false,
  282. value: "",
  283. isAnswer: false,
  284. },
  285. {},
  286. ],
  287. explainJsVisible: false,
  288. time: 45 * 60 * 1000,
  289. problemListTotal: 1,
  290. touchx: 0,
  291. touchy: 0,
  292. problemList: [
  293. {
  294. answer: "×",
  295. answerkeyword: "",
  296. answermp3:
  297. "https://t1-1305573081.file.myqcloud.com/kt/answer_mp3/answer1389.mp3",
  298. classIssue: "54",
  299. classIssueName: "车内开关/装置",
  300. classSort: 16,
  301. createTime: "2022-04-21 13:33:46",
  302. excellIssue: "23",
  303. excellIssueName: "必学题三",
  304. excellSort: 4,
  305. explainGif:
  306. "https://t1-1305573081.file.myqcloud.com/kt/explain_gif/explain1389.gif",
  307. explainJq:
  308. "看图答题:红色圆圈套在杆子中间.答对;不在中间或没有圆圈的.答错。",
  309. explainJs:
  310. "图中所示为左右转向灯开关转向灯操作:上提是右转向灯亮起,下压是左转向灯。",
  311. explainMp3:
  312. "https://t1-1305573081.file.myqcloud.com/kt/explain_mp3/explain1389.mp3",
  313. explainjsmp3:
  314. "https://t1-1305573081.file.myqcloud.com/kt/explain_js_mp3/explainJS1389.mp3",
  315. id: 831,
  316. idKt: 1389,
  317. idYdt: 950,
  318. image:
  319. "https://t1-1305573081.file.myqcloud.com/kt/image/image1389.png",
  320. imageYdt:
  321. "https://t1-1305573081.file.myqcloud.com/kt/image_ydt/5eb4d75agw1e291vmniovj.jpg",
  322. issue: "将转向灯开关向上提,左转向灯亮。",
  323. issuemp3:
  324. "https://t1-1305573081.file.myqcloud.com/kt/issue_mp3/issue1389.mp3",
  325. liceBus: "1",
  326. liceCar: "1",
  327. liceMoto: null,
  328. liceTruck: "1",
  329. number: 831,
  330. opts: "√-×",
  331. optsArr: ["√", "×"],
  332. placeIssue: null,
  333. placeIssueName: null,
  334. placeSort: null,
  335. questionType: 1,
  336. sequeIssue: "7",
  337. sequeIssueName: "机械仪表",
  338. sequeSort: 25,
  339. skillkeyword: "没有圆圈-答错",
  340. subject: 1,
  341. titlekeyword: "",
  342. updateTime: "2022-04-22 13:43:07",
  343. userAnswer: [],
  344. },
  345. ],
  346. problemListIndex: 0,
  347. };
  348. },
  349. filters: {
  350. questionType: function (value) {
  351. let question = "";
  352. switch (value) {
  353. case 1:
  354. case "1":
  355. question = "判断题";
  356. break;
  357. case 2:
  358. case "2":
  359. question = "单选题";
  360. break;
  361. case 3:
  362. case "3":
  363. question = "多选题";
  364. break;
  365. }
  366. return question;
  367. },
  368. },
  369. methods: {
  370. touchStart(e) {
  371. var that = this;
  372. (this.touchx = e.changedTouches[0].clientX),
  373. (this.touchy = e.changedTouches[0].clientY);
  374. },
  375. touchEnd(e) {
  376. console.log(e);
  377. var that = this;
  378. let x = e.changedTouches[0].clientX;
  379. let y = e.changedTouches[0].clientY;
  380. let turn = "";
  381. if (x - that.touchx > 50 && Math.abs(y - that.touchy) < 50) {
  382. //右滑
  383. turn = "right";
  384. this.problemListIndex <= 0
  385. ? uni.showToast({
  386. title: "到底了",
  387. icon: "none",
  388. })
  389. : this.problemListIndex--;
  390. } else if (x - that.touchx < -50 && Math.abs(y - that.touchy) < 50) {
  391. //左滑
  392. turn = "left";
  393. this.problemListIndex >= this.problemList.length - 1
  394. ? uni.showToast({
  395. title: "到底了",
  396. icon: "none",
  397. })
  398. : this.problemListIndex++;
  399. }
  400. if (y - that.touchy > 50 && Math.abs(x - that.touchx) < 50) {
  401. //下滑
  402. turn = "down";
  403. } else if (y - that.touchy < -50 && Math.abs(x - that.touchx) < 50) {
  404. //上滑
  405. turn = "up";
  406. }
  407. //根据方向进行操作
  408. if (turn == "down") {
  409. //下滑触发操作
  410. }
  411. console.log(turn);
  412. },
  413. isRightAnswer(item) {
  414. if (
  415. typeof item.userAnswer == "object" &&
  416. Array.isArray(item.userAnswer)
  417. ) {
  418. let answerArr = item.answer.split("-");
  419. answerArr.sort((a, b) => {
  420. return a - b;
  421. });
  422. item.userAnswer.sort((a, b) => {
  423. return a - b;
  424. });
  425. return answerArr.join("-") === item.userAnswer.join("-");
  426. } else if (typeof item.userAnswer == "string") {
  427. return item.answer === item.userAnswer;
  428. }
  429. },
  430. submitExam(e) {
  431. let score = 0;
  432. let query = this.query;
  433. this.problemList.forEach((item, index) => {
  434. if (
  435. typeof item.userAnswer == "object" &&
  436. Array.isArray(item.userAnswer)
  437. ) {
  438. let answerArr = item.answer.split("-");
  439. answerArr.sort((a, b) => {
  440. return a - b;
  441. });
  442. item.userAnswer.sort((a, b) => {
  443. return a - b;
  444. });
  445. if (answerArr.join("-") === item.userAnswer.join("-")) {
  446. score = score + 1;
  447. }
  448. } else if (typeof item.userAnswer == "string") {
  449. item.answer === item.userAnswer ? (score = score + 1) : "";
  450. }
  451. });
  452. uni.showModal({
  453. title: "是否交卷",
  454. content: "交卷后不可再修改了",
  455. success() {
  456. uni.navigateTo({
  457. url:
  458. "/otherPages/mockExamEnd/index?" +
  459. utils.mapToUrlQuery({
  460. score,
  461. ...query,
  462. }),
  463. });
  464. },
  465. fail() {},
  466. });
  467. },
  468. goBeforeTopics() {
  469. if (this.problemListIndex <= 0) {
  470. uni.showToast({
  471. title: "到底了",
  472. icon: "none",
  473. });
  474. return;
  475. }
  476. this.problemListIndex = this.problemListIndex - 1;
  477. },
  478. goNextTopics() {
  479. if (this.problemListIndex >= this.problemList.length - 1) {
  480. uni.showToast({
  481. title: "到底了",
  482. icon: "none",
  483. });
  484. return;
  485. }
  486. this.problemListIndex = this.problemListIndex + 1;
  487. },
  488. readQuestion(e) {
  489. let globalAudio = utils.wxUtils.getGlobAudio();
  490. if (globalAudio) {
  491. globalAudio.src = e;
  492. globalAudio.stop();
  493. globalAudio.play();
  494. }
  495. },
  496. confirmMult(e) {
  497. this.$set(this.problemList[this.problemListIndex], "isCompleted", true);
  498. if (
  499. JSON.stringify(
  500. this.problemList[this.problemListIndex].answer.split("-").sort()
  501. ) ===
  502. JSON.stringify(
  503. this.problemList[this.problemListIndex].userAnswer.sort()
  504. )
  505. ) {
  506. this.trueNum++;
  507. } else {
  508. this.falseNum++;
  509. }
  510. // this.problemList[this.problemListIndex]
  511. },
  512. changeCheckGroup(e) {
  513. //console.log(e);
  514. // this.$set()
  515. this.$set(
  516. this.problemList[this.problemListIndex],
  517. "userAnswer",
  518. e.detail
  519. );
  520. this.problemList[this.problemListIndex].optsArr.forEach((item) => {
  521. if (e.detail.includes(item.value)) {
  522. item.selected = true;
  523. } else {
  524. item.selected = false;
  525. }
  526. });
  527. },
  528. changeRadioGroup(e) {
  529. console.log(e);
  530. this.$set(this.problemList[this.problemListIndex], "isCompleted", true);
  531. this.$set(
  532. this.problemList[this.problemListIndex],
  533. "userAnswer",
  534. e.detail.value
  535. );
  536. e.detail.selected = true;
  537. this.$set(
  538. this.problemList[this.problemListIndex].optsArr,
  539. e.detail.index,
  540. e.detail
  541. );
  542. e.detail.value === this.problemList[this.problemListIndex].answer
  543. ? (this.trueNum = this.trueNum + 1)
  544. : (this.falseNum = this.falseNum + 1);
  545. // this.problemList[this.problemListIndex].optsArr
  546. },
  547. changeCheckbox(e) {
  548. // console.log(e);
  549. },
  550. numberToLetter(index) {
  551. index = Number(index);
  552. return String.fromCharCode(index + 65);
  553. },
  554. },
  555. onLoad(query) {
  556. let that = this;
  557. this.query = query;
  558. api.exam
  559. .studentQuestionInfoList({
  560. ...this.query,
  561. })
  562. .then((res) => {
  563. res.rows.forEach((element) => {
  564. element.optsArr = [];
  565. element.opts.split("-").forEach((item, index) => {
  566. if (element.questionType == 3) {
  567. element.optsArr.push({
  568. selected: false,
  569. value: item,
  570. index: index,
  571. isAnswer: element.answer.split("-").includes(item),
  572. });
  573. } else {
  574. element.optsArr.push({
  575. selected: false,
  576. value: item,
  577. index: index,
  578. isAnswer: item === element.answer,
  579. });
  580. }
  581. });
  582. element.isCompleted = false;
  583. element.userAnswer = [];
  584. });
  585. that.problemListTotal = res.total;
  586. that.problemList = res.rows;
  587. });
  588. },
  589. computed: {
  590. //liceCar=1&liceTruck=&liceBus=&liceMoto=&name=科目一&cert=C1/C2/C3&vehicle=轿车&subject=1&title=顺序练习&sort=3
  591. navTitle() {
  592. let subjectName = this.query.subject == 1 ? "科目一" : "科目四";
  593. return `(${this.query.cert})/${subjectName}/${this.query.title}/${
  594. this.query.classIssueName ||
  595. this.query.placeIssueName ||
  596. this.query.excellIssueName ||
  597. this.query.sequeIssueName
  598. }`;
  599. },
  600. },
  601. watch: {
  602. problemListIndex: {
  603. handler(newValue, oldValue) {},
  604. immediate: true,
  605. },
  606. },
  607. components: {
  608. navBar,
  609. explainJs,
  610. },
  611. };
  612. </script>
  613. <style lang="scss" scoped>
  614. .divider {
  615. width: 100%;
  616. height: 24rpx;
  617. background-color: #f2f3f5;
  618. }
  619. .flex-center {
  620. display: flex;
  621. justify-content: center;
  622. width: 100%;
  623. }
  624. .box {
  625. width: 100%;
  626. height: 100vh;
  627. background: #fff;
  628. .look-answer {
  629. margin-top: 30rpx;
  630. padding: 0 12rpx;
  631. background: #f2f3f5;
  632. font-size: 36rpx;
  633. line-height: 62rpx;
  634. }
  635. .last-subject {
  636. transform: rotate(90deg);
  637. }
  638. .function-list {
  639. width: 100%;
  640. font-size: 13px;
  641. display: flex;
  642. justify-content: space-around;
  643. flex-wrap: wrap;
  644. padding: 15px;
  645. box-sizing: border-box;
  646. .function-item {
  647. margin-bottom: 20px;
  648. width: 30%;
  649. display: flex;
  650. flex-direction: column;
  651. align-items: center;
  652. font-size: 13px;
  653. font-weight: 400;
  654. color: #8a9099;
  655. span {
  656. margin-top: 5px;
  657. }
  658. }
  659. }
  660. .problem-select {
  661. display: flex;
  662. align-content: center;
  663. align-items: center;
  664. margin-top: 15rpx;
  665. padding-left: 30rpx;
  666. }
  667. .problem-box {
  668. padding: 15rpx;
  669. padding-bottom: 50rpx;
  670. background: #fff;
  671. /deep/ .van-checkbox {
  672. padding-bottom: 15rpx;
  673. }
  674. /deep/ .van-radio {
  675. padding-bottom: 15rpx;
  676. }
  677. .problem-type {
  678. padding-left: 10rpx;
  679. padding-right: 10rpx;
  680. padding-top: 4rpx;
  681. padding-bottom: 4rpx;
  682. font-size: 24rpx;
  683. border-radius: 16rpx 16rpx 0 16rpx;
  684. background: #498ef5;
  685. margin-right: 10rpx;
  686. color: #fff;
  687. font-size: 32rpx;
  688. }
  689. .problem-issue {
  690. font-size: 42rpx;
  691. }
  692. .problem-ops {
  693. margin-top: 30rpx;
  694. padding-left: 30rpx;
  695. .problem-checkbox {
  696. height: 100rpx;
  697. }
  698. }
  699. .problem-op {
  700. width: 75rpx;
  701. height: 75rpx;
  702. line-height: 75rpx;
  703. border-radius: 50%;
  704. text-align: center;
  705. overflow: hidden;
  706. background: #fff;
  707. box-shadow: 0px 4rpx 12rpx rgba(0, 0, 0, 0.16);
  708. }
  709. .problem-op_green {
  710. width: 75rpx;
  711. height: 75rpx;
  712. line-height: 75rpx;
  713. border-radius: 50%;
  714. text-align: center;
  715. overflow: hidden;
  716. background: #01c18d;
  717. box-shadow: 0px 4rpx 12rpx rgba(0, 0, 0, 0.16);
  718. }
  719. .problem-opAnswer {
  720. font-size: 16px;
  721. margin-left: 12rpx;
  722. }
  723. .problem-op_selected {
  724. background: #498ef5;
  725. }
  726. .problem-img {
  727. width: 100%;
  728. margin-top: 20rpx;
  729. image {
  730. margin: 0 auto;
  731. }
  732. }
  733. }
  734. }
  735. </style>