2026-01-19 18:03:45 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-01-21 16:29:38 +08:00
|
|
|
|
import { NButton, NDatePicker, NForm, NInput, NInputNumber, NUpload } from 'naive-ui'
|
|
|
|
|
|
import { useTemplateRef } from 'vue'
|
|
|
|
|
|
import { useBasicInfo } from './useBasicInfo'
|
2026-01-20 10:58:42 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
modelValue: {
|
|
|
|
|
|
name: string
|
|
|
|
|
|
startTime: number | null
|
|
|
|
|
|
endTime: number | null
|
|
|
|
|
|
groupCount: number
|
|
|
|
|
|
teamCount: number
|
2026-01-21 08:41:36 +08:00
|
|
|
|
poster: string
|
2026-01-20 10:58:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
2026-01-19 18:03:45 +08:00
|
|
|
|
|
|
|
|
|
|
const formRef = useTemplateRef('formRef')
|
|
|
|
|
|
|
2026-01-21 16:29:38 +08:00
|
|
|
|
const { formData, updateCount, handleUpload, validate, reset } = useBasicInfo(props, emit)
|
2026-01-20 10:58:42 +08:00
|
|
|
|
|
2026-01-21 16:29:38 +08:00
|
|
|
|
defineExpose({ validate, reset })
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<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>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<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]"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
/>
|
2026-01-21 08:41:36 +08:00
|
|
|
|
</div>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<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>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</template>
|
2026-01-21 08:41:36 +08:00
|
|
|
|
|
|
|
|
|
|
<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>
|