Files
reader-star/apps/admin/src/views/user/game/index.vue

236 lines
8.4 KiB
Vue
Raw Normal View History

<!-- eslint-disable no-console -->
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed, onMounted, ref, watch } from 'vue'
import CompetitionLayout from '@/components/custom/user/CompetitionLayout.vue'
import { useRouterPush } from '@/hooks/common/router'
import { fetchGetGameStatistics, fetchGetQuestionDetail, fetchSubmitQuestionResult } from '@/service/api/game'
import { useActivityInfoStore } from '@/store/modules/activityinfo'
import { useCompetitionStore } from '@/store/modules/competition'
import QuestionRenderer from '../modules/QuestionRenderer.vue'
const { routerPushByKey } = useRouterPush()
const store = useCompetitionStore()
const activityInfoStore = useActivityInfoStore()
const { activityId, groupId, activityInfo } = storeToRefs(activityInfoStore)
const currentQuestionInfo = computed(() => store.currentQuestionInfo)
const currentQuestionDetail = computed(() => store.currentQuestionDetail)
const loading = ref(false)
// 本地倒计时控制(为了在页面上显示“开始答题”还是倒计时)
const isStarted = ref(false)
console.log(activityInfo.value, 'activityInfo.value')
console.log(currentQuestionInfo.value, 'currentQuestionInfo.value')
console.log(currentQuestionDetail.value, 'currentQuestionDetail.value')
onMounted(async () => {
await fetchGetQuestionDetailData()
})
// 监听倒计时,结束时自动跳转
watch(() => store.timeLeft, (newVal) => {
if (newVal === 0 && isStarted.value) {
store.stopTimer()
routerPushByKey('user_analysis')
}
})
// 优先使用 store 中的 API 数据,否则回退到 Mock 数据
const template = computed(() => store.currentQuestionInfo?.UIType)
const content = computed(() => {
if (store.currentQuestionDetail) {
const { Name, ImageUrl } = store.currentQuestionDetail
return {
title: Name,
titleImage: ImageUrl,
}
}
return null
})
const formattedTime = computed(() => {
const time = store.timeLeft
const m = Math.floor(time / 60)
const s = time % 60
const mm = m < 10 ? `0${m}` : m
const ss = s < 10 ? `0${s}` : s
return `倒计时 ${mm}:${ss}`
})
// Mock chart data
const chartData = [
{ group: '第一组', total: 42, correct: 36 },
{ group: '第二组', total: 20, correct: 16 },
{ group: '第三组', total: 19, correct: 19 },
{ group: '第四组', total: 17, correct: 14 },
]
function handleBack() {
store.stopTimer()
routerPushByKey('user_draw')
}
/**
* 获取题目详情
*/
async function fetchGetQuestionDetailData() {
if (store.currentQuestionInfo) {
loading.value = true
try {
const currentActivityId = String(activityId.value || '')
const questionId = store.currentQuestionInfo.QuestionID || (store.currentQuestionInfo as any).Id
const { data: detailData } = await fetchGetQuestionDetail(currentActivityId, questionId)
console.log(detailData, 'detailData')
if (detailData && detailData.data) {
store.setCurrentQuestionDetail(detailData.data)
}
}
catch (error) {
console.error('Failed to fetch question detail', error)
window.$message?.error('获取题目详情失败')
}
finally {
loading.value = false
}
}
}
async function handleNext() {
// 点击开始答题
if (!isStarted.value) {
isStarted.value = true
store.startTimer(store.currentQuestionInfo?.QuestionTime || 10)
// 提交题目使用记录
try {
if (store.currentQuestionInfo && store.currentQuestionDetail) {
const params: Api.Competition.QuestionAddParams = {
RoomID: activityInfo.value?.RoomID || 0,
MainID: activityId.value || 0,
TeamGroupID: groupId.value || 0,
QuestionID: store.currentQuestionInfo.ID, // 使用列表中的 ID
QuestionDetaiID: store.currentQuestionDetail.Id || 0, // 详情 ID
}
console.log(params, 'params')
await fetchSubmitQuestionResult(params)
await fetchGetGameStatisticsData(params)
}
}
catch (error) {
console.error('Failed to submit question result', error)
// 不阻塞流程,仅记录错误
}
}
else {
// 备用逻辑:如果已经在答题中(理论上不会触发,因为上面已经跳转),直接跳转
store.stopTimer()
routerPushByKey('user_analysis')
}
}
/** 获取游戏现场统计结果 */
async function fetchGetGameStatisticsData(_params: Api.Competition.QuestionAddParams) {
if (store.currentQuestionInfo && store.currentQuestionDetail) {
try {
const { data } = await fetchGetGameStatistics(_params)
console.log(data, 'data')
}
catch (error) {
console.error('Failed to fetch game statistics', error)
window.$message?.error('获取游戏现场统计结果失败')
}
}
}
</script>
<template>
<CompetitionLayout
action-position="top" :show-back="false" :show-next="true"
:title="store.currentQuestionInfo?.ActitvityQuestionName || ''" back-btn-text="返回"
:next-btn-text="isStarted ? formattedTime : '开始答题'" :next-disabled="false" btn-theme="light" @back="handleBack"
@next="handleNext"
>
<div class="relative h-full w-full flex flex-col items-center overflow-hidden px-12 font-sans">
<!-- 题目说明 -->
<!-- <p>{{ store.currentQuestionInfo.ActitvityQuestionName }}</p> -->
<!-- 题目内容区域 -->
<div v-if="template && content" class="w-full flex flex-1 items-center justify-center overflow-hidden py-4">
<div class="max-h-full max-w-full flex items-center justify-center">
<QuestionRenderer :template="template" :content="content" class="origin-center scale-150 transform" />
</div>
</div>
<!-- 隐藏的 Next 按钮 (方便演示右上角小区域) -->
<div class="absolute right-0 top-0 z-50 h-20 w-20 cursor-pointer" title="Next Step (Debug)" @click="handleNext">
<div class="h-full w-full flex items-center justify-center">
<div class="h-6 w-6 rotate-45 bg-blue-500">
<icon-ic:baseline-arrow-right-alt class="text-icon text-white" />
</div>
</div>
</div>
<!-- 底部图表区域 -->
<div
class="relative z-20 h-64 max-w-5xl w-full flex flex-col shrink-0 border-2 border-blue-300 rounded-xl bg-white/90 p-4 shadow-lg backdrop-blur"
>
<!-- 图表标题栏 -->
<div class="mb-4 flex items-center justify-between px-2">
<div class="flex items-center gap-2">
<div class="h-3 w-3 rotate-45 bg-orange-400" />
<span class="text-lg text-blue-800 font-bold">答题进度</span>
</div>
<div class="flex gap-6 text-sm">
<div class="flex items-center gap-2">
<div class="h-3 w-3 rounded-full bg-blue-400" />
<span class="text-gray-600">答题数量</span>
</div>
<div class="flex items-center gap-2">
<div class="h-3 w-3 rounded-full bg-orange-300" />
<span class="text-gray-600">正确数量</span>
</div>
</div>
</div>
<!-- 柱状图 -->
<div class="flex flex-1 items-end justify-around px-8 pb-2">
<div v-for="item in chartData" :key="item.group" class="w-16 flex flex-col items-center gap-2">
<div class="h-32 flex items-end gap-2">
<!-- Blue Bar -->
<div
class="relative w-6 rounded-t-md from-blue-500 to-blue-300 bg-gradient-to-t transition-all duration-1000"
:style="{ height: `${item.total * 2}px` }"
>
<span class="absolute left-1/2 text-blue-800 font-bold -top-6 -translate-x-1/2">{{ item.total
}}</span>
</div>
<!-- Orange Bar -->
<div
class="relative w-6 rounded-t-md from-orange-400 to-orange-200 bg-gradient-to-t transition-all duration-1000"
:style="{ height: `${item.correct * 2}px` }"
>
<span class="absolute left-1/2 text-orange-600 font-bold -top-6 -translate-x-1/2">{{ item.correct
}}</span>
</div>
</div>
<div class="mt-2 text-blue-800 font-bold">
{{ item.group }}
</div>
</div>
</div>
</div>
</div>
</CompetitionLayout>
</template>
<style scoped>
/* Question Renderer Override if needed */
:deep(.question-content) {
font-size: 4rem;
color: #ef4444;
/* red-500 */
font-weight: bold;
}
</style>