- 新增用户端答题流程主页面,包含封面、介绍、答题、答题图解四个状态 - 新增用户端组件:封面、介绍页、答题页、答题图解页、题目渲染器 - 新增用户端类型定义、模拟数据及业务常量 - 新增用户路由及类型声明 - 优化题库分类树,改为扁平化列表结构 - 完善比赛创建功能,增加轮次类型、题目分数类型及关联AP字段 - 修复模板删除函数调用参数错误
216 lines
6.6 KiB
Vue
216 lines
6.6 KiB
Vue
<script setup lang="ts">
|
|
import { NButton, NModal, NStep, NSteps } from 'naive-ui'
|
|
import { computed, ref, useTemplateRef, watch } from 'vue'
|
|
import { roundTypeOptions, scoreTypeOptions } from '@/constants/business'
|
|
import BasicInfo from './modules/BasicInfo.vue'
|
|
import GroupManagement from './modules/GroupManagement.vue'
|
|
// import HardwareBinding from './modules/HardwareBinding.vue'
|
|
import QuestionConfig from './modules/QuestionConfig.vue'
|
|
|
|
const props = defineProps<{
|
|
show: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:show', visible: boolean): void
|
|
(e: 'success'): void
|
|
}>()
|
|
|
|
const currentStep = ref<number>(1)
|
|
|
|
// Reset step when modal opens
|
|
watch(() => props.show, (val) => {
|
|
if (val) {
|
|
currentStep.value = 1
|
|
}
|
|
})
|
|
|
|
const competitionData = ref({
|
|
name: '阅读之星年度总决赛', // 竞赛名称
|
|
startTime: null, // 竞赛开始时间
|
|
endTime: null, // 竞赛结束时间
|
|
groupCount: 10, // 组别数量
|
|
teamCount: 4, // 每组队伍数量
|
|
poster: '', // 竞赛海报
|
|
rounds: [ // 赛题包配置
|
|
{
|
|
id: 'round_default',
|
|
name: '第一轮:主赛环节',
|
|
roundType: roundTypeOptions[0].value,
|
|
questions: Array.from({ length: 10 }).map(() => ({
|
|
value: null,
|
|
score: 5,
|
|
time: 30,
|
|
title: '第1题',
|
|
scoreType: scoreTypeOptions[0].value,
|
|
})),
|
|
},
|
|
],
|
|
// 名单录入
|
|
groupManagement: [], // 组别管理
|
|
// 绑定硬件
|
|
// hardware: [], // 绑定的硬件设备
|
|
// 其他配置项...
|
|
isPublic: true, // 是否公开
|
|
// 是否主动流程(主持人主动流程:需要选择大题)
|
|
})
|
|
|
|
const steps = [
|
|
{ title: '基础设置', component: BasicInfo },
|
|
{ title: '名单录入', component: GroupManagement },
|
|
// { title: '硬件绑定', component: HardwareBinding },
|
|
{ title: '题目配置', component: QuestionConfig },
|
|
]
|
|
|
|
const currentComponent = computed(() => steps[currentStep.value - 1]?.component)
|
|
const stepComponentRef = useTemplateRef('stepComponentRef')
|
|
|
|
function handleClose() {
|
|
emit('update:show', false)
|
|
}
|
|
|
|
function nextStep() {
|
|
// 校验当前步骤
|
|
const componentInstance = stepComponentRef.value
|
|
if (componentInstance && typeof componentInstance.validate === 'function') {
|
|
const isValid = componentInstance.validate()
|
|
if (!isValid)
|
|
return
|
|
}
|
|
|
|
if (currentStep.value < 4) {
|
|
currentStep.value++
|
|
}
|
|
else {
|
|
// 汇总整理数据
|
|
|
|
const { name, startTime, endTime, groupCount, teamCount, poster, rounds } = competitionData.value
|
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
const params = {
|
|
name,
|
|
startTime,
|
|
endTime,
|
|
groupCount,
|
|
teamCount,
|
|
poster,
|
|
rounds,
|
|
}
|
|
|
|
// 提交逻辑
|
|
window.$message?.success('比赛发布成功')
|
|
emit('success')
|
|
emit('update:show', false)
|
|
}
|
|
}
|
|
|
|
function prevStep() {
|
|
if (currentStep.value > 1) {
|
|
currentStep.value -= 1
|
|
}
|
|
}
|
|
|
|
function handleReset() {
|
|
window.$dialog?.warning({
|
|
title: '重置确认',
|
|
content: '确定要重置当前步骤的数据吗?此操作无法撤销。',
|
|
positiveText: '确定重置',
|
|
negativeText: '取消',
|
|
onPositiveClick: () => {
|
|
const componentInstance = stepComponentRef.value
|
|
if (componentInstance && typeof componentInstance.reset === 'function') {
|
|
componentInstance.reset()
|
|
window.$message?.success('重置成功')
|
|
}
|
|
},
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<NModal :show="show" :mask-closable="false" @update:show="handleClose">
|
|
<div
|
|
class="relative h-[90vh] max-h-[980px] max-w-[95vw] w-[1280px] flex flex-col overflow-hidden rounded-[2rem] bg-white shadow-2xl transition-all"
|
|
>
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between px-10 py-8">
|
|
<!-- Logo & Title -->
|
|
<div class="flex items-center gap-4">
|
|
<div class="h-12 w-12 flex items-center justify-center rounded-xl bg-[#3B82F6] shadow-blue-500/30 shadow-lg">
|
|
<div class="i-carbon-circle-dash text-2xl text-white" />
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<h2 class="m-0 text-xl text-gray-800 font-bold leading-none">
|
|
比赛创建向导
|
|
</h2>
|
|
<span class="mt-1 text-xs text-gray-400 font-bold tracking-widest uppercase">
|
|
Setup Configuration Wizard
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Steps -->
|
|
<div class="flex-1 px-20">
|
|
<NSteps :current="currentStep" class="custom-steps" size="medium">
|
|
<NStep v-for="(step, index) in steps" :key="index" :title="step.title" />
|
|
</NSteps>
|
|
</div>
|
|
|
|
<!-- Close Button -->
|
|
<div class="flex items-center gap-3">
|
|
<NButton secondary type="warning" size="small" @click="handleReset">
|
|
<template #icon>
|
|
<icon-ic-baseline-refresh class="text-icon" />
|
|
</template>
|
|
重置当前步骤
|
|
</NButton>
|
|
<button
|
|
class="h-10 w-10 flex items-center justify-center rounded-full text-gray-300 transition-colors hover:scale-110 hover:bg-gray-100 hover:text-gray-500"
|
|
@click="handleClose"
|
|
>
|
|
<NButton
|
|
type="primary" size="small"
|
|
class="h-8 w-8 rounded-full text-gray-300 transition-colors hover:bg-gray-100 hover:text-gray-500"
|
|
>
|
|
<icon-ic-baseline-close class="text-icon" />
|
|
</NButton>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="flex-1 overflow-y-auto px-10">
|
|
<div class="h-full">
|
|
<KeepAlive>
|
|
<component
|
|
:is="currentComponent" ref="stepComponentRef" v-model="competitionData"
|
|
:config="competitionData"
|
|
/>
|
|
</KeepAlive>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="flex items-center justify-between border-t border-gray-100 bg-gray-50/50 px-10 py-6">
|
|
<NButton v-if="currentStep > 1" secondary type="primary" size="large" class="px-8 font-bold" @click="prevStep">
|
|
<template #icon>
|
|
<icon-ic-baseline-arrow-back class="text-icon" />
|
|
</template>
|
|
上一步
|
|
</NButton>
|
|
<div v-else />
|
|
|
|
<NButton
|
|
type="primary" size="large" class="px-10 text-lg font-bold shadow-blue-500/30 shadow-lg"
|
|
@click="nextStep"
|
|
>
|
|
{{ currentStep === 4 ? '确认发布比赛' : '下一步' }}
|
|
<template #icon>
|
|
<icon-ic-baseline-arrow-forward v-if="currentStep < 4" class="text-icon" />
|
|
<icon-ic-baseline-check v-else class="text-icon" />
|
|
</template>
|
|
</NButton>
|
|
</div>
|
|
</div>
|
|
</NModal>
|
|
</template>
|