goNogoTask.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. <template>
  2. <div class="app-container">
  3. <Header />
  4. <div class="main-container">
  5. <van-nav-bar
  6. :title="subjectInfo.name"
  7. left-arrow
  8. left-text=""
  9. @click-left="goBack"
  10. />
  11. <div v-if="!userFinalTestStrShow" class="explan">
  12. <!-- LOGO -->
  13. <!--<h1>{{subjectInfo.name}}</h1>-->
  14. <div class="main-text-div">
  15. {{ subjectInfo.mobileTestDescription }}
  16. </div>
  17. <!-- <div>-->
  18. <!-- <p>{{ subjectInfo.mobileTestDescription }}</p>-->
  19. <!-- </div>-->
  20. <div v-if="buttonShow" class="btnArea">
  21. <van-button block round type="primary" @click="startTest()"
  22. >{{ buttonName }}
  23. </van-button>
  24. </div>
  25. </div>
  26. <div v-if="userFinalTestStrShow" class="t3">
  27. <p v-if="userFinalTestStrShow" class="t4">
  28. GO信号正确反应总时间(毫秒):{{ goTotalResponseTime }}
  29. </p>
  30. <p v-if="userFinalTestStrShow" class="t5">
  31. GO信号平均反应时间(毫秒):{{
  32. goTotalResponseTime == 0 ? 0 : severalGoResponseTime
  33. }}
  34. </p>
  35. <p v-if="userFinalTestStrShow" class="t5">
  36. NO-GO信号反应正确率(%):{{ blackRightRate }}
  37. </p>
  38. </div>
  39. <div
  40. v-if="testFlag"
  41. :class="{ testMainDiv: !testState, activeTask: testState }"
  42. @click="userClick()"
  43. >
  44. <div class="taskHead">
  45. <div v-if="testTypeCode == 1" class="progress-content">
  46. <van-progress
  47. :percentage="percentage"
  48. color="#f2826a"
  49. pivot-text="任务进度"
  50. />
  51. </div>
  52. </div>
  53. <div class="glass">
  54. <span v-if="countDownSpanShow" class="countdownStr">{{
  55. countDownStr
  56. }}</span>
  57. <div
  58. v-if="divShow"
  59. :style="{ background: backgroundColor }"
  60. class="goNoGoTask txt-center"
  61. ></div>
  62. </div>
  63. </div>
  64. <div v-if="saveFlag" class="t6" @click="resultSave()">
  65. <van-button class="scaleButton" type="primary">结果保存</van-button>
  66. </div>
  67. </div>
  68. </div>
  69. </template>
  70. <script>
  71. // import screenfull from "screenfull";
  72. // import { oSessionStorage } from "../../utils/utils";
  73. import Header from "@/components/header/index.vue";
  74. export default {
  75. components: { Header },
  76. data() {
  77. return {
  78. userId: "",
  79. beginCount: false,
  80. countDownTime: 6, //倒计时时长
  81. countDownSpanShow: false, //8秒倒计时显示标志
  82. testTurnCount: 0, //正式测试轮数
  83. backgroundColor: "",
  84. bgImage: require("../../assets/congnitiveAblitity/whiteFlag.png"),
  85. backgroundList: ["red", "black", "yellow", "blue"],
  86. taskId: "", // 认知任务id
  87. greenFlagCount: 0,
  88. redFlagCount: 0,
  89. blueFlagCount: 0,
  90. blackFlagCount: 0,
  91. totalCount: 0, //共有多少个刺激
  92. singleTurnCount: 2, //每一轮多少个刺激
  93. rightClickCount: 0, //正确点击次数
  94. blackClickCount: 0, //黑色框点击次数(错误点击次数)
  95. saveFlag: false, //结果保存按钮显示标志
  96. subjectInfo: {}, //认知任务详情
  97. divShow: false, // 控制黑框显示隐藏
  98. buttonShow: true, // 控制按钮显示吟唱
  99. passTestExam: false, //练习测试通过标识
  100. testType: "练习测试", // 正式测试
  101. testFlag: false,
  102. buttonName: "开始练习", // 正式测试 // 重新练习
  103. testTypeCode: 0, // 0-练习测试 1-正式测试 2-重新测试
  104. startMilliSeconds: 0, //div出现时的毫秒数
  105. divShowInteval: "", //控制div显示的定时器
  106. myInterval: "",
  107. countDownInterval: "", //倒计时定时器
  108. qualifiedCount: 0, //点击合格数
  109. unQualifiedCount: 0, //点击不合格数
  110. cuntdownMinMilliSeconds: 2000, //练习时 2-4秒 2000
  111. cuntdownMaxMilliSeconds: 4000, //正式测试时2-12秒
  112. testState: false,
  113. goTotalResponseTime: 0, //Go信号总反应时常
  114. userCanClick: false, //用户是否可以进行点击
  115. currentBackgroundName: "",
  116. countDownStr: "练习马上开始!",
  117. userResponseRecords: [],
  118. userFinalTestStrShow: false,
  119. blackRightRate: 0, //NO-GO信号正确率
  120. severalGoResponseTime: 0, //go信号平均反应时间
  121. testPlanId: "",
  122. currentIndex: 0
  123. };
  124. },
  125. computed: {
  126. percentage() {
  127. return (this.currentIndex / this.totalCount) * 100;
  128. }
  129. },
  130. // 页面初始化函数
  131. created() {
  132. // this.taskId = this.$route.query.taskId;
  133. this.init();
  134. },
  135. destroyed() {
  136. clearInterval(this.divShowInteval);
  137. clearInterval(this.myInterval);
  138. },
  139. methods: {
  140. init() {
  141. this.testPlanId = this.$route.query.testPlanId || "";
  142. this.userId = JSON.parse(sessionStorage.getItem("userInfo")).id;
  143. this.subjectInfo = JSON.parse(sessionStorage.getItem("subjectInfo"));
  144. this.imgUrl = this.subjectInfo.imageUrl;
  145. },
  146. format(percentage) {
  147. return percentage === 100 ? "测试已完成" : "任务进度";
  148. },
  149. userClick() {
  150. //用户点击动作
  151. if (this.userCanClick) {
  152. this.userCanClick = false;
  153. let milliSecondsCount = new Date().getTime() - this.startMilliSeconds;
  154. this.userResponseRecords[
  155. this.currentIndex
  156. ].responseTime = milliSecondsCount;
  157. if (milliSecondsCount < 1500) {
  158. //正确反应
  159. if (this.backgroundColor == "black") {
  160. this.blackClickCount++;
  161. } else {
  162. this.rightClickCount++;
  163. this.goTotalResponseTime += milliSecondsCount;
  164. }
  165. }
  166. } else if (!this.userCanClick && this.userCanClick != false) {
  167. this.$toast("错误点击!");
  168. }
  169. },
  170. startTest() {
  171. //开始测试
  172. if (
  173. sessionStorage.getItem("userInfo") == "" ||
  174. sessionStorage.getItem("userInfo") == null
  175. ) {
  176. this.$toast("请先登录!");
  177. return;
  178. }
  179. if (this.testTypeCode == 1 && this.beginCount == false) {
  180. this.countDownStr = "马上开始!";
  181. this.beginCount = true;
  182. }
  183. // screenfull.request();
  184. this.countDownSpanShow = true;
  185. this.pageDataInit();
  186. this.buttonShow = false;
  187. this.testState = true;
  188. this.backgroundColor = "";
  189. this.myInterval = setInterval(() => {
  190. this.countDownTime--;
  191. this.countDownStr = this.countDownTime;
  192. if (this.countDownTime == 0) {
  193. this.countDownStr = "开始!";
  194. }
  195. if (this.countDownTime == -1) {
  196. //清除定时器
  197. clearInterval(this.myInterval);
  198. this.countDownStr = "准备";
  199. this.countDownSpanShow = false;
  200. this.countDownTime = 6;
  201. this.backgroundColor = `url(${this.bgImage}) no-repeat center`;
  202. this.divShowInteval = setInterval(() => {
  203. //this.backgroundColor = ''
  204. this.divShow = true;
  205. this.divRandomShow();
  206. }, 2000);
  207. }
  208. }, 1000);
  209. },
  210. testEnd() {
  211. this.computeScore();
  212. },
  213. computeScore() {
  214. //计算练习成绩
  215. this.divShow = false;
  216. clearInterval(this.divShowInteval);
  217. if (this.testTypeCode == 0) {
  218. // 退出全屏
  219. // screenfull.exit();
  220. //练习测试
  221. let rightRate = parseFloat(
  222. (this.rightClickCount + this.singleTurnCount - this.blackClickCount) /
  223. this.totalCount
  224. ).toFixed(2);
  225. if (rightRate > 0.9) {
  226. this.passTestExam = true;
  227. this.testTypeCode = 1;
  228. this.testType = "正式测试";
  229. this.buttonName = "正式测试";
  230. this.singleTurnCount = 9;
  231. this.buttonShow = true;
  232. this.testState = false;
  233. this.testFlag = false;
  234. this.backgroundColor = "";
  235. this.rightClickCount = 0;
  236. this.blackClickCount = 0;
  237. this.subjectInfo.testDescription = this.subjectInfo.testExplain;
  238. //通过测试
  239. this.$toast("您已掌握测试规则,请点击正式测试按钮,进入正式测试!");
  240. } else {
  241. //重新练习
  242. this.rightClickCount = 0;
  243. this.blackClickCount = 0;
  244. this.buttonName = "重新测试";
  245. this.buttonShow = true;
  246. this.testState = false;
  247. this.testFlag = false;
  248. this.backgroundColor = "";
  249. this.$toast("测试未通过,请点击'重新测试'按钮继续练习!");
  250. }
  251. this.userResponseRecords = [];
  252. this.currentIndex = 0;
  253. } else {
  254. //正式测试
  255. //计算测试结果
  256. this.testTurnCount++;
  257. //console.log(this.testTurnCount)
  258. this.pageDataInit(); //页面数据初始化
  259. if (this.testTurnCount != 4) {
  260. this.countDownStr = "休息一下!";
  261. this.startTest();
  262. } else {
  263. //计算入参数据
  264. let goResponseTimeSum = 0;
  265. let goRightClickSum = 0;
  266. let noGoErrorClickSum = 0;
  267. this.userResponseRecords.forEach((item, index) => {
  268. if (item.responseTime < 1500) {
  269. //用户反应小于1000
  270. if (item.flag == "black" && item.responseTime != 0) {
  271. //反应块是黑色
  272. noGoErrorClickSum += 1;
  273. } else if (item.flag != "black" && item.responseTime != 0) {
  274. //反应块是彩色
  275. goResponseTimeSum += item.responseTime;
  276. goRightClickSum += 1;
  277. }
  278. }
  279. });
  280. let sendObj = {
  281. userId: this.userId,
  282. goTotalResponseTime: goResponseTimeSum,
  283. totalRightRate: parseFloat(
  284. ((goRightClickSum +
  285. this.singleTurnCount * 4 -
  286. noGoErrorClickSum) *
  287. 100) /
  288. this.totalCount
  289. ).toFixed(2),
  290. goSeveralResponseTime: parseFloat(
  291. goResponseTimeSum / (this.singleTurnCount * 3 * 4)
  292. ).toFixed(2),
  293. NoGoRightRate: parseFloat(
  294. (1 - noGoErrorClickSum / (this.singleTurnCount * 4)) * 100
  295. ).toFixed(2),
  296. goRightRate: parseFloat(
  297. (goRightClickSum * 100) / (this.singleTurnCount * 3 * 4)
  298. ).toFixed(2),
  299. testTotalCount: this.totalCount,
  300. userResponseRecords: this.userResponseRecords,
  301. testPlanId: this.testPlanId || ""
  302. };
  303. this.$http.post(`/cognize/GO_NO-GO`, sendObj, res => {
  304. this.testState = false;
  305. this.testFlag = false;
  306. this.backgroundColor = "";
  307. this.$toast("测试结束!");
  308. this.goTestResult(res.data);
  309. });
  310. }
  311. }
  312. },
  313. // goTestResult(id) {
  314. // this.$http.get(`getRecordById?id=${id}`, {}, (res) => {
  315. // if (res.code == 2001) {
  316. // this.$toast.fail(res.msg);
  317. // return;
  318. // }
  319. // if (res && res.code == 200) {
  320. // if (
  321. // JSON.parse(res.data?.userRecordEntity?.testResult).versionNo ==
  322. // "2.0.1"
  323. // ) {
  324. // //跳转新测试结果模版数据暂存本地
  325. // // sessionStorage.setItem("testResult", res.data?.userRecordEntity?.testResult);
  326. // // this.$store.dispatch('setTestResult',JSON.parse(res.data?.userRecordEntity?.testResult));
  327. // this.$store.dispatch("setTestResult", res?.data);
  328. // this.$router.push({
  329. // name: "testResultNew",
  330. // query: {
  331. // id: res.data?.userRecordEntity?.id,
  332. // title: res.data?.userRecordEntity?.name,
  333. // testPlanId: this.testPlanId || "",
  334. // come: 1, //1-来自认知任务列表,2-测试记录列表
  335. // },
  336. // });
  337. // } else {
  338. // //跳转旧测试结果模版
  339. //
  340. // let testResult = JSON.parse(
  341. // res.data?.userRecordEntity?.testResult
  342. // )[0].version;
  343. //
  344. // if (testResult == 2) {
  345. // this.$router.push("/testRecordsSCl?id=" + id);
  346. // } else {
  347. // this.$router.push(
  348. // "/testResult?flag=" +
  349. // res.data?.userRecordEntity?.flag +
  350. // "&id=" +
  351. // id
  352. // );
  353. // }
  354. // }
  355. // } else {
  356. // this.$toast.fail("获取数据失败!服务器异常");
  357. // }
  358. // });
  359. // },
  360. countDown() {
  361. // 测试时间倒计时
  362. this.myInterval = setInterval(() => {
  363. this.countDownTime--;
  364. if (this.countDownTime == 0) {
  365. //计算测试结果
  366. this.computeScore();
  367. // 清除定时器
  368. clearInterval(this.myInterval);
  369. this.countDownTime = 6;
  370. }
  371. }, 1000);
  372. },
  373. divRandomShow() {
  374. if (this.backgroundList.length > 0) {
  375. let colorIndex = Math.floor(Math.random() * this.backgroundList.length);
  376. this.backgroundColor = this.backgroundList[colorIndex];
  377. this.currentBackgroundName = this.backgroundColor;
  378. this.userResponseRecords.push({
  379. flag: this.backgroundColor,
  380. responseTime: 0
  381. });
  382. this.userCanClick = true;
  383. this.startMilliSeconds = new Date().getTime();
  384. switch (this.backgroundColor) {
  385. case "red":
  386. this.redFlagCount--;
  387. if (this.redFlagCount == 0) {
  388. this.backgroundList.splice(colorIndex, 1);
  389. }
  390. break;
  391. case "black":
  392. this.blackFlagCount--;
  393. if (this.blackFlagCount == 0) {
  394. this.backgroundList.splice(colorIndex, 1);
  395. }
  396. break;
  397. case "yellow":
  398. this.greenFlagCount--;
  399. if (this.greenFlagCount == 0) {
  400. this.backgroundList.splice(colorIndex, 1);
  401. }
  402. break;
  403. case "blue":
  404. this.blueFlagCount--;
  405. if (this.blueFlagCount == 0) {
  406. this.backgroundList.splice(colorIndex, 1);
  407. }
  408. break;
  409. default:
  410. break;
  411. }
  412. setTimeout(() => {
  413. this.divShow = false;
  414. }, 500);
  415. setTimeout(() => {
  416. this.backgroundColor = `url(${this.bgImage}) no-repeat center`;
  417. this.currentIndex++;
  418. }, 1900);
  419. } else {
  420. this.backgroundColor = `url(${this.bgImage}) no-repeat center`;
  421. this.testEnd();
  422. }
  423. },
  424. goBack() {
  425. this.$router.go(-1);
  426. },
  427. pageDataInit() {
  428. this.backgroundList = ["red", "black", "yellow", "blue"];
  429. this.greenFlagCount = this.singleTurnCount;
  430. this.redFlagCount = this.singleTurnCount;
  431. this.blueFlagCount = this.singleTurnCount;
  432. this.blackFlagCount = this.singleTurnCount;
  433. this.testFlag = true;
  434. if (this.testTypeCode == 0) {
  435. this.rightClickCount = 0;
  436. this.blackClickCount = 0;
  437. this.totalCount = this.singleTurnCount * 4;
  438. } else {
  439. this.totalCount = this.singleTurnCount * 4 * 4;
  440. }
  441. }
  442. }
  443. };
  444. </script>
  445. <style lang="scss" scoped>
  446. .btnArea {
  447. width: 85%;
  448. margin: 50px auto 0;
  449. }
  450. .explan {
  451. box-sizing: border-box;
  452. padding: 16px 10px 0;
  453. }
  454. .explan h1 {
  455. font-size: 16px;
  456. color: #222222;
  457. text-align: center;
  458. }
  459. .explan p {
  460. font-size: 14px;
  461. color: #666666;
  462. line-height: 26px;
  463. text-indent: 15px;
  464. margin: 20px 0 20px;
  465. }
  466. .progress-content {
  467. position: absolute;
  468. width: 100%;
  469. height: 20px;
  470. left: 0;
  471. top: 60px;
  472. box-sizing: border-box;
  473. padding: 4px 10px;
  474. }
  475. .testMainDiv {
  476. margin: 0 auto;
  477. margin-top: 10px;
  478. background: gray;
  479. background-size: cover;
  480. /* text-align:center; */
  481. width: 500px;
  482. height: 300px;
  483. /* object-fit:fill; */
  484. }
  485. .activeTask {
  486. background: url(../../assets/congnitiveAblitity/goNoGoBg.png) no-repeat center;
  487. background-size: cover;
  488. position: fixed;
  489. top: 0;
  490. left: 0;
  491. right: 0;
  492. bottom: 0;
  493. display: flex;
  494. flex-direction: column;
  495. }
  496. .goNoGoTask {
  497. position: absolute;
  498. top: 50%;
  499. left: 50%;
  500. transform: translate(-50%, -50%);
  501. /* background:black; */
  502. background-size: cover;
  503. /* text-align:center; */
  504. width: 190px;
  505. height: 140px;
  506. box-shadow: 0px 8px 12px rgba(0, 0, 0, 0.16);
  507. opacity: 1;
  508. border-radius: 28px;
  509. /* object-fit:fill; */
  510. }
  511. .countdownStr {
  512. position: absolute;
  513. top: 50%;
  514. left: 50%;
  515. transform: translate(-50%, -50%);
  516. color: black;
  517. font-size: 18px;
  518. /*font-weight: bold;*/
  519. text-align: center;
  520. margin: 0 auto;
  521. }
  522. .scaleName {
  523. margin-top: 70px;
  524. background-size: cover;
  525. }
  526. .scaleButton {
  527. margin-top: 20px;
  528. margin-bottom: 20px;
  529. background-size: cover;
  530. }
  531. .stip2 {
  532. padding: 0 20px;
  533. margin: 0 auto;
  534. text-align: left;
  535. position: absolute;
  536. z-index: 8;
  537. }
  538. .stips {
  539. height: 100%;
  540. background: rgb(248, 248, 248);
  541. opacity: 1;
  542. border-radius: 12px;
  543. margin: 0 auto;
  544. }
  545. .stip {
  546. float: left;
  547. height: 100%;
  548. overflow-y: auto;
  549. }
  550. .t3 {
  551. width: 750px;
  552. height: 100px;
  553. margin: 0 auto;
  554. }
  555. .t7 {
  556. background: url(../../assets/page4.png) no-repeat center;
  557. position: absolute;
  558. top: 0;
  559. bottom: 0;
  560. right: 0;
  561. left: 0;
  562. background-size: cover;
  563. z-index: 1;
  564. }
  565. .t4 {
  566. color: red;
  567. text-align: left;
  568. margin-top: 3%;
  569. margin-left: 15%;
  570. font-size: 30px;
  571. }
  572. .t5 {
  573. color: red;
  574. text-align: left;
  575. font-size: 30px;
  576. margin-left: 15%;
  577. }
  578. .t6 {
  579. text-align: center;
  580. /* padding-bottom: 2rem; */
  581. }
  582. .pix {
  583. position: fixed;
  584. bottom: 130px;
  585. right: 0;
  586. left: 0;
  587. width: 70px;
  588. margin: 0 auto;
  589. cursor: pointer;
  590. z-index: 99;
  591. }
  592. .start {
  593. position: fixed;
  594. z-index: 999;
  595. }
  596. </style>