feat(results): 新增实时结果列表和详情页面,支持评委查看和评分

- 添加 results_results-list 和 results_results-detail 路由及页面
- 实现比赛列表展示,支持跳转到实时结果详情
- 在结果详情页面添加分组和题目切换功能
- 集成游戏统计 API,实时获取队伍答题数据
- 添加 TeamResultCard 和 GroupTabs 可复用组件
- 更新国际化配置和类型定义
This commit is contained in:
2026-02-28 08:46:11 +08:00
parent e70409082a
commit 2b50bd2c71
19 changed files with 870 additions and 102 deletions

View File

@ -150,14 +150,14 @@ async function submitForm() {
activityID: 0,
questionID: question.QuestionID,
questionIndex: index,
actitvityQuestionName: question.ActitvityQuestionName,
actitvityQuestionName: question.ActitvityQuestionName || '',
questionTime: question.QuestionTime,
questionRule: question.QuestionRule,
uiType: question.UIType,
point: question.Point,
templateID: question.TemplateID,
roundType: round.roundType,
questionSubTitle: question.ActitvityQuestionName,
questionSubTitle: question.ActitvityQuestionName || '',
})
})
})

View File

@ -1,25 +1,19 @@
<script setup lang="ts">
// import type { CompetitionItem } from './modules/data'
import { NButton, NGi, NGrid, NSpace } from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
import { useRouterPush } from '@/hooks/common/router'
import { fetchDeleteActivity, fetchGetActivityList } from '@/service/api/competition'
import { useAppStore } from '@/store/modules/app'
import CompetitionAddModal from '../competition-add/index.vue'
import CompetitionCard from './modules/competition-card.vue'
export interface CompetitionItem {
Id: number
Name: string
StartTime: string
EndTime: string
TeamGroupNumber: number
PublishStatus: number
}
const { routerPushByKey } = useRouterPush()
const appStore = useAppStore()
const gap = computed(() => (appStore.isMobile ? 12 : 26))
const competitionListRef = ref<CompetitionItem[]>([])
const competitionListRef = ref<Api.Competition.CompetitionItem[]>([])
const showAddModal = ref(false)
@ -27,12 +21,12 @@ function handleAdd() {
showAddModal.value = true
}
function handleCopy(item: CompetitionItem) {
function handleCopy(item: Api.Competition.CompetitionItem) {
// TODO: 实现复制逻辑
window.$message?.success(`已复制活动:${item.Name}`)
}
function handleDelete(item: CompetitionItem) {
function handleDelete(item: Api.Competition.CompetitionItem) {
const d = window.$dialog?.warning({
title: '确认删除',
content: `确定删除活动:${item.Name}吗?`,
@ -82,6 +76,10 @@ async function fetchActivityList() {
}
}
function toDetail(item: Api.Competition.CompetitionItem) {
routerPushByKey('competition_competition-detail', { query: { Id: item.Id.toString() } })
}
onMounted(() => fetchActivityList())
</script>
@ -109,7 +107,7 @@ onMounted(() => fetchActivityList())
<!-- 卡片列表区域 -->
<!-- 响应式布局手机1列平板2列中屏3列大屏4列 -->
<NGrid :x-gap="gap" :y-gap="gap" responsive="screen" item-responsive>
<NGi v-for="item in competitionListRef" :key="item.Id" span="24 s:12 m:8 l:6">
<NGi v-for="item in competitionListRef" :key="item.Id" span="24 s:12 m:8 l:6" @click="toDetail(item)">
<CompetitionCard :item="item" @copy="handleCopy" @delete="handleDelete" />
</NGi>
</NGrid>

View File

@ -1,24 +1,21 @@
<script setup lang="ts">
import type { CompetitionItem } from '../index.vue'
import dayjs from 'dayjs'
import { NButton, NCard, NTag, NTooltip } from 'naive-ui'
import { computed } from 'vue'
import { PublishStatus } from '@/enum/business'
import { useDict } from '@/hooks/business/useDict'
import { useRouterPush } from '@/hooks/common/router'
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'copy', item: CompetitionItem): void
(e: 'delete', item: CompetitionItem): void
(e: 'copy', item: Api.Competition.CompetitionItem): void
(e: 'delete', item: Api.Competition.CompetitionItem): void
}>()
const { options: publishStatusOptions } = useDict('publish_status')
const { routerPushByKey } = useRouterPush()
interface Props {
item: CompetitionItem
item: Api.Competition.CompetitionItem
}
// 状态配置:颜色和文本
@ -43,10 +40,6 @@ const statusConfig = computed(() => {
// 处理标题换行
// const formattedTitle = computed(() => props.item.title.replace(/\n/g, '<br/>'))
function toDetail(item: CompetitionItem) {
routerPushByKey('competition_competition-detail', { query: { Id: item.Id.toString() } })
}
// 随机背景色池
const bgColors = [
'bg-blue-50/80',
@ -78,7 +71,7 @@ const decorationBgClass = computed(() => {
<NCard
:bordered="false"
class="group relative h-full overflow-hidden border border-transparent rounded-2xl transition-all duration-300 hover:scale-[1.02] hover:border-blue-500 hover:shadow-xl hover:-translate-y-2"
content-style="padding: 34px; display: flex; flex-direction: column; height: 100%;" @click="toDetail(item)"
content-style="padding: 34px; display: flex; flex-direction: column; height: 100%;"
>
<!-- 特殊背景装饰 (所有卡片显示随机颜色) -->
<div
@ -138,7 +131,7 @@ const decorationBgClass = computed(() => {
<!-- 日期 -->
<div class="mb-6 flex items-center text-gray-400">
<icon-ic-baseline-calendar-month class="mr-2 text-xl" />
<span class="text-base font-bold">{{ item.StartTime }} {{ item.EndTime }}</span>
<span class="text-base font-bold">{{ dayjs(item.StartTime).format('YYYY-MM-DD HH:mm') }} {{ dayjs(item.EndTime).format('YYYY-MM-DD HH:mm') }}</span>
</div>
</div>