<script lang="ts" setup>
|
import type { QuestionFormModel, QuestionOption, QuestionType } from '../types';
|
|
import { computed } from 'vue';
|
|
import { OPTION_LABELS } from '../constants';
|
import { createDefaultOptions } from '../parseAnswer';
|
|
const model = defineModel<QuestionFormModel>({ required: true });
|
|
const type = computed(() => model.value.questionType);
|
|
function relabel(options: QuestionOption[], qType: QuestionType) {
|
return options.map((opt, i) => ({
|
...opt,
|
sortNo: i + 1,
|
optionLabel: qType === 'blank' ? String(i + 1) : OPTION_LABELS[i] || String(i + 1),
|
}));
|
}
|
|
function addOption() {
|
const qType = type.value;
|
const next = [...model.value.options];
|
if (qType === 'blank') {
|
next.push({ sortNo: next.length + 1, optionLabel: String(next.length + 1), optionContent: '', isCorrect: '1' });
|
} else {
|
if (next.length >= OPTION_LABELS.length) return;
|
next.push({
|
sortNo: next.length + 1,
|
optionLabel: OPTION_LABELS[next.length],
|
optionContent: '',
|
isCorrect: '0',
|
});
|
}
|
model.value.options = relabel(next, qType);
|
}
|
|
function removeOption(index: number) {
|
const qType = type.value;
|
const min = qType === 'blank' ? 1 : 2;
|
if (model.value.options.length <= min) return;
|
const next = model.value.options.filter((_, i) => i !== index);
|
model.value.options = relabel(next, qType);
|
}
|
|
function onSingleCorrect(index: number) {
|
model.value.options = model.value.options.map((opt, i) => ({
|
...opt,
|
isCorrect: i === index ? '1' : '0',
|
}));
|
}
|
|
function onMultiCorrect(index: number, checked: boolean) {
|
model.value.options = model.value.options.map((opt, i) =>
|
i === index ? { ...opt, isCorrect: checked ? '1' : '0' } : opt,
|
);
|
}
|
|
function resetIfEmpty() {
|
if (!model.value.options?.length) {
|
model.value.options = createDefaultOptions(type.value);
|
}
|
}
|
|
resetIfEmpty();
|
</script>
|
|
<template>
|
<div class="answer-settings">
|
<!-- 单选 -->
|
<template v-if="type === 'single'">
|
<a-button type="link" class="!px-0 mb-2" @click="addOption">增加选项</a-button>
|
<div v-for="(opt, index) in model.options" :key="index" class="option-row">
|
<a-radio :checked="opt.isCorrect === '1'" @change="onSingleCorrect(index)" />
|
<span class="option-label">选项{{ opt.optionLabel }}</span>
|
<a-input v-model:value="opt.optionContent" placeholder="请输入选项内容" class="flex-1" />
|
<a class="text-primary" @click="removeOption(index)">移除</a>
|
</div>
|
</template>
|
|
<!-- 多选 -->
|
<template v-else-if="type === 'multi'">
|
<a-button type="link" class="!px-0 mb-2" @click="addOption">增加选项</a-button>
|
<div v-for="(opt, index) in model.options" :key="index" class="option-row option-row--multi">
|
<a-checkbox :checked="opt.isCorrect === '1'" @change="(e: any) => onMultiCorrect(index, !!e?.target?.checked)" />
|
<span class="option-label">选项{{ opt.optionLabel }}</span>
|
<a-textarea v-model:value="opt.optionContent" :rows="2" placeholder="请输入选项内容" class="flex-1" />
|
<a class="text-primary self-start mt-1" @click="removeOption(index)">移除</a>
|
</div>
|
</template>
|
|
<!-- 判断 -->
|
<template v-else-if="type === 'judge'">
|
<a-radio-group v-model:value="model.judgeAnswer">
|
<a-radio :value="true">正确</a-radio>
|
<a-radio :value="false">错误</a-radio>
|
</a-radio-group>
|
</template>
|
|
<!-- 填空 -->
|
<template v-else-if="type === 'blank'">
|
<div class="mb-2 flex items-center gap-4">
|
<a-button type="link" class="!px-0" @click="addOption">增加填空</a-button>
|
<a-checkbox v-model:checked="model.blankMixMode">混杂模式批改</a-checkbox>
|
</div>
|
<div v-for="(opt, index) in model.options" :key="index" class="option-row">
|
<span class="option-label">填空{{ opt.optionLabel }}</span>
|
<a-input v-model:value="opt.optionContent" placeholder="请输入标准答案" class="flex-1" />
|
<a class="text-primary" @click="removeOption(index)">移除</a>
|
</div>
|
</template>
|
|
<!-- 问答 -->
|
<template v-else-if="type === 'essay'">
|
<a-textarea v-model:value="model.essayAnswer" :rows="5" placeholder="请输入参考答案 / 评分要点" />
|
</template>
|
</div>
|
</template>
|
|
<style scoped>
|
.option-row {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
margin-bottom: 12px;
|
}
|
.option-row--multi {
|
align-items: flex-start;
|
}
|
.option-label {
|
width: 56px;
|
flex-shrink: 0;
|
color: rgba(0, 0, 0, 0.65);
|
}
|
</style>
|