Browse Source

feat(认知任务): 图片拼图、空间定向接口对接及页面优化完成

JutarryWu 6 months ago
parent
commit
2ac4c77bb1

+ 1 - 1
README.md

@@ -2,7 +2,7 @@
 
 <img src="https://cdn.jsdelivr.net/gh/easy-temps/easy-static/cover.png" alt="cover" />
 
-<h1 align="center">vue3-vant-mobile</h1>
+<h1 align="center">insomnia-cognition-h5</h1>
 
 English / [简体中文](./README.zh-CN.md)
 

+ 2 - 2
build/vite/index.ts

@@ -94,8 +94,8 @@ export function createVitePlugins() {
       registerType: 'autoUpdate',
       includeAssets: ['favicon.svg', 'safari-pinned-tab.svg'],
       manifest: {
-        name: 'vue3-vant-mobile',
-        short_name: 'vue3-vant-mobile',
+        name: 'insomnia-cognition-h5',
+        short_name: 'insomnia-cognition-h5',
         theme_color: '#ffffff',
         icons: [
           {

+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "vue3-vant-mobile",
+  "name": "insomnia-cognition-h5",
   "type": "module",
   "version": "2.3.9",
   "packageManager": "pnpm@9.6.0",

+ 43 - 9
src/api/game.ts

@@ -2,6 +2,7 @@ import request from '@/utils/request'
 import type { ResponseBody } from '@/api/typing'
 
 const GAME_BASE_URL = '/game'
+const GAME_BASE_URL_2 = '/record'
 
 class GameAPI {
   /**
@@ -18,17 +19,17 @@ class GameAPI {
   }
 
   /**
-   * 添加游戏
+   * 添加游戏结果数据
    *
-   * @param data 游戏表单数据
+   * @param data 游戏结果数据
    */
-  // static add(data: GameVO) {
-  //   return request({
-  //     url: `${GAME_BASE_URL}/save`,
-  //     method: 'post',
-  //     data: data
-  //   })
-  // }
+  static add(data: GameResultVO) {
+    return request({
+      url: `${GAME_BASE_URL_2}/save`,
+      method: 'post',
+      data,
+    })
+  }
 
   /**
    * 修改游戏
@@ -69,3 +70,36 @@ export interface GameVO {
   /** 游戏简介 */
   intro?: string
 }
+
+/** 游戏记录信息 */
+export interface GameResultVO {
+  /** 游戏记录ID */
+  id?: string
+  /** 完成(0:未完成 1:已完成) */
+  finish?: string
+  /** 游戏ID */
+  gameId?: string | number
+  /** 游戏名称 */
+  gameName?: string
+  /** 游戏等级 */
+  gamelevel?: string | number
+  /** 游戏得分 */
+  score?: number
+  /** 用户ID */
+  userId?: string
+  /** 游戏结果参数 */
+  paramList?: Result[]
+  /** 游戏结果参数(关卡) */
+  levelList?: ResultLevel[]
+}
+
+interface Result {
+  code?: string
+  name?: string
+  value?: string | number
+}
+
+export interface ResultLevel {
+  level?: string | number
+  levelParamList?: Result[]
+}

+ 1 - 1
src/assets/icons/correct.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1717481423786" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="985" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M442.88 1010.176C294.912 826.88 120.832 688.128 0 644.608l286.72-172.032 138.24 268.8s225.28-545.792 581.12-727.552c-7.68 130.048-43.008 242.688 17.92 381.44-156.16 35.328-476.672 424.96-581.12 614.912z" p-id="986"></path></svg>
+<svg 
 xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink"
 width="49px" height="43px">
<defs>
<filter filterUnits="userSpaceOnUse" id="Filter_0" x="-1px" y="-1px" width="50px" height="44px"  >
                <feOffset in="SourceAlpha" dx="0" dy="2" />
                <feGaussianBlur result="blurOut" stdDeviation="0" />
                <feFlood flood-color="rgb(39, 48, 113)" result="floodOut" />
                <feComposite operator="atop" in="floodOut" in2="blurOut" />
                <feComponentTransfer><feFuncA type="linear" slope="0.4"/></feComponentTransfer>
                <feMerge>
    <feMergeNode/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
            </filter>

</defs>
<g filter="url(#Filter_0)">
<path fill-rule="evenodd"  stroke="rgb(255, 255, 255)" stroke-width="2px" stroke-linecap="butt" stroke-linejoin="miter" fill="rgb(100, 197, 17)"
 d="M9.0,11.999 C9.0,11.999 11.499,11.59 13.0,11.0 C14.589,10.937 15.187,11.771 15.226,12.409 L19.199,16.375 C19.199,16.375 19.762,16.912 20.193,16.375 C20.624,15.838 32.115,3.486 32.115,3.486 C32.115,3.486 33.828,1.276 36.89,3.486 C38.350,5.696 44.37,11.418 44.37,11.418 C44.37,11.418 45.572,12.781 43.43,15.383 C40.515,17.985 22.180,36.203 22.180,36.203 C22.180,36.203 19.241,39.118 16.219,36.203 C13.197,33.288 3.303,23.314 3.303,23.314 C3.303,23.314 2.0,22.377 2.0,21.0 C2.0,19.942 3.303,18.358 3.303,18.358 L9.0,11.999 Z"/>
</g>
<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
 d="M5.293,19.535 L7.884,16.944 C8.274,16.553 8.907,16.553 9.298,16.944 C9.688,17.334 9.688,17.967 9.298,18.358 L6.706,20.949 C6.316,21.340 5.683,21.340 5.293,20.949 C4.902,20.559 4.902,19.925 5.293,19.535 Z"/>
<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
 d="M10.127,15.253 L11.255,14.125 C11.425,13.955 11.701,13.955 11.871,14.125 C12.41,14.295 12.41,14.571 11.871,14.740 L10.743,15.868 C10.572,16.39 10.297,16.39 10.127,15.868 C9.957,15.699 9.957,15.423 10.127,15.253 Z"/>
</svg>

+ 1 - 1
src/assets/icons/wrong.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1717481430223" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1128" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M916.7 230.5L743 34.6S591.9 266 444.6 501.4C328.6 361.6 229 244.9 229 244.9L107.3 423.7c99.8 59.2 191.4 121.1 273.1 181-116.7 189.3-215.7 359-212.2 384.7 0 0 101.5-141.4 281.9-332.3C669.2 826 800.6 967 800.6 967c0.2-25.7-141.7-205.1-284.1-378.6 110.9-112.5 245.6-237.2 400.2-357.9z" p-id="1129"></path></svg>
+<svg 
 xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink"
 width="43px" height="42px">
<defs>
<filter filterUnits="userSpaceOnUse" id="Filter_0" x="-1px" y="-1px" width="44px" height="43px"  >
                <feOffset in="SourceAlpha" dx="0" dy="2" />
                <feGaussianBlur result="blurOut" stdDeviation="0" />
                <feFlood flood-color="rgb(39, 48, 113)" result="floodOut" />
                <feComposite operator="atop" in="floodOut" in2="blurOut" />
                <feComponentTransfer><feFuncA type="linear" slope="0.4"/></feComponentTransfer>
                <feMerge>
    <feMergeNode/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
            </filter>

</defs>
<g filter="url(#Filter_0)">
<path fill-rule="evenodd"  stroke="rgb(255, 255, 255)" stroke-width="2px" stroke-linecap="butt" stroke-linejoin="miter" fill="rgb(245, 50, 50)"
 d="M31.456,19.166 L37.486,24.825 C39.76,26.316 39.120,28.777 37.586,30.323 L32.366,35.582 C30.832,37.127 28.300,37.170 26.710,35.679 L20.376,29.736 L13.935,35.880 C12.355,37.387 9.838,37.343 8.313,35.782 L3.124,30.468 C1.599,28.906 1.644,26.418 3.224,24.911 L9.180,19.230 L3.727,14.113 C2.137,12.622 2.92,10.160 3.627,8.615 L8.847,3.356 C10.381,1.811 12.913,1.768 14.503,3.259 L20.259,8.660 L26.69,3.118 C27.649,1.611 30.166,1.656 31.690,3.217 L36.879,8.531 C38.404,10.92 38.360,12.580 36.780,14.87 L31.456,19.166 Z"/>
</g>
<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
 d="M5.292,10.535 L7.884,7.943 C8.274,7.553 8.908,7.553 9.298,7.943 C9.689,8.334 9.689,8.968 9.298,9.358 L6.707,11.949 C6.316,12.340 5.683,12.340 5.292,11.949 C4.902,11.559 4.902,10.926 5.292,10.535 Z"/>
<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
 d="M10.127,6.253 L11.255,5.125 C11.425,4.954 11.701,4.954 11.871,5.125 C12.41,5.295 12.41,5.571 11.871,5.741 L10.743,6.869 C10.573,7.39 10.297,7.39 10.127,6.869 C9.957,6.699 9.957,6.423 10.127,6.253 Z"/>
</svg>

+ 11 - 13
src/components/WuIsCorrect/index.vue

@@ -1,23 +1,21 @@
 <script setup lang="ts">
-// const props = defineProps({
-//   correct: {
-//     type: Boolean,
-//     default: false,
-//     required: true,
-//     immediate: true,
-//   },
-// })
+const props = defineProps({
+  correct: {
+    type: Boolean,
+    default: false,
+    required: true,
+    immediate: true,
+  },
+})
 </script>
 
 <template>
-  <div class="flex-center absolute bottom-[8px] right-[9px]">
+  <div class="flex-center absolute bottom-[6px] right-[6px]">
     <transition name="el-fade-in">
-      111
-      <!-- <svg-icon v-if="props.correct" icon-class="correct" class="text-[32px] text-[#2bae68]" /> -->
+      <SvgIcon v-if="props.correct" name="correct" class="text-[32px]" />
     </transition>
     <transition name="el-fade-in">
-      1122
-      <!-- <svg-icon v-if="!props.correct" icon-class="wrong" class="text-[32px] text-[#FF7E7E]" /> -->
+      <SvgIcon v-if="!props.correct" name="wrong" class="text-[32px]" />
     </transition>
   </div>
 </template>

+ 5 - 5
src/pages/cognitiveTasks/PicturePuzzle/Topics.json

@@ -7,11 +7,11 @@
     { "level": 1, "title": "", "choices": ["3/5/1", "3/5/2", "3/5/3", "3/5/4", "3/5/5", "3/5/6", "3/5/7", "3/5/8", "3/5/9"], "tipImg": "3/5/puzzle" }
   ],
   [
-    { "level": 2, "title": "", "choices": ["4/1/1", "4/1/2", "4/1/3", "4/1/4", "4/1/5", "4/1/6", "4/1/7", "4/1/8", "4/1/9", "4/1/10", "4/1/11", "4/1/12", "4/1/13", "4/1/14", "4/1/15", "4/1/15"], "tipImg": "4/1/puzzle" },
-    { "level": 2, "title": "", "choices": ["4/2/1", "4/2/2", "4/2/3", "4/2/4", "4/2/5", "4/2/6", "4/2/7", "4/2/8", "4/2/9", "4/2/10", "4/2/11", "4/2/12", "4/2/13", "4/2/14", "4/2/15", "4/2/15"], "tipImg": "4/2/puzzle" },
-    { "level": 2, "title": "", "choices": ["4/3/1", "4/3/2", "4/3/3", "4/3/4", "4/3/5", "4/3/6", "4/3/7", "4/3/8", "4/3/9", "4/3/10", "4/3/11", "4/3/12", "4/3/13", "4/3/14", "4/3/15", "4/3/15"], "tipImg": "4/3/puzzle" },
-    { "level": 2, "title": "", "choices": ["4/4/1", "4/4/2", "4/4/3", "4/4/4", "4/4/5", "4/4/6", "4/4/7", "4/4/8", "4/4/9", "4/4/10", "4/4/11", "4/4/12", "4/4/13", "4/4/14", "4/4/15", "4/4/15"], "tipImg": "4/4/puzzle" },
-    { "level": 2, "title": "", "choices": ["4/5/1", "4/5/2", "4/5/3", "4/5/4", "4/5/5", "4/5/6", "4/5/7", "4/5/8", "4/5/9", "4/5/10", "4/5/11", "4/5/12", "4/5/13", "4/5/14", "4/5/15", "4/5/15"], "tipImg": "4/5/puzzle" }
+    { "level": 2, "title": "", "choices": ["4/1/1", "4/1/2", "4/1/3", "4/1/4", "4/1/5", "4/1/6", "4/1/7", "4/1/8", "4/1/9", "4/1/10", "4/1/11", "4/1/12", "4/1/13", "4/1/14", "4/1/15", "4/1/16"], "tipImg": "4/1/puzzle" },
+    { "level": 2, "title": "", "choices": ["4/2/1", "4/2/2", "4/2/3", "4/2/4", "4/2/5", "4/2/6", "4/2/7", "4/2/8", "4/2/9", "4/2/10", "4/2/11", "4/2/12", "4/2/13", "4/2/14", "4/2/15", "4/2/16"], "tipImg": "4/2/puzzle" },
+    { "level": 2, "title": "", "choices": ["4/3/1", "4/3/2", "4/3/3", "4/3/4", "4/3/5", "4/3/6", "4/3/7", "4/3/8", "4/3/9", "4/3/10", "4/3/11", "4/3/12", "4/3/13", "4/3/14", "4/3/15", "4/3/16"], "tipImg": "4/3/puzzle" },
+    { "level": 2, "title": "", "choices": ["4/4/1", "4/4/2", "4/4/3", "4/4/4", "4/4/5", "4/4/6", "4/4/7", "4/4/8", "4/4/9", "4/4/10", "4/4/11", "4/4/12", "4/4/13", "4/4/14", "4/4/15", "4/4/16"], "tipImg": "4/4/puzzle" },
+    { "level": 2, "title": "", "choices": ["4/5/1", "4/5/2", "4/5/3", "4/5/4", "4/5/5", "4/5/6", "4/5/7", "4/5/8", "4/5/9", "4/5/10", "4/5/11", "4/5/12", "4/5/13", "4/5/14", "4/5/15", "4/5/16"], "tipImg": "4/5/puzzle" }
   ],
   [
     { "level": 3, "title": "", "choices": ["5/1/1", "5/1/2", "5/1/3", "5/1/4", "5/1/5", "5/1/6", "5/1/7", "5/1/8", "5/1/9", "5/1/10", "5/1/11", "5/1/12", "5/1/13", "5/1/14", "5/1/15", "5/1/16", "5/1/17", "5/1/18", "5/1/19", "5/1/20", "5/1/21", "5/1/22", "5/1/23", "5/1/24", "5/1/25"], "tipImg": "5/1/puzzle" },

+ 13 - 14
src/pages/cognitiveTasks/PicturePuzzle/components/PicturePuzzleChild/index.vue

@@ -26,16 +26,15 @@ const activeIndex_svg = ref(-1)
 const containerWidth_left = 332
 const containerHeight_left = 325
 const containerWidth_right = 332
-const svgNum_left = ref([3, 5, 7, 9])
+const svgNum_left = ref([3, 4, 5])
 const svgNum_right = ref([
-  [6, 2, 9],
-  [8, 4, 25],
-  [10, 5, 49],
-  [12, 7, 81],
+  [5, 2, 9],
+  [7, 4, 16],
+  [8, 5, 25],
 ])
 const countDownVisible = ref(false)
-const countDownVals = ref([2 * 60, 5 * 60, 8 * 60, 10 * 60])
-const levelStrArr = ref(['一', '二', '三', '四'])
+const countDownVals = ref([2 * 60, 4 * 60, 6 * 60])
+const levelStrArr = ref(['一', '二', '三'])
 const showResult = ref(false)
 
 const coordinateSet = ref<Record<string, string>[]>([]) // 坐标集合
@@ -66,8 +65,8 @@ function coordinateSetFn(n: number, boxWidth: number, boxHeight: number) {
     result.push({
       left: `${x / 6.67}vh`,
       top: `${y / 3.75}vw`,
-      width: `${(boxWidth - 6) / 6.67}vh`,
-      height: `${(boxHeight - 6) / 3.75}vw`,
+      width: `${(boxWidth - 4) / 6.67}vh`,
+      height: `${(boxHeight - 4) / 3.75}vw`,
       positionDes: `left_${i + 1}`,
     })
   }
@@ -82,8 +81,8 @@ function coordinateSetFn_right(n: number, m: number, boxWidth: number) {
         result.push({
           left: `${(j * boxWidth + 334) / 6.67}vh`,
           top: `${(i * boxWidth + 160) / 3.75}vw`,
-          width: `${(boxWidth - 6) / 6.67}vh`,
-          height: `${(boxWidth - 6) / 3.75}vw`,
+          width: `${(boxWidth - 4) / 6.67}vh`,
+          height: `${(boxWidth - 4) / 3.75}vw`,
           positionDes: `right_${j * (i + 1) + 1}`,
         })
       }
@@ -244,7 +243,7 @@ defineExpose({
       :key="index"
       :style="box"
       :class="{ '!border-[2px]!border-[#134FA4]': activeIndex_left === index }"
-      class="absolute m-[6px] cursor-pointer overflow-hidden border border-white rounded-[8px] bg-[#f3f3bdCE] shadow-lg hover:shadow-2xl"
+      class="absolute m-[4px] cursor-pointer overflow-hidden border border-white rounded-[8px] bg-[#f3f3bdCE] shadow-lg hover:shadow-2xl"
       @click.stop="changeImg(0, index)"
     />
     <template v-if="svgNum_right && svgNum_right[showData.level! - 1]">
@@ -253,7 +252,7 @@ defineExpose({
         :key="index"
         :style="boxes_right[index]"
         :class="{ '!border-[2px]!border-[#134FA4]': activeIndex_right === index }"
-        class="absolute m-[6px] cursor-pointer overflow-hidden border border-white rounded-[8px] bg-[#f3f3bdCE] text-[#FFFFFF00] shadow-lg hover:shadow-2xl"
+        class="absolute m-[4px] cursor-pointer overflow-hidden border border-white rounded-[8px] bg-[#f3f3bdCE] text-[#FFFFFF00] shadow-lg hover:shadow-2xl"
         @click.stop="changeImg(1, index)"
       >
         {{ svg }}
@@ -265,7 +264,7 @@ defineExpose({
       :src="imgUrl(item)"
       :style="coordinateSet[index]"
       :class="{ '!border-[6px]': activeIndex_svg === index }"
-      class="z49 m-[6px] cursor-pointer overflow-hidden border-white rounded-[10px] !absolute hover:border-[4px] hover:shadow-2xl"
+      class="z49 m-[4px] cursor-pointer overflow-hidden border-white rounded-[10px] !absolute hover:border-[4px] hover:shadow-2xl"
       @click.stop="changeImg(2, index)"
     />
 

+ 58 - 60
src/pages/cognitiveTasks/PicturePuzzle/index.vue

@@ -6,21 +6,21 @@
  * 编写者: JutarryWu
  */
 import { shuffle } from 'lodash-es'
-import { showToast } from 'vant'
+import { showSuccessToast } from 'vant'
 import Topics from './Topics.json'
 import PicturePuzzleChild from './components/PicturePuzzleChild/index.vue'
 import type { IGameBaseData } from '@/typing'
-// import AchievementAPI from '@/api/tester/rehabilitation/training/achievement'
+import { formatSeconds } from '@/utils'
+import GameAPI, { type GameResultVO, type GameVO } from '@/api/game'
 
 const router = useRouter()
-const subjectInfo = ref({
-  name: '图片拼图',
-})
+const subjectInfo = ref<GameVO>({})
 const PicturePuzzleChildRef = ref()
 const TopicsArr = Topics.map((item) => {
   item = shuffle(item)
   return item
 })
+let gameStartTime = null // 游戏开始时间
 const currentLevel = ref(0) // 当前难度
 const tempIndex = ref(0) // 当前难度下的次序
 const baseData: IGameBaseData = reactive({
@@ -34,57 +34,42 @@ const baseData: IGameBaseData = reactive({
 const scoreList = [10, 30, 60, 100]
 
 function endGame() {
-  showToast('本次训练已结束')
-  // if (planInfo.value) {
-  //   // 记录游戏结束时间
-  //   baseData.endTime = Date.now()
-  //
-  //   // 计算游戏得分
-  //   const totalScore = baseData.totalScore
-  //
-  //   const gameTime = ((performance.now() - gameStartTime) / 1000).toFixed(2)
-  //
-  //   const { gameId, gameName, id: planId } = planInfo.value
-  //   const data = {
-  //     finish: '1',
-  //     gameId,
-  //     gameName,
-  //     paramList: [
-  //       {
-  //         code: 'score',
-  //         name: '得分',
-  //         value: totalScore,
-  //       },
-  //       {
-  //         code: 'gameTime',
-  //         name: '游戏时长',
-  //         value: `${gameTime}s`,
-  //       },
-  //       {
-  //         code: 'maxLevel',
-  //         name: '最终难度',
-  //         value: currentLevel.value + 1,
-  //       },
-  //       // ,
-  //       // {
-  //       //   code: 'levelTable',
-  //       //   name: '难度表',
-  //       //   value: ''
-  //       // }
-  //     ],
-  //     planId,
-  //     gamelevel: currentLevel.value + 1,
-  //     score: totalScore,
-  //     userId: userStore.user.id,
-  //   }
-  //
-  //   AchievementAPI.add(data).then(() => {
-  //     showToast.success('本次训练已结束')
-  //   })
-  // }
-  // else {
-  //   showToast('本次训练已结束')
-  // }
+  // 计算游戏得分
+  const totalScore = baseData.totalScore
+  const gameTime = formatSeconds(Math.ceil(performance.now() - gameStartTime))
+
+  const data: GameResultVO = {
+    finish: '1',
+    gameId: subjectInfo.value.id,
+    gameName: subjectInfo.value.name,
+    paramList: [
+      {
+        code: 'score',
+        name: '得分',
+        value: totalScore,
+      },
+      {
+        code: 'gameTime',
+        name: '游戏时长',
+        value: gameTime,
+      },
+      {
+        code: 'maxLevel',
+        name: '最终难度',
+        value: currentLevel.value + 1,
+      },
+    ],
+    userId: sessionStorage.getItem('userId'),
+  }
+  GameAPI.add(data).then(() => {
+    showSuccessToast({
+      className: 'rotate-toast',
+      message: '本次训练已结束',
+    })
+    setTimeout(() => {
+      router.go(-1)
+    }, 1300)
+  })
 }
 
 function nextLevel() {
@@ -105,8 +90,7 @@ function nextIndex() {
   }
   else {
     if (
-      baseData.dataList[baseData.dataList.length - 1].correct
-      === baseData.dataList[baseData.dataList.length - 2].correct
+      baseData.dataList[baseData.dataList.length - 1].correct === baseData.dataList[baseData.dataList.length - 2].correct
     ) {
       if (baseData.dataList[baseData.dataList.length - 1].correct) {
         nextLevel()
@@ -134,15 +118,19 @@ function setChildData() {
 function endLoop(data: any) {
   if (data.correct) {
     baseData.totalScore += scoreList[currentLevel.value]
-    baseData.maxLevel
-      = baseData.maxLevel && baseData.maxLevel > currentLevel.value ? baseData.maxLevel : currentLevel.value
+    baseData.maxLevel = baseData.maxLevel && baseData.maxLevel > currentLevel.value ? baseData.maxLevel : currentLevel.value
   }
   baseData.dataList.push(data)
   nextIndex()
 }
 
 async function exec() {
+  const temp = sessionStorage.getItem('subjectInfo')
+  if (temp) {
+    subjectInfo.value = JSON.parse(temp)
+  }
   baseData.beginTime = Date.now()
+  gameStartTime = performance.now()
 }
 
 onMounted(() => {
@@ -171,5 +159,15 @@ onMounted(() => {
   background-image: url('/static/image/game/bg-puzzle.png');
   background-size: 100% 100%;
   background-position: center center;
+
+  :deep(.van-nav-bar) {
+    .van-nav-bar__title {
+      color: #fff;
+    }
+
+    .van-icon {
+      color: #fff;
+    }
+  }
 }
 </style>

+ 3 - 2
src/pages/cognitiveTasks/main/index.vue

@@ -23,7 +23,7 @@ const urlList = [
 ]
 
 function startTest() {
-  router.push(urlList[Number(subjectInfo.value.id) - 1])
+  router.push(urlList[Number(taskId.value) - 1])
 }
 
 async function exec() {
@@ -31,7 +31,8 @@ async function exec() {
   userId.value = route.query.userId as string
   GameAPI.findById(taskId.value).then((res) => {
     subjectInfo.value = res.data
-    sessionStorage.setItem('userId', res.data.id.toString())
+    sessionStorage.setItem('userId', userId.value)
+    sessionStorage.setItem('subjectInfo', JSON.stringify(res.data))
   })
 }
 

+ 81 - 55
src/pages/cognitiveTasks/spatialOrientationAbility/index.vue

@@ -6,8 +6,8 @@
  * 编写者: JutarryWu
  */
 import { shuffle } from 'lodash-es'
-import { Decimal } from 'decimal.js'
-import { generateRandomNumbers } from '@/utils'
+import { showSuccessToast } from 'vant'
+import { formatSeconds, generateRandomNumbers } from '@/utils'
 import Url_forest from '@/assets/images/task/spatialOrientationAbility/forest.png'
 import Url_hospital from '@/assets/images/task/spatialOrientationAbility/hospital.png'
 import Url_lakes from '@/assets/images/task/spatialOrientationAbility/lakes.png'
@@ -18,6 +18,7 @@ import Url_car from '@/assets/images/task/spatialOrientationAbility/car.png'
 import Url_trafficLight from '@/assets/images/task/spatialOrientationAbility/trafficLight.png'
 import Url_stop from '@/assets/images/task/spatialOrientationAbility/stop.png'
 import Url_house from '@/assets/images/task/spatialOrientationAbility/house.png'
+import GameAPI, { type GameResultVO, type GameVO, type ResultLevel } from '@/api/game'
 
 const router = useRouter()
 
@@ -36,21 +37,19 @@ interface IData {
   angle?: string
 }
 
-const subjectInfo = ref({
-  name: '空间定向训练',
-})
-
+const subjectInfo = ref<GameVO>({})
 const showCountDown = ref(false) // 显示倒计时
 const showCountDownBottom = ref(false) // 显示底部倒计时
 const showSubmit = ref(false) // 显示画布
 const isSubmit = ref(false) // 是否提交进程中
 
-const levelNum = [3, 5, 7] // 正式测试:3个难度等级需要呈现的刺激目标数
-const tryCount = 3 // 正式测试:每个难度等级需要进行的试次数
+const levelNum = [3, 4, 5] // 正式测试:3个难度等级需要呈现的刺激目标数
+const tryCount = 1 // 正式测试:每个难度等级需要进行的试次数
 const currentIndex = ref(0) // 当前试次索引值:练习模式2次最大为1, 正式测试45次
 const itemAngle = ref(0) // 选项物品实际角度
 const roundSliderVal = ref(0) // 圆形滑杆产生的值(即:用户操作实际角度)
 const offsetAngle = ref(0) // 角度偏差值(用户绘制时产生的实际偏差值)
+let gameStartTime = null // 游戏开始时间
 
 const showDataArr = ref<IData[][]>([]) // 伪随机数组
 let arrDataDemo: IData[] = [
@@ -80,7 +79,7 @@ const secondObj = ref<IData>()
 const topCoordinate = ref<IData>()
 const thirdObj = ref<IData>()
 const thirdCoordinate = ref<IData>()
-const resultDataArr = ref([]) // 存放结果数组
+const levelList = ref<ResultLevel[]>([]) // 存放结果数组
 
 /**
  * 生成不重叠随机点
@@ -207,18 +206,44 @@ submitAngle() {
   // console.log('用户操作逻辑角度值: ', roundSliderVal)
   // console.log('角度偏差值: ', offsetAngle)
   // console.log('---------------------------------------------------------------')
-  resultDataArr.value.push({
-    // level: showDataArr.value[currentIndex.value][0].level,
+  levelList.value.push({
+    level: showDataArr.value[currentIndex.value][0].level,
+    levelParamList: [
+      {
+        code: 'itemAngle',
+        name: '物品实际角度值',
+        value: itemAngle.value.toFixed(2),
+      },
+      {
+        code: 'rotationAngle',
+        name: '用户操作逻辑角度值',
+        value: Math.abs(roundSliderVal.value).toFixed(2),
+      },
+      {
+        code: 'offsetAngle',
+        name: '角度偏差值',
+        value: offsetAngle.value,
+      },
+      {
+        code: 'CenterName',
+        name: '中心点物体',
+        value: centerCoordinate.value.name,
+      },
+      {
+        code: 'BaseName',
+        name: '顶点物体',
+        value: topCoordinate.value.name,
+      },
+      {
+        code: 'TargetName',
+        name: '目标物体',
+        value: thirdCoordinate.value.name,
+      },
+    ],
     // onceStartTime: dayjs(new Date(showDataArr.value[currentIndex.value].onceStartTime)).format('YY-mm-dd HH:MM:SS'),
     // onceEndTime: dayjs(new Date(showDataArr.value[currentIndex.value].onceEndTime)).format('YY-mm-dd HH:MM:SS'),
     // reactionTime: (showDataArr.value[currentIndex.value].onceEndTime - showDataArr.value[currentIndex.value].onceStartTime).toFixed(2),
-    itemAngle: itemAngle.value.toFixed(2), // 物品实际角度值
-    rotationAngle: Math.abs(roundSliderVal.value).toFixed(2), // 用户操作逻辑角度值
-    offsetAngle: offsetAngle.value, // 角度偏差值
-    CenterName: centerCoordinate.value.name, // 中心点物体
-    BeseName: topCoordinate.value.name, // 顶点物体
-    TargetName: thirdCoordinate.value.name, // 目标物体
-    randomArr: dealArrDataPoint(showDataArr.value[currentIndex.value], centerCoordinate.value), // 物品随机坐标位置
+    // randomArr: dealArrDataPoint(showDataArr.value[currentIndex.value], centerCoordinate.value), // 物品随机坐标位置
   })
   currentIndex.value++
   // curPercentage.value = Number(roundToFixed((currentIndex.value / showDataArr.value.length), 2))
@@ -226,32 +251,28 @@ submitAngle() {
     resetGame()
   }
   else {
-    // taskEndTime.value = Date.now()
-    // console.log(`本次 空间定向任务共计耗时:${taskEndTime.value - taskStartTime.value}ms`, resultDataArr.value)
-    // $http.post(
-    //   `/cognize/SpatialOrientation`,
-    //   {
-    //     userId: userId,
-    //     userResponseRecords: resultDataArr,
-    //     testPlanId: $route.query.testPlanId || '',
-    //     taskStartTime: dateFormat('YY-mm-dd HH:MM:SS', new Date(taskStartTime)),
-    //     taskEndTime: dateFormat('YY-mm-dd HH:MM:SS', new Date(taskEndTime)),
-    //   },
-    //   (res) => {
-    //     console.log('res: ', res)
-    //     if (res && res.code === '200') {
-    //       // 跳转新测试结果页面
-    //       goTestResult(res.data)
-    //     }
-    //     else {
-    //       $toast.fail(res?.msg)
-    //     }
-    //
-    //     setTimeout(() => {
-    //       isSubmit = false
-    //     }, 500)
-    //   },
-    // )
+    const gameTime = formatSeconds(Math.ceil(performance.now() - gameStartTime))
+
+    const data: GameResultVO = {
+      finish: '1',
+      gameId: subjectInfo.value.id,
+      gameName: subjectInfo.value.name,
+      paramList: [
+        {
+          code: 'gameTime',
+          name: '游戏时长',
+          value: gameTime,
+        },
+      ],
+      levelList: levelList.value,
+      userId: sessionStorage.getItem('userId'),
+    }
+    GameAPI.add(data).then(() => {
+      showSuccessToast('本次训练已结束')
+      setTimeout(() => {
+        router.go(-1)
+      }, 1300)
+    })
   }
 }
 
@@ -260,17 +281,17 @@ submitAngle() {
  * @param arrData
  * @param centerPoint
  */
-function dealArrDataPoint(arrData: IData[], centerPoint: IData) {
-  const xAxisOffset = new Decimal(centerPoint.xAxis)
-  const yAxisOffset = new Decimal(centerPoint.yAxis)
-  return arrData.map((item) => {
-    const xAxis = new Decimal(item.xAxis)
-    const yAxis = new Decimal(item.yAxis)
-    item.xAxis = xAxis.minus(xAxisOffset).toNumber()
-    item.yAxis = yAxis.minus(yAxisOffset).toNumber()
-    return item
-  })
-}
+// function dealArrDataPoint(arrData: IData[], centerPoint: IData) {
+//   const xAxisOffset = new Decimal(centerPoint.xAxis)
+//   const yAxisOffset = new Decimal(centerPoint.yAxis)
+//   return arrData.map((item) => {
+//     const xAxis = new Decimal(item.xAxis)
+//     const yAxis = new Decimal(item.yAxis)
+//     item.xAxis = xAxis.minus(xAxisOffset).toNumber()
+//     item.yAxis = yAxis.minus(yAxisOffset).toNumber()
+//     return item
+//   })
+// }
 
 function resetGame() {
   showCountDown.value = false
@@ -291,9 +312,14 @@ function resetGame() {
 function exec() {
   showCountDown.value = false
   showCountDownBottom.value = true
+  gameStartTime = performance.now()
 }
 
 onMounted(() => {
+  const temp = sessionStorage.getItem('subjectInfo')
+  if (temp) {
+    subjectInfo.value = JSON.parse(temp)
+  }
   showCountDown.value = true
   initImgDataList()
 })

+ 7 - 0
src/styles/app.less

@@ -115,3 +115,10 @@ html.dark {
     transform: translate(-50%, -50%);
   }
 }
+
+.van-toast {
+  &.rotate-toast {
+    transform: rotate(90deg) translateX(50%);
+    transform-origin: right top;
+  }
+}

+ 21 - 0
src/utils/index.ts

@@ -14,6 +14,27 @@ function generateRandomNumbers(count: number, max: number) {
   return Array.from(numbers)
 }
 
+/**
+ * 毫秒数转时分秒
+ * @param fmtSeconds
+ */
+function formatSeconds(fmtSeconds: number) {
+  const hours = Math.floor(fmtSeconds / (1000 * 60 * 60))
+  const minutes = Math.floor((fmtSeconds % (1000 * 60 * 60)) / (1000 * 60))
+  const seconds = Math.floor((fmtSeconds % (1000 * 60)) / 1000)
+
+  if (hours === 0) {
+    if (hours === 0 && minutes === 0) {
+      return `${seconds.toString().padStart(2, '0')}秒`
+    }
+    else {
+      return `${minutes.toString().padStart(2, '0')}分 ${seconds.toString().padStart(2, '0')}秒`
+    }
+  }
+  return `${hours.toString().padStart(2, '0')}时 ${minutes.toString().padStart(2, '0')}分 ${seconds.toString().padStart(2, '0')}秒`
+}
+
 export {
   generateRandomNumbers,
+  formatSeconds,
 }