feat(results): 新增实时结果列表和详情页面,支持评委查看和评分
- 添加 results_results-list 和 results_results-detail 路由及页面 - 实现比赛列表展示,支持跳转到实时结果详情 - 在结果详情页面添加分组和题目切换功能 - 集成游戏统计 API,实时获取队伍答题数据 - 添加 TeamResultCard 和 GroupTabs 可复用组件 - 更新国际化配置和类型定义
This commit is contained in:
@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { NImage } from 'naive-ui'
|
||||
import { computed, onBeforeUnmount, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import CompetitionLayout from '@/components/custom/user/CompetitionLayout.vue'
|
||||
import { useRouterPush } from '@/hooks/common/router'
|
||||
import { fetchCheckNextStep, fetchGetCurrentQuestion } from '@/service/api/game'
|
||||
import { fetchCheckNextStep, fetchGetCurrentQuestion, fetchGetGameStatistics } from '@/service/api/game'
|
||||
import { useCompetitionStore } from '@/store/modules/competition'
|
||||
|
||||
const store = useCompetitionStore()
|
||||
@ -15,56 +16,17 @@ const groupsId = computed(() => route.query?.groupsId as string)
|
||||
const teamId = computed(() => route.query?.teamId as string)
|
||||
const teamIds = computed(() => route.query?.teamIds as string)
|
||||
const currentQuestionMainInfo = computed(() => store.currentQuestionMainInfo)
|
||||
const currentQuestionDetail = computed(() => store.currentQuestionDetail)
|
||||
|
||||
const TeamGroup_QuestionID = computed(() => currentQuestionMainInfo.value?.TeamGroup_QuestionID || 0)
|
||||
const QuestionID = computed(() => currentQuestionMainInfo.value?.Activity_Question.ID || 0)
|
||||
const QuestionDetaiID = computed(() => currentQuestionDetail.value?.Id || 0)
|
||||
|
||||
const zoomedImage = ref<string | null>(null)
|
||||
const isCompleted = ref(false)
|
||||
const showNextBtn = ref(false)
|
||||
|
||||
// Mock team data
|
||||
const teams = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '第一队',
|
||||
writtenCount: 16,
|
||||
correctCount: 13,
|
||||
score: 13,
|
||||
manualScore: null as number | null,
|
||||
image: 'https://via.placeholder.com/300x400?text=Team+1+Answer',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '第二队',
|
||||
writtenCount: 15,
|
||||
correctCount: 12,
|
||||
score: 12,
|
||||
manualScore: null as number | null,
|
||||
image: 'https://via.placeholder.com/300x400?text=Team+2+Answer',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '第三队',
|
||||
writtenCount: 18,
|
||||
correctCount: 15,
|
||||
score: 15,
|
||||
manualScore: null as number | null,
|
||||
image: 'https://via.placeholder.com/300x400?text=Team+3+Answer',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '第四队',
|
||||
writtenCount: 14,
|
||||
correctCount: 10,
|
||||
score: 10,
|
||||
manualScore: null as number | null,
|
||||
image: 'https://via.placeholder.com/300x400?text=Team+4+Answer',
|
||||
},
|
||||
])
|
||||
|
||||
function handleZoom(img: string) {
|
||||
zoomedImage.value = img
|
||||
}
|
||||
const teams = ref<any[]>([])
|
||||
|
||||
function handleBack() {
|
||||
routerPushByKey('user_game', {
|
||||
@ -127,6 +89,7 @@ async function initQuestions() {
|
||||
}
|
||||
|
||||
const timer = ref<ReturnType<typeof setInterval> | undefined>(undefined)
|
||||
const statisticsTimer = ref<ReturnType<typeof setInterval> | undefined>(undefined)
|
||||
|
||||
/** 定时刷新 检查评委是否已经完成打分,是否可以下一步 fetchCheckNextStep */
|
||||
async function checkNextStep() {
|
||||
@ -153,12 +116,34 @@ async function checkNextStep() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取游戏现场统计结果 */
|
||||
async function fetchGetGameStatisticsData() {
|
||||
if (store.currentQuestionInfo && store.currentQuestionDetail) {
|
||||
try {
|
||||
const _params = {
|
||||
TeamGroupID: Number(groupsId.value),
|
||||
QuestionID: Number(QuestionID.value),
|
||||
QuestionDetaiID: Number(QuestionDetaiID.value),
|
||||
}
|
||||
const { data } = await fetchGetGameStatistics(_params)
|
||||
teams.value = (data?.data as any[]) || []
|
||||
}
|
||||
catch (error: any) {
|
||||
console.error('Failed to fetch game statistics', error)
|
||||
window.$message?.error(error.message || '获取游戏现场统计结果失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 定时刷新 检查是否可以下一步
|
||||
timer.value = setInterval(checkNextStep, 2000)
|
||||
// 定时刷新 游戏统计数据
|
||||
statisticsTimer.value = setInterval(fetchGetGameStatisticsData, 2000)
|
||||
|
||||
/** 销毁 定时刷新 检查是否可以下一步 */
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(timer.value)
|
||||
clearInterval(statisticsTimer.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -172,7 +157,7 @@ onBeforeUnmount(() => {
|
||||
<!-- Content -->
|
||||
<div class="w-full flex flex-col flex-1 overflow-hidden">
|
||||
<div class="mb-2 text-xl text-red-600 font-bold">
|
||||
备注:由AI模型评判
|
||||
备注:评委正在评分中,请等待。。。
|
||||
</div>
|
||||
|
||||
<div v-if="teams && teams.length > 0" class="grid grid-cols-4 h-full gap-6 pb-8">
|
||||
@ -180,43 +165,37 @@ onBeforeUnmount(() => {
|
||||
v-for="team in teams" :key="team.id"
|
||||
class="flex flex-col border-2 border-gray-200 rounded-xl bg-white/90 p-4 shadow-sm"
|
||||
>
|
||||
<div class="mb-2 text-xl text-gray-800 font-bold">
|
||||
{{ team.name }}
|
||||
<div
|
||||
class="mb-3 flex items-center gap-2 rounded-full from-sky-300 to-cyan-200 bg-gradient-to-r px-4 py-2 shadow-sm"
|
||||
>
|
||||
<span class="text-lg text-white font-bold drop-shadow">{{ team.TeamName }}</span>
|
||||
<span class="ml-2 rounded-full bg-white/40 px-2 py-0.5 text-sm">正确 <span class="text-md text-blue">{{
|
||||
team.correctCount }}</span> 字</span>
|
||||
<span class="text-s rounded-full bg-white/40 px-2 py-0.5">积分 <span class="text-md text-blue">{{ team.Points
|
||||
}}</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Answer Image Area -->
|
||||
<div
|
||||
class="relative mb-4 flex-1 cursor-zoom-in overflow-hidden border border-gray-300 rounded bg-gray-50"
|
||||
@click="handleZoom(team.image)"
|
||||
class="relative mb-4 flex-1 overflow-hidden border border-gray-300 rounded bg-gray-50"
|
||||
>
|
||||
<!-- Grid Background Simulation -->
|
||||
<div class="pointer-events-none absolute inset-0 grid grid-cols-8 gap-px bg-gray-200 opacity-20">
|
||||
<div v-for="n in 64" :key="n" class="bg-white" />
|
||||
</div>
|
||||
<!-- Simulated Handwriting/Content -->
|
||||
<div class="absolute inset-0 flex items-center justify-center text-gray-400">
|
||||
(点击放大查看详情)
|
||||
<!-- <div v-for="n in 64" :key="n" class="bg-white" /> -->
|
||||
</div>
|
||||
<NImage
|
||||
:src="team.UserAnswerPicture"
|
||||
class="h-full w-full"
|
||||
object-fit="contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Stats & Score -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- <div class="flex flex-col gap-2">
|
||||
<div class="text-sm text-gray-700 font-medium">
|
||||
写了数量{{ team.writtenCount }},正确{{ team.correctCount }}个,积分{{ team.score }}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-red-500 font-bold">评委当场评</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input
|
||||
v-model="team.manualScore" type="number"
|
||||
class="w-full border border-gray-300 rounded px-3 py-2 text-sm focus:border-blue-500 focus:outline-none"
|
||||
placeholder="留个输入框,修改正确积分"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user