2025-12-09 11:10:36 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="contest-detail-page">
|
|
|
|
|
|
<a-spin :spinning="loading">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<!-- 顶部海报区域 -->
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div class="header-section">
|
|
|
|
|
|
<!-- 返回按钮 -->
|
|
|
|
|
|
<a-button class="back-button" type="text" @click="$router.back()">
|
|
|
|
|
|
<template #icon>
|
|
|
|
|
|
<ArrowLeftOutlined />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
返回
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 海报图片 -->
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<div
|
2026-01-09 18:14:35 +08:00
|
|
|
|
class="poster-container"
|
2026-01-08 09:17:46 +08:00
|
|
|
|
:style="{
|
|
|
|
|
|
backgroundImage:
|
|
|
|
|
|
contest?.posterUrl || contest?.coverUrl
|
|
|
|
|
|
? `url(${contest.posterUrl || contest.coverUrl})`
|
|
|
|
|
|
: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div class="poster-placeholder">
|
|
|
|
|
|
<span>赛事海报</span>
|
|
|
|
|
|
</div>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<!-- 导航栏和报名信息 -->
|
|
|
|
|
|
<div class="nav-bar-section">
|
|
|
|
|
|
<div class="nav-tabs-wrapper">
|
|
|
|
|
|
<a-tabs
|
|
|
|
|
|
v-model:activeKey="activeTab"
|
|
|
|
|
|
class="nav-tabs"
|
|
|
|
|
|
@change="handleTabChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-tab-pane key="info" tab="赛事信息" />
|
|
|
|
|
|
<a-tab-pane
|
|
|
|
|
|
key="notices"
|
|
|
|
|
|
:tab="`通知公告${
|
|
|
|
|
|
notices.length > 0 ? ` (${notices.length})` : ''
|
|
|
|
|
|
}`"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<a-tab-pane
|
|
|
|
|
|
key="results"
|
|
|
|
|
|
:tab="`赛事结果${
|
|
|
|
|
|
results.length > 0 ? ` (${results.length})` : ''
|
|
|
|
|
|
}`"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-tabs>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="registration-info-bar">
|
|
|
|
|
|
<div class="registration-time-info">
|
|
|
|
|
|
<span class="time-label">报名时间</span>
|
|
|
|
|
|
<span v-if="isRegistering" class="countdown-text">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
距离报名截止还有 {{ daysRemaining }} 天
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</span>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</div>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div class="registration-time-range">
|
|
|
|
|
|
{{
|
|
|
|
|
|
formatDateRange(
|
|
|
|
|
|
contest?.registerStartTime,
|
|
|
|
|
|
contest?.registerEndTime
|
|
|
|
|
|
)
|
|
|
|
|
|
}}
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</div>
|
2025-12-09 11:10:36 +08:00
|
|
|
|
<a-button
|
2026-01-15 16:35:00 +08:00
|
|
|
|
v-if="isTeacher && isRegistering && !hasRegistered"
|
2026-01-08 09:17:46 +08:00
|
|
|
|
type="primary"
|
|
|
|
|
|
size="large"
|
2026-01-09 18:14:35 +08:00
|
|
|
|
class="register-button"
|
2026-01-08 09:17:46 +08:00
|
|
|
|
@click="handleRegister"
|
2025-12-09 11:10:36 +08:00
|
|
|
|
>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
立即报名
|
2025-12-09 11:10:36 +08:00
|
|
|
|
</a-button>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<a-button
|
2026-01-15 16:35:00 +08:00
|
|
|
|
v-else-if="isTeacher && hasRegistered && canViewRegistration"
|
2026-01-08 09:17:46 +08:00
|
|
|
|
type="default"
|
|
|
|
|
|
size="large"
|
2026-01-09 18:14:35 +08:00
|
|
|
|
class="register-button"
|
2026-01-08 09:17:46 +08:00
|
|
|
|
@click="handleViewRegistration"
|
|
|
|
|
|
>
|
|
|
|
|
|
查看报名
|
|
|
|
|
|
</a-button>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<a-button
|
2026-01-15 16:35:00 +08:00
|
|
|
|
v-else-if="isTeacher"
|
2026-01-09 18:14:35 +08:00
|
|
|
|
type="default"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="register-button"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
>
|
|
|
|
|
|
立即报名
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</a-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-09 11:10:36 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<!-- 主标题 -->
|
|
|
|
|
|
<div v-if="contest" class="title-section">
|
|
|
|
|
|
<h1 class="contest-title">{{ contest.contestName }}</h1>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</div>
|
2025-12-09 11:10:36 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<!-- 内容区域 - 两列布局 -->
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<div v-if="contest" class="content-section">
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<a-row :gutter="24">
|
|
|
|
|
|
<!-- 左侧:竞赛信息 -->
|
|
|
|
|
|
<a-col :xs="24" :lg="16">
|
|
|
|
|
|
<div class="info-content">
|
|
|
|
|
|
<div class="section-header">| 竞赛信息</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 赛事信息 Tab 内容 -->
|
|
|
|
|
|
<div v-if="activeTab === 'info'" class="info-detail">
|
|
|
|
|
|
<div v-if="contest.content" class="info-section">
|
|
|
|
|
|
<h3 class="section-title">一、大赛介绍</h3>
|
|
|
|
|
|
<div class="section-content" v-html="contest.content"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="(contest as any).theme" class="info-section">
|
|
|
|
|
|
<h3 class="section-title">二、大赛主题</h3>
|
|
|
|
|
|
<div class="section-content">
|
|
|
|
|
|
{{ (contest as any).theme }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-section">
|
|
|
|
|
|
<h3 class="section-title">三、参赛对象</h3>
|
|
|
|
|
|
<div class="section-content">
|
|
|
|
|
|
在创新创业应用领域具有先进技术解决方案和成功实践经验的创业团队、优秀人才均可报名参赛。
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="
|
|
|
|
|
|
(contest as any).tracks && (contest as any).tracks.length
|
|
|
|
|
|
"
|
|
|
|
|
|
class="info-section"
|
|
|
|
|
|
>
|
|
|
|
|
|
<h3 class="section-title">四、大赛赛道</h3>
|
|
|
|
|
|
<div class="section-content">
|
|
|
|
|
|
<a-tag
|
|
|
|
|
|
v-for="track in (contest as any).tracks"
|
|
|
|
|
|
:key="track"
|
|
|
|
|
|
class="track-tag"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ track }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-section">
|
|
|
|
|
|
<h3 class="section-title">五、参赛规则</h3>
|
|
|
|
|
|
<div class="section-content">
|
|
|
|
|
|
<div class="rule-item">
|
|
|
|
|
|
<strong>(一)参赛团队:</strong>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
1.参赛团队应由1-8人组成,一名参赛人员仅允许参与一支参赛队伍。
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 通知公告 Tab 内容 -->
|
|
|
|
|
|
<div v-if="activeTab === 'notices'" class="notices-content">
|
|
|
|
|
|
<a-list
|
|
|
|
|
|
:data-source="notices"
|
|
|
|
|
|
:loading="noticesLoading"
|
|
|
|
|
|
item-layout="vertical"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #renderItem="{ item }">
|
|
|
|
|
|
<a-list-item>
|
|
|
|
|
|
<a-list-item-meta>
|
|
|
|
|
|
<template #title>
|
|
|
|
|
|
<a-space>
|
|
|
|
|
|
<span>{{ item.title }}</span>
|
|
|
|
|
|
<a-tag :color="getNoticeTypeColor(item.noticeType)">
|
|
|
|
|
|
{{ getNoticeTypeText(item.noticeType) }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
<a-tag v-if="item.priority > 0" color="red">
|
|
|
|
|
|
优先级: {{ item.priority }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #description>
|
|
|
|
|
|
<div>{{ item.content }}</div>
|
|
|
|
|
|
<div style="margin-top: 8px; color: #999">
|
|
|
|
|
|
发布时间: {{ formatDateTime(item.publishTime) }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-list-item-meta>
|
|
|
|
|
|
</a-list-item>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #empty>
|
|
|
|
|
|
<a-empty description="暂无公告" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-list>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 赛事结果 Tab 内容 -->
|
|
|
|
|
|
<div v-if="activeTab === 'results'" class="results-content">
|
|
|
|
|
|
<div v-if="contest.resultState === 'published'">
|
|
|
|
|
|
<a-spin :spinning="resultsLoading">
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
:columns="resultColumns"
|
|
|
|
|
|
:data-source="results"
|
|
|
|
|
|
:pagination="resultsPagination"
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
@change="handleResultsTableChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
|
<template v-if="column.key === 'rank'">
|
|
|
|
|
|
<a-tag :color="getRankColor(record.rank)">
|
|
|
|
|
|
{{ record.rank || "-" }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.key === 'author'">
|
|
|
|
|
|
{{
|
|
|
|
|
|
record.registration?.user?.nickname ||
|
|
|
|
|
|
record.registration?.team?.teamName ||
|
|
|
|
|
|
"-"
|
|
|
|
|
|
}}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.key === 'award'">
|
|
|
|
|
|
<a-tag
|
|
|
|
|
|
v-if="record.awardName"
|
|
|
|
|
|
:color="getAwardColor(record.awardName)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ record.awardName }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-spin>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<a-empty v-else description="结果尚未公布" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧:组织信息 -->
|
|
|
|
|
|
<a-col :xs="24" :lg="8">
|
|
|
|
|
|
<div class="org-info-card">
|
2026-01-15 16:35:00 +08:00
|
|
|
|
<!-- <div class="info-item">
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div class="info-label">| 发布者</div>
|
|
|
|
|
|
<div class="info-value">
|
|
|
|
|
|
<div v-if="(contest as any).publisher">
|
|
|
|
|
|
{{ (contest as any).publisher }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-else>-</div>
|
|
|
|
|
|
</div>
|
2026-01-15 16:35:00 +08:00
|
|
|
|
</div> -->
|
2026-01-09 18:14:35 +08:00
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<div class="info-label">| 类型</div>
|
|
|
|
|
|
<div class="info-value">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<a-tag
|
|
|
|
|
|
:color="
|
|
|
|
|
|
contest.contestType === 'individual' ? 'blue' : 'green'
|
|
|
|
|
|
"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{
|
|
|
|
|
|
contest.contestType === "individual" ? "个人赛" : "团队赛"
|
|
|
|
|
|
}}
|
|
|
|
|
|
</a-tag>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<div class="info-label">| 主办单位</div>
|
|
|
|
|
|
<div class="info-value">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<template
|
|
|
|
|
|
v-if="contest.organizers && contest.organizers.length"
|
|
|
|
|
|
>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="org in contest.organizers"
|
|
|
|
|
|
:key="org"
|
|
|
|
|
|
class="org-item"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ org }}
|
|
|
|
|
|
</div>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<span v-else>-</span>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<div class="info-label">| 协办单位</div>
|
|
|
|
|
|
<div class="info-value">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<template
|
|
|
|
|
|
v-if="contest.coOrganizers && contest.coOrganizers.length"
|
|
|
|
|
|
>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="org in contest.coOrganizers"
|
|
|
|
|
|
:key="org"
|
|
|
|
|
|
class="org-item"
|
|
|
|
|
|
>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
{{ org }}
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</div>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<span v-else>-</span>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<div class="info-label">| 赞助单位</div>
|
|
|
|
|
|
<div class="info-value">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<template v-if="contest.sponsors && contest.sponsors.length">
|
2026-01-09 18:14:35 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="sp in contest.sponsors"
|
|
|
|
|
|
:key="sp"
|
|
|
|
|
|
class="org-item"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ sp }}
|
|
|
|
|
|
</div>
|
2025-12-09 11:10:36 +08:00
|
|
|
|
</template>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<span v-else>-</span>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<div class="info-label">| 报名时间</div>
|
|
|
|
|
|
<div class="info-value">
|
|
|
|
|
|
{{ formatDateTime(contest.registerStartTime) }} 至
|
2026-01-08 09:17:46 +08:00
|
|
|
|
{{ formatDateTime(contest.registerEndTime) }}
|
2026-01-09 18:14:35 +08:00
|
|
|
|
</div>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</div>
|
2026-01-09 18:14:35 +08:00
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<div class="info-label">| 比赛时间</div>
|
|
|
|
|
|
<div class="info-value">
|
|
|
|
|
|
{{ formatDateTime(contest.startTime) }} 至
|
|
|
|
|
|
{{ formatDateTime(contest.endTime) }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
2026-01-08 09:17:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
|
<a-empty v-else description="比赛不存在" />
|
|
|
|
|
|
</a-spin>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-01-08 09:17:46 +08:00
|
|
|
|
import { ref, onMounted, computed } from "vue"
|
|
|
|
|
|
import { useRoute, useRouter } from "vue-router"
|
|
|
|
|
|
import { message } from "ant-design-vue"
|
|
|
|
|
|
import { ArrowLeftOutlined } from "@ant-design/icons-vue"
|
|
|
|
|
|
import dayjs from "dayjs"
|
|
|
|
|
|
import { useAuthStore } from "@/stores/auth"
|
|
|
|
|
|
import {
|
|
|
|
|
|
contestsApi,
|
|
|
|
|
|
noticesApi,
|
|
|
|
|
|
resultsApi,
|
|
|
|
|
|
registrationsApi,
|
|
|
|
|
|
type Contest,
|
|
|
|
|
|
type ContestNotice,
|
|
|
|
|
|
type ContestResult,
|
|
|
|
|
|
} from "@/api/contests"
|
2025-12-09 11:10:36 +08:00
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
|
const router = useRouter()
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const authStore = useAuthStore()
|
|
|
|
|
|
const tenantCode = route.params.tenantCode as string
|
2025-12-09 11:10:36 +08:00
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const noticesLoading = ref(false)
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const resultsLoading = ref(false)
|
2025-12-09 11:10:36 +08:00
|
|
|
|
const contest = ref<Contest | null>(null)
|
|
|
|
|
|
const notices = ref<ContestNotice[]>([])
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const results = ref<ContestResult[]>([])
|
|
|
|
|
|
const activeTab = ref("info")
|
|
|
|
|
|
const hasRegistered = ref(false)
|
|
|
|
|
|
const myRegistration = ref<any>(null)
|
2025-12-09 11:10:36 +08:00
|
|
|
|
|
2026-01-12 20:04:11 +08:00
|
|
|
|
// 检查是否有查看报名的权限
|
|
|
|
|
|
const canViewRegistration = computed(() => {
|
|
|
|
|
|
const permissions = authStore.user?.permissions || []
|
2026-01-15 16:35:00 +08:00
|
|
|
|
return (
|
|
|
|
|
|
permissions.includes("registration:read") ||
|
|
|
|
|
|
permissions.includes("registration:create")
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是教师角色
|
|
|
|
|
|
const isTeacher = computed(() => {
|
|
|
|
|
|
return authStore.hasRole("teacher")
|
2026-01-12 20:04:11 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
|
const contestId = Number(route.params.id)
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const resultsPagination = ref({
|
|
|
|
|
|
current: 1,
|
|
|
|
|
|
pageSize: 20,
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const resultColumns = [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "排名",
|
|
|
|
|
|
key: "rank",
|
|
|
|
|
|
dataIndex: "rank",
|
|
|
|
|
|
width: 80,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "作品名称",
|
|
|
|
|
|
key: "title",
|
|
|
|
|
|
dataIndex: "title",
|
|
|
|
|
|
width: 200,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "作者",
|
|
|
|
|
|
key: "author",
|
|
|
|
|
|
width: 150,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "最终得分",
|
|
|
|
|
|
key: "finalScore",
|
|
|
|
|
|
dataIndex: "finalScore",
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
sorter: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "奖项",
|
|
|
|
|
|
key: "award",
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化日期
|
|
|
|
|
|
const formatDate = (dateStr?: string) => {
|
|
|
|
|
|
if (!dateStr) return "-"
|
|
|
|
|
|
return dayjs(dateStr).format("YYYY-MM-DD")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
|
// 格式化日期时间
|
|
|
|
|
|
const formatDateTime = (dateStr?: string) => {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
if (!dateStr) return "-"
|
|
|
|
|
|
return dayjs(dateStr).format("YYYY-MM-DD HH:mm")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
// 格式化日期范围
|
|
|
|
|
|
const formatDateRange = (startDate?: string, endDate?: string) => {
|
|
|
|
|
|
if (!startDate || !endDate) return "-"
|
|
|
|
|
|
return `${formatDate(startDate)} ~ ${formatDate(endDate)}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 判断是否在报名中
|
|
|
|
|
|
const isRegistering = computed(() => {
|
|
|
|
|
|
if (!contest.value) return false
|
|
|
|
|
|
const now = dayjs()
|
|
|
|
|
|
const start = dayjs(contest.value.registerStartTime)
|
|
|
|
|
|
const end = dayjs(contest.value.registerEndTime)
|
|
|
|
|
|
return now.isAfter(start) && now.isBefore(end)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 计算距离报名截止还有几天
|
|
|
|
|
|
const daysRemaining = computed(() => {
|
|
|
|
|
|
if (!contest.value || !isRegistering.value) return 0
|
|
|
|
|
|
const now = dayjs()
|
|
|
|
|
|
const end = dayjs(contest.value.registerEndTime)
|
|
|
|
|
|
const diff = end.diff(now, "day")
|
|
|
|
|
|
return diff > 0 ? diff : 0
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
|
// 获取公告类型颜色
|
|
|
|
|
|
const getNoticeTypeColor = (type?: string) => {
|
|
|
|
|
|
switch (type) {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
case "urgent":
|
|
|
|
|
|
return "red"
|
|
|
|
|
|
case "system":
|
|
|
|
|
|
return "blue"
|
2025-12-09 11:10:36 +08:00
|
|
|
|
default:
|
2026-01-08 09:17:46 +08:00
|
|
|
|
return "default"
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取公告类型文本
|
|
|
|
|
|
const getNoticeTypeText = (type?: string) => {
|
|
|
|
|
|
switch (type) {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
case "urgent":
|
|
|
|
|
|
return "紧急通知"
|
|
|
|
|
|
case "system":
|
|
|
|
|
|
return "系统公告"
|
2025-12-09 11:10:36 +08:00
|
|
|
|
default:
|
2026-01-08 09:17:46 +08:00
|
|
|
|
return "普通公告"
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 获取排名颜色
|
|
|
|
|
|
const getRankColor = (rank?: number) => {
|
|
|
|
|
|
if (!rank) return "default"
|
|
|
|
|
|
if (rank === 1) return "gold"
|
|
|
|
|
|
if (rank === 2) return "default"
|
|
|
|
|
|
if (rank === 3) return "orange"
|
|
|
|
|
|
return "blue"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取奖项颜色
|
|
|
|
|
|
const getAwardColor = (award?: string) => {
|
|
|
|
|
|
if (!award) return "default"
|
|
|
|
|
|
if (award.includes("一等奖") || award.includes("金奖")) return "gold"
|
|
|
|
|
|
if (award.includes("二等奖") || award.includes("银奖")) return "default"
|
|
|
|
|
|
if (award.includes("三等奖") || award.includes("铜奖")) return "orange"
|
|
|
|
|
|
return "blue"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
|
// 加载比赛详情
|
|
|
|
|
|
const fetchContestDetail = async () => {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
contest.value = await contestsApi.getDetail(contestId)
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 检查是否已报名
|
|
|
|
|
|
await checkRegistration()
|
2025-12-09 11:10:36 +08:00
|
|
|
|
} catch (error: any) {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
message.error(error?.response?.data?.message || "获取比赛详情失败")
|
2025-12-09 11:10:36 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 检查是否已报名
|
|
|
|
|
|
const checkRegistration = async () => {
|
|
|
|
|
|
if (!authStore.user) return
|
2025-12-09 11:10:36 +08:00
|
|
|
|
try {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const response = await registrationsApi.getList({
|
|
|
|
|
|
contestId,
|
|
|
|
|
|
userId: authStore.user.id,
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
pageSize: 1,
|
|
|
|
|
|
})
|
|
|
|
|
|
if (response.list && response.list.length > 0) {
|
|
|
|
|
|
hasRegistered.value = true
|
|
|
|
|
|
myRegistration.value = response.list[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("检查报名状态失败:", error)
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载公告列表
|
|
|
|
|
|
const fetchNotices = async () => {
|
|
|
|
|
|
noticesLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
notices.value = await noticesApi.getList(contestId)
|
|
|
|
|
|
} catch (error: any) {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
message.error("获取公告列表失败")
|
2025-12-09 11:10:36 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
noticesLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 加载赛事结果
|
|
|
|
|
|
const fetchResults = async () => {
|
|
|
|
|
|
if (!contest.value || contest.value.resultState !== "published") return
|
|
|
|
|
|
resultsLoading.value = true
|
2025-12-09 11:10:36 +08:00
|
|
|
|
try {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const response = await resultsApi.getResults(
|
|
|
|
|
|
contestId,
|
|
|
|
|
|
resultsPagination.value.current,
|
|
|
|
|
|
resultsPagination.value.pageSize
|
|
|
|
|
|
)
|
|
|
|
|
|
results.value = response.list || []
|
|
|
|
|
|
resultsPagination.value.total = response.total || 0
|
2025-12-09 11:10:36 +08:00
|
|
|
|
} catch (error: any) {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
message.error("获取赛事结果失败")
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
resultsLoading.value = false
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 结果表格变化处理
|
|
|
|
|
|
const handleResultsTableChange = (pag: any) => {
|
|
|
|
|
|
resultsPagination.value.current = pag.current || 1
|
|
|
|
|
|
resultsPagination.value.pageSize = pag.pageSize || 20
|
|
|
|
|
|
fetchResults()
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
// 立即报名 - 跳转到报名页面
|
|
|
|
|
|
const handleRegister = () => {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
if (!authStore.user) {
|
|
|
|
|
|
message.warning("请先登录")
|
|
|
|
|
|
router.push(`/${tenantCode}/login`)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
if (!contest.value) return
|
|
|
|
|
|
|
|
|
|
|
|
// 根据比赛类型跳转到对应的报名页面
|
|
|
|
|
|
if (contest.value.contestType === "team") {
|
|
|
|
|
|
router.push(`/${tenantCode}/contests/${contestId}/register/team`)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
router.push(`/${tenantCode}/contests/${contestId}/register/individual`)
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
// 查看报名 - 跳转到报名页面
|
2026-01-08 09:17:46 +08:00
|
|
|
|
const handleViewRegistration = () => {
|
2026-01-09 18:14:35 +08:00
|
|
|
|
if (!contest.value) return
|
|
|
|
|
|
|
|
|
|
|
|
// 根据比赛类型跳转到对应的报名页面
|
|
|
|
|
|
if (contest.value.contestType === "team") {
|
|
|
|
|
|
router.push(`/${tenantCode}/contests/${contestId}/register/team`)
|
2026-01-08 09:17:46 +08:00
|
|
|
|
} else {
|
2026-01-09 18:14:35 +08:00
|
|
|
|
router.push(`/${tenantCode}/contests/${contestId}/register/individual`)
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
fetchContestDetail()
|
|
|
|
|
|
fetchNotices()
|
|
|
|
|
|
})
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听tab切换,切换到结果tab时加载结果
|
|
|
|
|
|
const handleTabChange = (key: string) => {
|
|
|
|
|
|
activeTab.value = key
|
|
|
|
|
|
if (key === "results" && contest.value?.resultState === "published") {
|
|
|
|
|
|
fetchResults()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-09 11:10:36 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
<style lang="scss" scoped>
|
2025-12-09 11:10:36 +08:00
|
|
|
|
.contest-detail-page {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
background-color: #f0f2f5;
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.header-section {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
position: relative;
|
2026-01-09 18:14:35 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
|
|
|
|
|
|
.back-button {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 24px;
|
|
|
|
|
|
left: 24px;
|
|
|
|
|
|
z-index: 100;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
color: #1890ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.poster-container {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
width: 100%;
|
2026-01-09 18:14:35 +08:00
|
|
|
|
height: 400px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
background-size: cover;
|
|
|
|
|
|
background-position: center;
|
|
|
|
|
|
background-repeat: no-repeat;
|
2026-01-09 18:14:35 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
position: relative;
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.poster-placeholder {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.3);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 18px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
2026-01-09 18:14:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-bar-section {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 16px 24px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
|
|
|
|
|
|
.nav-tabs-wrapper {
|
|
|
|
|
|
flex: 1;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
:deep(.ant-tabs-nav) {
|
|
|
|
|
|
margin-bottom: 0;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
:deep(.ant-tabs-tab) {
|
|
|
|
|
|
padding: 12px 24px;
|
|
|
|
|
|
font-size: 16px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
2026-01-09 18:14:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.registration-info-bar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
gap: 8px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.registration-time-info {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.time-label {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #8c8c8c;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.countdown-text {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #1890ff;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
2026-01-09 18:14:35 +08:00
|
|
|
|
}
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.registration-time-range {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #595959;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.register-button {
|
|
|
|
|
|
min-width: 120px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.title-section {
|
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
padding: 24px 24px 0;
|
|
|
|
|
|
|
|
|
|
|
|
.contest-title {
|
|
|
|
|
|
font-size: 32px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #262626;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
.content-section {
|
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.info-content {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
2026-01-09 18:14:35 +08:00
|
|
|
|
|
|
|
|
|
|
.section-header {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #262626;
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
padding-bottom: 12px;
|
|
|
|
|
|
border-bottom: 2px solid #1890ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-detail {
|
|
|
|
|
|
.info-section {
|
|
|
|
|
|
margin-bottom: 32px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #262626;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-content {
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
color: #595959;
|
|
|
|
|
|
line-height: 1.8;
|
|
|
|
|
|
|
|
|
|
|
|
.track-tag {
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rule-item {
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
|
|
|
|
|
|
strong {
|
|
|
|
|
|
color: #262626;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
p {
|
|
|
|
|
|
margin: 8px 0 0 24px;
|
|
|
|
|
|
color: #595959;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.notices-content,
|
|
|
|
|
|
.results-content {
|
|
|
|
|
|
padding-top: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.org-info-card {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
|
|
|
|
.info-item {
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
padding-bottom: 24px;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-label {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #262626;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-value {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #595959;
|
|
|
|
|
|
line-height: 1.8;
|
|
|
|
|
|
|
|
|
|
|
|
.org-item {
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-09 11:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
|
// 响应式设计
|
2026-01-09 18:14:35 +08:00
|
|
|
|
@media (max-width: 992px) {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
.contest-detail-page {
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.header-section {
|
|
|
|
|
|
.nav-bar-section {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
|
|
|
|
|
|
.registration-info-bar {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
align-items: flex-start;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-09 18:14:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-section {
|
|
|
|
|
|
.info-content,
|
|
|
|
|
|
.org-info-card {
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.contest-detail-page {
|
|
|
|
|
|
.header-section {
|
|
|
|
|
|
.poster-container {
|
|
|
|
|
|
height: 250px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-bar-section {
|
|
|
|
|
|
padding: 12px 16px;
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.nav-tabs-wrapper {
|
|
|
|
|
|
:deep(.ant-tabs-tab) {
|
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.title-section {
|
|
|
|
|
|
padding: 16px 16px 0;
|
|
|
|
|
|
|
|
|
|
|
|
.contest-title {
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
}
|
2026-01-08 09:17:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-section {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
|
2026-01-09 18:14:35 +08:00
|
|
|
|
.info-content,
|
|
|
|
|
|
.org-info-card {
|
2026-01-08 09:17:46 +08:00
|
|
|
|
padding: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|