123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- <script setup lang="ts">
- /*
- * 组件名: PictureNaming
- * 组件用途: 图片命名
- * 创建日期: 2024/8/20
- * 编写者: JutarryWu
- */
- import { shuffle } from 'lodash-es'
- import { showSuccessToast } from 'vant'
- import Topics from './Topics.json'
- import GameAPI, { type GameResultVO, type GameVO } from '@/api/game'
- interface IData {
- choices?: string[]
- img?: string
- seconds?: number
- correct?: string
- isRight?: boolean
- checked?: boolean
- score?: number
- reactionTime?: number
- }
- const router = useRouter()
- const VoiceImpRef = ref()
- const subjectInfo = ref<GameVO>({})
- const showCountDown = ref(true) // 显示倒计时
- const showImg = ref(true) // 显示图片
- const showText = ref(false) // 显示文字
- const musicFlag = ref(true) // 页面音乐开关
- const userCanClickFlag = ref(false) // 用户是否可以点击开关
- const showDataArr = ref<IData[]>([])
- const seconds = [3000, 2000, 1500]
- const currentIndex = ref(0) // 当前索引
- const selectiveIndex = ref(-1) // 用户选择的索引
- const textTimeout = ref<NodeJS.Timeout | null>(null)
- const imgTimeout = ref<NodeJS.Timeout | null>(null)
- const onceTimer = ref<NodeJS.Timeout | null>(null)
- let onceStart = 0 // 试次开始时间
- function initData() {
- for (let i = 0; i < 3; i++) {
- const tempArr = []
- for (let j = 0; j < 15; j++) {
- tempArr.push({
- choices: shuffle(Topics[i * 15 + j]),
- img: `/static/image/game/name/${i * 15 + j}.png`,
- seconds: seconds[i], // 图片显示时长
- correct: Topics[i * 15 + j][0], // 正确答案
- isRight: false, // 是否正确
- score: 0, // 正确: 1, 错误:0
- reactionTime: 4000, // 反应时间(ms)
- checked: false, // 是否选过
- })
- }
- showDataArr.value = [...showDataArr.value, ...shuffle(tempArr)]
- }
- }
- function musicClick(flag: number) {
- musicFlag.value = !musicFlag.value
- if (musicFlag.value) {
- VoiceImpRef.value.videoPlay('click')
- VoiceImpRef.value.videoPlay('bg', flag)
- }
- else {
- VoiceImpRef.value.videoPause('bg')
- }
- }
- function choiceClick(item: string, index: number) {
- if (!userCanClickFlag.value) {
- return
- }
- if (!showDataArr.value[currentIndex.value].checked) {
- showDataArr.value[currentIndex.value].checked = true
- selectiveIndex.value = index
- if (item === showDataArr.value[currentIndex.value].correct) {
- showDataArr.value[currentIndex.value].isRight = true
- showDataArr.value[currentIndex.value].score = 1
- showDataArr.value[currentIndex.value].reactionTime = Number((performance.now() - onceStart).toFixed(3))
- if (musicFlag.value) {
- VoiceImpRef.value.videoPlay('right')
- }
- }
- else {
- if (musicFlag.value) {
- VoiceImpRef.value.videoPlay('error')
- }
- }
- clearTimeout(textTimeout.value)
- clearTimeout(imgTimeout.value)
- clearTimeout(onceTimer.value)
- setTimeout(() => {
- userCanClickFlag.value = false
- showText.value = false
- showImg.value = false
- currentIndex.value++
- nextOnce()
- }, 900)
- }
- }
- function nextOnce() {
- if (currentIndex.value < showDataArr.value.length) {
- onceStart = performance.now()
- selectiveIndex.value = -1
- showImg.value = true
- // 隐藏图片
- imgTimeout.value = setTimeout(() => {
- showImg.value = false
- showText.value = true
- userCanClickFlag.value = true
- // 隐藏文字
- textTimeout.value = setTimeout(() => {
- showText.value = false
- }, 3000)
- }, showDataArr.value[currentIndex.value].seconds)
- // 如果用户未点击,自动清除定时器,进入下一个试次
- onceTimer.value = setTimeout(() => {
- userCanClickFlag.value = false
- showText.value = false
- showImg.value = false
- clearTimeout(textTimeout.value)
- clearTimeout(imgTimeout.value)
- clearTimeout(onceTimer.value)
- currentIndex.value++
- nextOnce()
- }, 7000)
- }
- else {
- submit()
- }
- }
- function submit() {
- let correctNum = 0
- let wrongNum = 0
- let accuracy = 0
- let totalScore = 0
- let averageReactionTimeForCorrect = 0
- showDataArr.value.forEach((item) => {
- if (item.isRight) {
- correctNum++
- totalScore++
- averageReactionTimeForCorrect += item.reactionTime
- }
- })
- wrongNum = 45 - correctNum
- accuracy = Number((correctNum / 45).toFixed(4))
- averageReactionTimeForCorrect = Number((averageReactionTimeForCorrect / correctNum).toFixed(3))
- const data: GameResultVO = {
- finish: '1',
- gameId: subjectInfo.value.id,
- gameName: subjectInfo.value.name,
- paramList: [
- {
- code: 'correctNum',
- name: '正确数',
- value: correctNum,
- },
- {
- code: 'wrongNum',
- name: '错误数',
- value: wrongNum,
- },
- {
- code: 'accuracy',
- name: '正确率',
- value: accuracy,
- },
- {
- code: 'totalScore',
- name: '得分',
- value: totalScore,
- },
- {
- code: 'averageReactionTimeForCorrect',
- name: '正确点击的反应时长均值',
- value: `${averageReactionTimeForCorrect}ms`,
- },
- ],
- userId: sessionStorage.getItem('userId'),
- }
- GameAPI.add(data).then(() => {
- showSuccessToast({
- className: 'rotate-toast',
- message: '本次训练已结束',
- })
- setTimeout(() => {
- router.go(-1)
- }, 1300)
- })
- }
- function exec() {
- showCountDown.value = false
- nextOnce()
- }
- onMounted(() => {
- const temp = sessionStorage.getItem('subjectInfo')
- if (temp) {
- subjectInfo.value = JSON.parse(temp)
- }
- if (musicFlag.value) {
- VoiceImpRef.value.videoPlay('bg')
- }
- initData()
- })
- onBeforeUnmount(() => {
- clearInterval(onceTimer.value)
- })
- </script>
- <template>
- <section class="app-container">
- <van-nav-bar class="self-nav-bar" :title="subjectInfo.name" left-arrow @click-left="router.go(-1)">
- <template #right>
- <img v-if="musicFlag" src="/static/image/game/video/play.png" alt="" class="h-[34px] w-[34px]" @click="musicClick(0)">
- <img v-else src="/static/image/game/video/stop.png" alt="" class="h-[34px] w-[34px]" @click="musicClick(1)">
- </template>
- </van-nav-bar>
- <count-down v-if="showCountDown" :time="5" color="#fff" @end-count-down="exec" />
- <VoiceImp ref="VoiceImpRef" />
- <div v-if="!showCountDown && currentIndex < showDataArr.length" class="absolute-center flex-column h-[78%] w-[100%]">
- <div class="center-div flex-center ml-[4%] h-[390px] w-[92%]">
- <img v-if="showImg" :src="showDataArr[currentIndex].img" alt="" class="h-[50%] w-[58%]">
- </div>
- <div class="mt-[20px] h-[80px] w-[100%] flex-row justify-around text-center text-[42px] text-[#3A3A3AE2] line-height-[80px]">
- <div
- v-for="(item, index) in showDataArr[currentIndex].choices"
- :key="index"
- class="text-area relative h-[80px] w-[80px] rounded-[20px] text-center line-height-[80px] shadow-lg"
- @click="choiceClick(item, index)"
- >
- <span v-if="showText">{{ item }}</span>
- <WuIsCorrect
- v-if="selectiveIndex === index && showDataArr[currentIndex].checked"
- :correct="showDataArr[currentIndex].isRight"
- />
- </div>
- </div>
- </div>
- </section>
- </template>
- <style scoped lang="less">
- .app-container {
- background-image: url('/static/image/game/bg-pic-naming.png');
- background-size: 100% 100%;
- background-repeat: no-repeat;
- background-position: center center;
- :deep(.van-nav-bar) {
- .van-nav-bar__title {
- color: #fff;
- }
- .van-icon {
- color: #fff;
- }
- }
- .center-div {
- background-image: url('/static/image/game/bg-center.png');
- background-size: 100% 100%;
- background-repeat: no-repeat;
- background-position: center center;
- }
- .text-area {
- background-image: url('/static/image/game/bg-text.png');
- background-size: 100% 100%;
- background-repeat: no-repeat;
- background-position: center center;
- }
- }
- </style>
|