2026-01-19 18:03:45 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { NGrid, NGridItem, NSelect } from 'naive-ui'
|
2026-01-21 08:41:36 +08:00
|
|
|
|
import { computed } from 'vue'
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
modelValue: {
|
|
|
|
|
|
questions: {
|
|
|
|
|
|
regular: Array<{ value: string, score: number, time: number }>
|
|
|
|
|
|
pk: Array<{ value: string, score: number, time: number }>
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}>()
|
2026-01-19 18:03:45 +08:00
|
|
|
|
|
|
|
|
|
|
const questionOptions = [
|
|
|
|
|
|
{ label: '汉字听写 - 根据提示书写汉字', value: 'hanzi' },
|
|
|
|
|
|
{ label: '同音字辨析 - 根据拼音书写', value: 'tongyin' },
|
|
|
|
|
|
{ label: '偏旁部首 - 按要求写字', value: 'pianpang' },
|
|
|
|
|
|
{ label: '词语听写 - 综合练习', value: 'ciyu' },
|
|
|
|
|
|
{ label: '成语填空 - 四字成语', value: 'chengyu' },
|
|
|
|
|
|
{ label: '反义词挑战', value: 'fanyi' },
|
|
|
|
|
|
{ label: '近义词挑战', value: 'jinyi' },
|
2026-01-20 10:58:42 +08:00
|
|
|
|
{ label: '看图猜字', value: 'kantu' },
|
|
|
|
|
|
{ label: '古诗词接龙', value: 'gushi' },
|
|
|
|
|
|
{ label: '名句听写', value: 'mingju' },
|
2026-01-19 18:03:45 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const pkOptions = [
|
|
|
|
|
|
{ label: '极速成语接龙 - 每题10秒', value: 'speed10' },
|
|
|
|
|
|
{ label: '极速成语接龙 - 每题20秒', value: 'speed20' },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-01-20 10:58:42 +08:00
|
|
|
|
const scoreOptions = [
|
|
|
|
|
|
{ label: '1分', value: 1 },
|
|
|
|
|
|
{ label: '2分', value: 2 },
|
|
|
|
|
|
{ label: '3分', value: 3 },
|
|
|
|
|
|
{ label: '5分', value: 5 },
|
|
|
|
|
|
{ label: '10分', value: 10 },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const timeOptions = [
|
|
|
|
|
|
{ label: '15s', value: 15 },
|
|
|
|
|
|
{ label: '30s', value: 30 },
|
|
|
|
|
|
{ label: '45s', value: 45 },
|
|
|
|
|
|
{ label: '60s', value: 60 },
|
|
|
|
|
|
{ label: '90s', value: 90 },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-01-21 08:41:36 +08:00
|
|
|
|
const regularTotalScore = computed(() => {
|
|
|
|
|
|
return props.modelValue?.questions?.regular?.reduce((acc, cur) => acc + cur.score, 0) || 0
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const pkTotalScore = computed(() => {
|
|
|
|
|
|
return props.modelValue?.questions?.pk?.reduce((acc, cur) => acc + cur.score, 0) || 0
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function validate() {
|
|
|
|
|
|
// 简单校验:主赛环节是否有题目
|
|
|
|
|
|
if (!props.modelValue?.questions?.regular?.length) {
|
|
|
|
|
|
window.$message?.error('请至少配置一道主赛题目')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
// 检查是否所有题目都选了题型
|
|
|
|
|
|
const invalidRegular = props.modelValue.questions.regular.some(q => !q.value)
|
|
|
|
|
|
if (invalidRegular) {
|
|
|
|
|
|
window.$message?.error('请完善主赛环节的题型配置')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
defineExpose({ validate })
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="py-4">
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<div class="mb-8 text-xl text-gray-800 font-bold">
|
|
|
|
|
|
比赛赛题包配置
|
|
|
|
|
|
</div>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<NGrid :x-gap="48" :y-gap="24" cols="1 l:2" responsive="screen">
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<!-- 左侧:主赛环节 -->
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<NGridItem>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
<div class="h-full rounded-2xl bg-[#F5F9FF] p-6">
|
|
|
|
|
|
<div class="mb-6 flex items-center justify-between border-b border-blue-100 pb-4">
|
|
|
|
|
|
<div class="flex items-center gap-2 text-gray-800 font-bold">
|
|
|
|
|
|
<icon-ic-outline-archive class="text-xl text-blue-500" />
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<span class="text-lg">主赛环节</span>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-sm text-[#8DA5C3] font-medium">
|
2026-01-21 08:41:36 +08:00
|
|
|
|
共 {{ props.modelValue?.questions?.regular?.length || 0 }} 题 / 总计 {{ regularTotalScore }} 分
|
2026-01-20 10:58:42 +08:00
|
|
|
|
</div>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-01-20 10:58:42 +08:00
|
|
|
|
<div class="flex flex-col gap-5">
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<div
|
2026-01-21 08:41:36 +08:00
|
|
|
|
v-for="(item, idx) in props.modelValue?.questions?.regular"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
:key="idx"
|
2026-01-20 10:58:42 +08:00
|
|
|
|
class="flex items-center gap-3"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
<span class="w-12 flex-shrink-0 text-sm text-[#8DA5C3] font-bold">第{{ idx + 1 }}题</span>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 题型选择 -->
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<NSelect
|
|
|
|
|
|
v-model:value="item.value"
|
|
|
|
|
|
:options="questionOptions"
|
|
|
|
|
|
placeholder="请选择题型"
|
2026-01-20 10:58:42 +08:00
|
|
|
|
class="min-w-0 flex-[2]"
|
|
|
|
|
|
:theme-overrides="{
|
|
|
|
|
|
peers: {
|
|
|
|
|
|
InternalSelection: {
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
border: '1px solid #EBF1F9',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
/>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 分数 -->
|
|
|
|
|
|
<NSelect
|
|
|
|
|
|
v-model:value="item.score"
|
|
|
|
|
|
:options="scoreOptions"
|
|
|
|
|
|
class="w-20 flex-shrink-0"
|
|
|
|
|
|
:theme-overrides="{
|
|
|
|
|
|
peers: {
|
|
|
|
|
|
InternalSelection: {
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
border: '1px solid #EBF1F9',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 答题时间 -->
|
|
|
|
|
|
<div class="flex flex-shrink-0 items-center gap-2">
|
|
|
|
|
|
<span class="text-xs text-gray-400">答题时间:</span>
|
|
|
|
|
|
<NSelect
|
|
|
|
|
|
v-model:value="item.time"
|
|
|
|
|
|
:options="timeOptions"
|
|
|
|
|
|
class="w-22"
|
|
|
|
|
|
:theme-overrides="{
|
|
|
|
|
|
peers: {
|
|
|
|
|
|
InternalSelection: {
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
border: '1px solid #EBF1F9',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</NGridItem>
|
|
|
|
|
|
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<!-- 右侧:加时PK环节 -->
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<NGridItem>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
<div class="h-full border border-[#FFF0E0] rounded-2xl bg-[#FFF9F5] p-6">
|
|
|
|
|
|
<div class="mb-6 flex items-center justify-between border-b border-orange-100 pb-4">
|
|
|
|
|
|
<div class="flex items-center gap-2 text-[#5E3218] font-bold">
|
|
|
|
|
|
<icon-ic-outline-timer class="text-xl text-[#D96B23]" />
|
2026-01-21 08:41:36 +08:00
|
|
|
|
<span class="text-lg">加时PK环节</span>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-sm text-[#D96B23]/60 font-medium">
|
2026-01-21 08:41:36 +08:00
|
|
|
|
共 {{ props.modelValue?.questions?.pk?.length || 0 }} 题 / 总计 {{ pkTotalScore }} 分
|
2026-01-20 10:58:42 +08:00
|
|
|
|
</div>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-01-20 10:58:42 +08:00
|
|
|
|
<div class="flex flex-col gap-5">
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<div
|
2026-01-21 08:41:36 +08:00
|
|
|
|
v-for="(item, idx) in props.modelValue?.questions?.pk"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
:key="idx"
|
2026-01-20 10:58:42 +08:00
|
|
|
|
class="flex items-center gap-3"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
<span class="w-12 flex-shrink-0 text-sm text-[#FF8C38] font-bold">PK{{ idx + 1 }}</span>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- PK规则 -->
|
2026-01-19 18:03:45 +08:00
|
|
|
|
<NSelect
|
|
|
|
|
|
v-model:value="item.value"
|
|
|
|
|
|
:options="pkOptions"
|
|
|
|
|
|
placeholder="请选择PK规则"
|
2026-01-20 10:58:42 +08:00
|
|
|
|
class="min-w-0 flex-[2]"
|
|
|
|
|
|
:theme-overrides="{
|
|
|
|
|
|
peers: {
|
|
|
|
|
|
InternalSelection: {
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
border: '1px solid #FFEDD5',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}"
|
2026-01-19 18:03:45 +08:00
|
|
|
|
/>
|
2026-01-20 10:58:42 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 分数 -->
|
|
|
|
|
|
<NSelect
|
|
|
|
|
|
v-model:value="item.score"
|
|
|
|
|
|
:options="scoreOptions"
|
|
|
|
|
|
class="w-20 flex-shrink-0"
|
|
|
|
|
|
:theme-overrides="{
|
|
|
|
|
|
peers: {
|
|
|
|
|
|
InternalSelection: {
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
border: '1px solid #FFEDD5',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 答题时间 -->
|
|
|
|
|
|
<div class="flex flex-shrink-0 items-center gap-2">
|
|
|
|
|
|
<span class="text-xs text-[#D96B23]/60">答题时间:</span>
|
|
|
|
|
|
<NSelect
|
|
|
|
|
|
v-model:value="item.time"
|
|
|
|
|
|
:options="timeOptions"
|
|
|
|
|
|
class="w-22"
|
|
|
|
|
|
:theme-overrides="{
|
|
|
|
|
|
peers: {
|
|
|
|
|
|
InternalSelection: {
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
border: '1px solid #FFEDD5',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-01-19 18:03:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</NGridItem>
|
|
|
|
|
|
</NGrid>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|