Files
reader-star/apps/admin/src/views/competition/competition-add/modules/BasicInfo.vue
rjl 89773bd49b feat(admin): 新增题库模块并优化比赛管理功能
新增题库模块,包含题目管理页面和Excel导入导出功能
优化比赛管理功能,包括分组管理、硬件绑定和题目配置
重构比赛添加流程,增加重置功能和数据校验
添加Excel工具函数,支持读取和导出Excel文件
更新依赖,添加xlsx库支持
2026-01-21 16:29:38 +08:00

239 lines
8.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { NButton, NDatePicker, NForm, NInput, NInputNumber, NUpload } from 'naive-ui'
import { useTemplateRef } from 'vue'
import { useBasicInfo } from './useBasicInfo'
const props = defineProps<{
modelValue: {
name: string
startTime: number | null
endTime: number | null
groupCount: number
teamCount: number
poster: string
}
}>()
const emit = defineEmits(['update:modelValue'])
const formRef = useTemplateRef('formRef')
const { formData, updateCount, handleUpload, validate, reset } = useBasicInfo(props, emit)
defineExpose({ validate, reset })
</script>
<template>
<div class="h-full flex gap-12">
<!-- 左侧表单区域 -->
<div class="flex-1">
<div class="mb-12">
<h2 class="mb-4 text-4xl text-gray-800 font-bold">
第一步 基础定义
</h2>
<p class="text-lg text-gray-400 font-bold">
请确认比赛名称与参与规模这将作为系统自动生成分组与终端连接数的依据
</p>
</div>
<NForm ref="formRef" label-placement="top" :show-feedback="false">
<div class="mb-10">
<div class="mb-3 text-sm text-[#8DA5C3] font-bold">
比赛项目名称
</div>
<NInput
v-model:value="formData.name" placeholder="例如2026年度阅读之星-辞海遨游总决赛"
class="h-16 rounded-2xl border-none text-xl line-height-16 shadow-[0_2px_10px_rgba(0,0,0,0.02)] !bg-[#FCFCFC]"
/>
</div>
<div class="grid grid-cols-2 mb-10 gap-8">
<div>
<div class="mb-3 text-sm text-[#8DA5C3] font-bold">
开始时间
</div>
<NDatePicker
v-model:value="formData.startTime" type="datetime" clearable class="w-full"
placeholder="选择开始时间" :theme-overrides="{
peers: {
Input: {
borderRadius: '1rem',
color: '#FCFCFC',
heightLarge: '4rem',
fontSizeLarge: '1.25rem',
textColor: '#1f2937',
borderHover: 'none',
borderFocus: 'none',
},
},
}" size="large"
/>
</div>
<div>
<div class="mb-3 text-sm text-[#8DA5C3] font-bold">
结束时间
</div>
<NDatePicker
v-model:value="formData.endTime" type="datetime" clearable class="w-full" placeholder="选择结束时间"
:theme-overrides="{
peers: {
Input: {
borderRadius: '1rem',
color: '#FCFCFC',
heightLarge: '4rem',
fontSizeLarge: '1.25rem',
textColor: '#1f2937',
borderHover: 'none',
borderFocus: 'none',
},
},
}" size="large"
/>
</div>
</div>
<div class="grid grid-cols-2 gap-8">
<!-- 参赛小组数量卡片 -->
<div class="rounded-3xl bg-[#F5F8FF] p-8">
<div class="mb-6 text-sm text-[#3B82F6] font-bold tracking-wide uppercase">
参赛小组总数 (GROUPS)
</div>
<div class="mb-6 flex items-center justify-between">
<button
class="h-14 w-14 flex items-center justify-center rounded-full bg-white text-3xl text-[#3B82F6] shadow-md transition-transform active:scale-95 hover:bg-gray-50"
@click="updateCount('group', -1)"
>
<div class="mb-1 font-bold">
-
</div>
</button>
<div class="w-32">
<NInputNumber
v-model:value="formData.groupCount" :min="1" :max="999" :show-button="false"
class="text-center" :theme-overrides="{
peers: {
Input: {
textColor: '#1f2937',
fontSizeMedium: '3rem',
heightMedium: '4rem',
color: 'transparent',
caretColor: '#3B82F6',
},
},
}"
/>
</div>
<button
class="h-14 w-14 flex items-center justify-center rounded-full bg-white text-3xl text-[#3B82F6] shadow-md transition-transform active:scale-95 hover:bg-gray-50"
@click="updateCount('group', 1)"
>
<div class="mb-1 font-bold">
+
</div>
</button>
</div>
<div class="text-center text-sm text-[#3B82F6]/60 font-medium italic">
系统将预设 {{ formData.groupCount }} 个队伍
</div>
</div>
<!-- 每组队伍数量卡片 -->
<div class="rounded-3xl bg-[#F5F8FF] p-8">
<div class="mb-6 text-sm text-[#3B82F6] font-bold tracking-wide uppercase">
单组包含队伍 (TEAMS)
</div>
<div class="mb-6 flex items-center justify-between">
<button
class="h-14 w-14 flex items-center justify-center rounded-full bg-white text-3xl text-[#3B82F6] shadow-md transition-transform active:scale-95 hover:bg-gray-50"
@click="updateCount('team', -1)"
>
<div class="mb-1 font-bold">
-
</div>
</button>
<div class="w-32">
<NInputNumber
v-model:value="formData.teamCount" :min="1" :max="999" :show-button="false"
class="text-center" :theme-overrides="{
peers: {
Input: {
textColor: '#1f2937',
fontSizeMedium: '3rem',
heightMedium: '4rem',
color: 'transparent',
caretColor: '#3B82F6',
},
},
}"
/>
</div>
<button
class="h-14 w-14 flex items-center justify-center rounded-full bg-white text-3xl text-[#3B82F6] shadow-md transition-transform active:scale-95 hover:bg-gray-50"
@click="updateCount('team', 1)"
>
<div class="mb-1 font-bold">
+
</div>
</button>
</div>
<div class="text-center text-sm text-[#3B82F6]/60 font-medium italic">
每组将有 {{ formData.teamCount }} 支队伍同时进行比赛
</div>
</div>
</div>
</NForm>
</div>
<!-- 右侧海报上传区域 -->
<div class="w-99 flex flex-col pt-32">
<div class="mb-3 w-full text-sm text-[#8DA5C3] font-bold">
活动海报
<NAlert type="info" class="mt-2 text-xs text-[#616263] font-normal">
<span class="text-xs font-normal">
建议上传 16:9 4:3 比例的图片以获得最佳的全屏显示效果
</span>
</NAlert>
</div>
<NUpload accept="image/*" :show-file-list="false" :custom-request="handleUpload" class="block w-full">
<div
class="relative h-[400px] w-full flex flex-col cursor-pointer items-center justify-center overflow-hidden rounded-3xl bg-[#F5F8FF] transition-all hover:bg-gray-100"
:class="{ 'border-2 border-dashed border-gray-300': !formData.poster }"
>
<div
v-if="formData.poster"
class="group absolute inset-0 flex items-center justify-center bg-black/50 opacity-0 transition-opacity hover:opacity-100"
>
<div class="flex gap-4">
<!-- NUpload trigger 会自动处理点击事件这里不需要额外的上传逻辑只需要一个按钮作为视觉触发 -->
<NButton ghost color="#fff" size="small">
更换
</NButton>
<NButton ghost color="#ff4d4f" size="small" @click.stop="formData.poster = ''">
删除
</NButton>
</div>
</div>
<img v-if="formData.poster" :src="formData.poster" class="h-full w-full object-cover" alt="Poster">
<div v-else class="flex flex-col items-center text-gray-400">
<div class="i-carbon-add-filled mb-4 text-4xl text-[#3B82F6]" />
<span class="text-sm font-bold">点击上传海报</span>
<span class="mt-2 text-xs opacity-60">支持 JPG/PNG 格式</span>
</div>
</div>
</NUpload>
</div>
</div>
</template>
<style scoped>
:deep(.n-input-number .n-input__input-el) {
text-align: center;
font-family: inherit;
font-weight: 700;
}
:deep(.n-upload-trigger) {
width: 100%;
}
</style>