diff --git a/src/views/platformAdmin/questionBank/index.vue b/src/views/platformAdmin/questionBank/index.vue index 208e55a..85b966a 100644 --- a/src/views/platformAdmin/questionBank/index.vue +++ b/src/views/platformAdmin/questionBank/index.vue @@ -1,12 +1,12 @@ @@ -28,23 +28,23 @@ import * as api from "@/api/exam"; const types = [{ label: "单选题", value: "SINGLE" }, { label: "多选题", value: "MULTIPLE" }, { label: "判断题", value: "TRUE_FALSE" }, { label: "简答题", value: "SHORT_ANSWER" }]; const typeName = Object.fromEntries(types.map((item) => [item.value, item.label])); -const loading = ref(false); const saving = ref(false); const uploading = ref(false); const aiLoading = ref(false); const questions = ref([]); const projects = ref([]); const editorVisible = ref(false); const aiVisible = ref(false); const preview = ref([]); const fileInput = ref(); +const loading = ref(false); const saving = ref(false); const uploading = ref(false); const aiLoading = ref(false); const questions = ref([]); const editorVisible = ref(false); const aiVisible = ref(false); const preview = ref([]); const fileInput = ref(); const query = reactive({ type: "", taskKey: "", keyword: "" }); const form = reactive(emptyQuestion()); const aiForm = reactive({ taskKey: "", type: "SINGLE", count: 5, requirement: "" }); const isChoice = computed(() => ["SINGLE", "MULTIPLE"].includes(form.type)); const answers = computed({ get: () => Array.isArray(form.answer) ? form.answer : [], set: (value) => { form.answer = value; } }); const singleAnswer = computed({ get: () => Array.isArray(form.answer) ? form.answer[0] || "" : "", set: (value) => { form.answer = value ? [value] : []; } }); function emptyQuestion() { return { id: null, taskKey: "", type: "SINGLE", stem: "

", options: ["A", "B", "C", "D"].map((label) => ({ label, content: "

" })), answer: [], analysis: "

", attachmentName: "", attachmentUrl: "" }; } -function projectLabel(item) { return `${item.projectName} / ${item.taskName}`; } function plainHtml(value) { return String(value || "").replace(/<[^>]+>/g, "").replace(/ /g, " "); } -async function load() { loading.value = true; try { questions.value = (await api.getPlatformQuestions(query)).data || []; } finally { loading.value = false; } } async function init() { projects.value = (await api.getPlatformQuestionProjects()).data || []; await load(); } onMounted(init); -function openEditor(row) { Object.assign(form, row ? JSON.parse(JSON.stringify(row)) : emptyQuestion()); if (!Array.isArray(form.answer) && isChoice.value) form.answer = []; editorVisible.value = true; } function openAi() { aiForm.taskKey = query.taskKey || aiForm.taskKey || projects.value[0]?.taskKey || ""; preview.value = []; aiVisible.value = true; } +function plainHtml(value) { return String(value || "").replace(/<[^>]+>/g, "").replace(/ /g, " "); } +async function load() { loading.value = true; try { questions.value = (await api.getPlatformQuestions(query)).data || []; } finally { loading.value = false; } } async function init() { await load(); } onMounted(init); +function openEditor(row) { Object.assign(form, row ? JSON.parse(JSON.stringify(row)) : emptyQuestion()); if (!Array.isArray(form.answer) && isChoice.value) form.answer = []; editorVisible.value = true; } function openAi() { aiForm.taskKey = ""; preview.value = []; aiVisible.value = true; } function optionLabel(index) { return String.fromCharCode(65 + index); } function addOption() { if (form.options.length >= 26) return ElMessage.warning("最多支持 26 个选项"); form.options.push({ label: optionLabel(form.options.length), content: "

" }); } function removeOption(index) { if (form.options.length <= 2) return ElMessage.warning("至少保留 2 个选项"); const removed = form.options[index]; form.options.splice(index, 1); form.options.forEach((opt, i) => { opt.label = optionLabel(i); }); if (Array.isArray(form.answer)) form.answer = form.answer.filter((a) => a !== removed.label); } function resetForType() { form.answer = form.type === "TRUE_FALSE" ? true : form.type === "SHORT_ANSWER" ? "

" : []; if (form.type !== "SHORT_ANSWER") { form.attachmentName = ""; form.attachmentUrl = ""; } if (isChoice.value) { const count = form.options.length || 4; form.options = Array.from({ length: Math.max(count, 4) }, (_, i) => ({ label: optionLabel(i), content: "

" })); } } -async function save() { if (!form.taskKey || !plainHtml(form.stem).trim()) return ElMessage.warning("请填写归属项目和题干"); if (isChoice.value && (!form.answer.length || form.options.some((item) => !plainHtml(item.content).trim()))) return ElMessage.warning("请补全选项并设置正确答案"); if (form.type === "SHORT_ANSWER" && !plainHtml(form.answer).trim()) return ElMessage.warning("请填写参考答案"); saving.value = true; try { await (form.id ? api.updatePlatformQuestion(form.id, form) : api.createPlatformQuestion(form)); ElMessage.success("题目已保存"); editorVisible.value = false; load(); } finally { saving.value = false; } } -async function remove(row) { await ElMessageBox.confirm("删除后无法恢复,确认删除?", "删除题目", { type: "warning" }); await api.deletePlatformQuestion(row.id); ElMessage.success("已删除"); load(); } function pickImport() { if (!query.taskKey) return ElMessage.warning("请先选择归属项目,再导入题目"); fileInput.value.value = ""; fileInput.value.click(); } -async function importFile(event) { const file = event.target.files[0]; if (!file) return; if (!query.taskKey) return ElMessage.warning("请先选择归属项目"); const data = new FormData(); data.append("file", file); await api.importPlatformQuestions(data, query.taskKey); ElMessage.success("导入成功"); event.target.value = ""; load(); } +async function save() { if (!plainHtml(form.stem).trim()) return ElMessage.warning("请填写题干"); if (isChoice.value && (!form.answer.length || form.options.some((item) => !plainHtml(item.content).trim()))) return ElMessage.warning("请补全选项并设置正确答案"); if (form.type === "SHORT_ANSWER" && !plainHtml(form.answer).trim()) return ElMessage.warning("请填写参考答案"); saving.value = true; try { await (form.id ? api.updatePlatformQuestion(form.id, form) : api.createPlatformQuestion(form)); ElMessage.success("题目已保存"); editorVisible.value = false; load(); } finally { saving.value = false; } } +async function remove(row) { await ElMessageBox.confirm("删除后无法恢复,确认删除?", "删除题目", { type: "warning" }); await api.deletePlatformQuestion(row.id); ElMessage.success("已删除"); load(); } function pickImport() { fileInput.value.value = ""; fileInput.value.click(); } +async function importFile(event) { const file = event.target.files[0]; if (!file) return; const data = new FormData(); data.append("file", file); await api.importPlatformQuestions(data, query.taskKey); ElMessage.success("导入成功"); event.target.value = ""; load(); } async function downloadTemplate(format) { const extension = format === "excel" ? "xlsx" : "docx"; const blob = await api.downloadPlatformQuestionTemplate(format); saveAs(blob, `考试题库${format === "excel" ? "Excel" : "Word"}导入模板.${extension}`); } async function uploadMaterial({ file }) { uploading.value = true; try { const data = new FormData(); data.append("file", file); const result = await api.uploadPlatformQuestionAttachment(data); form.attachmentName = result.data.name; form.attachmentUrl = result.data.url; ElMessage.success("资料已上传"); } finally { uploading.value = false; } } -async function previewAi() { if (!aiForm.taskKey) return ElMessage.warning("请选择归属项目"); aiLoading.value = true; try { preview.value = (await api.previewPlatformAiQuestions(aiForm)).data.questions || []; } finally { aiLoading.value = false; } } async function confirmAi() { aiLoading.value = true; try { await api.confirmPlatformAiQuestions({ questions: preview.value }); ElMessage.success("题目已入库"); preview.value = []; aiVisible.value = false; load(); } finally { aiLoading.value = false; } } +async function previewAi() { aiLoading.value = true; try { preview.value = (await api.previewPlatformAiQuestions(aiForm)).data.questions || []; } finally { aiLoading.value = false; } } async function confirmAi() { aiLoading.value = true; try { await api.confirmPlatformAiQuestions({ questions: preview.value }); ElMessage.success("题目已入库"); preview.value = []; aiVisible.value = false; load(); } finally { aiLoading.value = false; } } function displayAnswer(value) { return Array.isArray(value) ? value.join("、") : value === true ? "正确" : value === false ? "错误" : value || "-"; }