2026-01-21 16:29:38 +08:00
|
|
|
|
import { computed } from 'vue'
|
2026-01-23 18:16:03 +08:00
|
|
|
|
import { roundTypeOptions, scoreTypeOptions } from '@/constants/business'
|
2026-01-21 16:29:38 +08:00
|
|
|
|
|
|
|
|
|
|
export interface QuestionItem {
|
2026-01-23 18:16:03 +08:00
|
|
|
|
id: string
|
2026-01-21 16:29:38 +08:00
|
|
|
|
value: string | null
|
|
|
|
|
|
score: number
|
|
|
|
|
|
time: number
|
2026-01-23 18:16:03 +08:00
|
|
|
|
title: string
|
|
|
|
|
|
scoreType: Api.Competition.QuestionScoreType
|
2026-01-27 09:48:42 +08:00
|
|
|
|
uiType?: string
|
|
|
|
|
|
templateId?: string
|
2026-01-21 16:29:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface RoundModel {
|
|
|
|
|
|
id: string
|
|
|
|
|
|
name: string
|
|
|
|
|
|
questions: QuestionItem[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 18:16:03 +08:00
|
|
|
|
function generateId() {
|
|
|
|
|
|
return `q_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`
|
2026-01-21 16:29:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function useQuestionConfig(props: any) {
|
|
|
|
|
|
// 计算每个轮次的分数
|
|
|
|
|
|
const getRoundScore = (roundIndex: number) => {
|
|
|
|
|
|
const round = props.modelValue?.rounds?.[roundIndex]
|
|
|
|
|
|
if (!round)
|
|
|
|
|
|
return 0
|
|
|
|
|
|
return round.questions.reduce((sum: number, item: any) => sum + (Number(item.score) || 0), 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算每个轮次的时长
|
|
|
|
|
|
const getRoundTime = (roundIndex: number) => {
|
|
|
|
|
|
const round = props.modelValue?.rounds?.[roundIndex]
|
|
|
|
|
|
if (!round)
|
|
|
|
|
|
return 0
|
|
|
|
|
|
return round.questions.reduce((sum: number, item: any) => sum + (Number(item.time) || 0), 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 总计统计
|
|
|
|
|
|
const totalStats = computed(() => {
|
|
|
|
|
|
const rounds = props.modelValue?.rounds || []
|
|
|
|
|
|
return rounds.reduce((acc: any, round: any) => {
|
|
|
|
|
|
acc.count += round.questions.length
|
|
|
|
|
|
acc.score += round.questions.reduce((sum: number, item: any) => sum + (Number(item.score) || 0), 0)
|
|
|
|
|
|
acc.time += round.questions.reduce((sum: number, item: any) => sum + (Number(item.time) || 0), 0)
|
|
|
|
|
|
return acc
|
|
|
|
|
|
}, { count: 0, score: 0, time: 0 })
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const questionOptions = [
|
2026-01-27 09:48:42 +08:00
|
|
|
|
{ label: '请根据提示书写正确的汉字-jiū 表示小鸟的叫声。', value: 'hanzi' },
|
|
|
|
|
|
{ label: '请根据提示书写正确的汉字-“春色满园关不住,一枝红杏出墙来”。请书写“杏”字。', value: 'tongyin' },
|
|
|
|
|
|
{ label: '请写出含有“车”的汉字。', value: 'pianpang' },
|
|
|
|
|
|
{ label: '请根据拼音书写正确的词语。', value: 'ciyu' },
|
|
|
|
|
|
{ label: '请写出含有反义字的四字成语。 - 例如:“不日而月”', value: 'chengyu' },
|
|
|
|
|
|
{ label: '请根据图片书写正确的成语。', value: 'fanyi' },
|
2026-01-21 16:29:38 +08:00
|
|
|
|
{ label: '近义词挑战', value: 'jinyi' },
|
|
|
|
|
|
{ label: '看图猜字', value: 'kantu' },
|
|
|
|
|
|
{ label: '古诗词接龙', value: 'gushi' },
|
|
|
|
|
|
{ label: '名句听写', value: 'mingju' },
|
|
|
|
|
|
{ label: '极速成语接龙 - 每题10秒', value: 'speed10' },
|
|
|
|
|
|
{ label: '极速成语接龙 - 每题20秒', value: 'speed20' },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 新增轮次
|
2026-01-23 18:16:03 +08:00
|
|
|
|
function addRound(customName?: string, customCount?: number, roundType?: Api.Competition.CompetitionRoundType) {
|
2026-01-21 16:29:38 +08:00
|
|
|
|
if (!props.modelValue.rounds) {
|
|
|
|
|
|
props.modelValue.rounds = []
|
|
|
|
|
|
}
|
|
|
|
|
|
const index = props.modelValue.rounds.length + 1
|
|
|
|
|
|
const count = typeof customCount === 'number' ? customCount : 10
|
|
|
|
|
|
const name = typeof customName === 'string' ? customName : `第${index}轮:比赛环节`
|
|
|
|
|
|
|
|
|
|
|
|
props.modelValue.rounds.push({
|
|
|
|
|
|
id: `round_${Date.now()}`,
|
|
|
|
|
|
name,
|
2026-01-23 18:16:03 +08:00
|
|
|
|
roundType: roundType || roundTypeOptions[0].value,
|
2026-01-21 16:29:38 +08:00
|
|
|
|
questions: Array.from({ length: count }).map(() => ({
|
2026-01-23 18:16:03 +08:00
|
|
|
|
id: generateId(),
|
2026-01-21 16:29:38 +08:00
|
|
|
|
value: null,
|
|
|
|
|
|
score: 5,
|
|
|
|
|
|
time: 30,
|
2026-01-23 18:16:03 +08:00
|
|
|
|
title: '',
|
|
|
|
|
|
scoreType: scoreTypeOptions[0].value,
|
2026-01-21 16:29:38 +08:00
|
|
|
|
})),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除轮次
|
|
|
|
|
|
function removeRound(index: number) {
|
|
|
|
|
|
props.modelValue.rounds.splice(index, 1)
|
|
|
|
|
|
// 重命名剩余轮次
|
|
|
|
|
|
props.modelValue.rounds.forEach((round: RoundModel, idx: number) => {
|
|
|
|
|
|
round.name = round.name.replace(/第\d+轮/, `第${idx + 1}轮`)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新增题目
|
|
|
|
|
|
function addQuestion(roundIndex: number) {
|
|
|
|
|
|
const round = props.modelValue.rounds[roundIndex]
|
|
|
|
|
|
if (round) {
|
|
|
|
|
|
round.questions.push({
|
2026-01-23 18:16:03 +08:00
|
|
|
|
id: generateId(),
|
2026-01-21 16:29:38 +08:00
|
|
|
|
value: null,
|
|
|
|
|
|
score: 5,
|
|
|
|
|
|
time: 30,
|
2026-01-23 18:16:03 +08:00
|
|
|
|
title: '',
|
|
|
|
|
|
scoreType: scoreTypeOptions[0].value,
|
2026-01-21 16:29:38 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除题目
|
|
|
|
|
|
function removeQuestion(roundIndex: number, questionIndex: number) {
|
|
|
|
|
|
const round = props.modelValue.rounds[roundIndex]
|
|
|
|
|
|
if (round) {
|
|
|
|
|
|
round.questions.splice(questionIndex, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function validate() {
|
|
|
|
|
|
const rounds = props.modelValue?.rounds || []
|
|
|
|
|
|
if (rounds.length === 0) {
|
|
|
|
|
|
window.$message?.error('请至少配置一个比赛环节')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < rounds.length; i++) {
|
|
|
|
|
|
const round = rounds[i]
|
|
|
|
|
|
if (round.questions.length === 0) {
|
|
|
|
|
|
window.$message?.error(`${round.name} 至少需要配置一道题目`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
for (let j = 0; j < round.questions.length; j++) {
|
2026-01-27 09:48:42 +08:00
|
|
|
|
const question = round.questions[j]
|
|
|
|
|
|
const questionPrefix = `${round.name} 第 ${j + 1} 题`
|
|
|
|
|
|
|
|
|
|
|
|
if (!question.value) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未选择题型`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!question.uiType) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未选择UI类型`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!question.templateId) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未选择模板`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!question.time) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未设置答题时间`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!question.scoreType) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未设置分数规则`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!question.title) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未填写标题`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (question.score === null || question.score === undefined) {
|
|
|
|
|
|
window.$message?.error(`${questionPrefix}未设置分数`)
|
2026-01-21 16:29:38 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function reset() {
|
|
|
|
|
|
// 默认重置为1个轮次,10个题目
|
|
|
|
|
|
props.modelValue.rounds = []
|
|
|
|
|
|
addRound()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
getRoundScore,
|
|
|
|
|
|
getRoundTime,
|
|
|
|
|
|
totalStats,
|
|
|
|
|
|
questionOptions,
|
|
|
|
|
|
addRound,
|
|
|
|
|
|
removeRound,
|
|
|
|
|
|
addQuestion,
|
|
|
|
|
|
removeQuestion,
|
|
|
|
|
|
validate,
|
|
|
|
|
|
reset,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|