|
|
@@ -63,16 +63,29 @@
|
|
|
highlight-current
|
|
|
:props="{ label: 'label', children: 'children' }"
|
|
|
@node-click="handleQuestionClick"
|
|
|
- />
|
|
|
+ >
|
|
|
+ <span class="question-node" slot-scope="{ node, data }">
|
|
|
+ <span>{{ node.label }}</span>
|
|
|
+ <el-tooltip
|
|
|
+ v-if="!isView && data.question"
|
|
|
+ content="删除试题"
|
|
|
+ placement="top"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ class="question-delete"
|
|
|
+ icon="el-icon-delete"
|
|
|
+ aria-label="删除试题"
|
|
|
+ @click.stop="handleDeleteQuestion(data)"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ </span>
|
|
|
+ </el-tree>
|
|
|
</div>
|
|
|
|
|
|
<!-- 右侧试题表单 -->
|
|
|
<div class="right-panel">
|
|
|
- <question-form
|
|
|
- ref="questionFormRef"
|
|
|
- v-model="form"
|
|
|
- :is-view="isView"
|
|
|
- />
|
|
|
+ <question-form ref="questionFormRef" v-model="form" :is-view="isView" />
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -93,6 +106,27 @@
|
|
|
<script>
|
|
|
import DeptSelect from '@/components/CommomSelect/dept-select.vue';
|
|
|
import QuestionForm from '../paperManagement/question-form.vue';
|
|
|
+ import {
|
|
|
+ deleteQuestion,
|
|
|
+ edit as updateQuestionBank,
|
|
|
+ getById,
|
|
|
+ getList,
|
|
|
+ save as saveQuestionBank
|
|
|
+ } from '@/api/questionBank/questionBank.js';
|
|
|
+
|
|
|
+ const questionTypeMap = {
|
|
|
+ 1: 'SINGLE',
|
|
|
+ 2: 'MULTIPLE',
|
|
|
+ 3: 'JUDGE',
|
|
|
+ 4: 'QUESTION_ANSWER'
|
|
|
+ };
|
|
|
+
|
|
|
+ const questionTypeReverseMap = {
|
|
|
+ SINGLE: '1',
|
|
|
+ MULTIPLE: '2',
|
|
|
+ JUDGE: '3',
|
|
|
+ QUESTION_ANSWER: '4'
|
|
|
+ };
|
|
|
|
|
|
const defBankForm = {
|
|
|
id: '',
|
|
|
@@ -102,6 +136,7 @@
|
|
|
};
|
|
|
|
|
|
const defQuestionForm = {
|
|
|
+ id: '',
|
|
|
sort: '',
|
|
|
content: '',
|
|
|
type: '1',
|
|
|
@@ -118,6 +153,8 @@
|
|
|
analysis: ''
|
|
|
};
|
|
|
|
|
|
+ const createQuestionList = () => [];
|
|
|
+
|
|
|
export default {
|
|
|
components: {
|
|
|
DeptSelect,
|
|
|
@@ -139,11 +176,7 @@
|
|
|
]
|
|
|
},
|
|
|
form: JSON.parse(JSON.stringify(defQuestionForm)),
|
|
|
- questionList: [
|
|
|
- { id: '1', label: '第1题' },
|
|
|
- { id: '2', label: '第2题' },
|
|
|
- { id: '3', label: '第3题' }
|
|
|
- ]
|
|
|
+ questionList: createQuestionList()
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -160,27 +193,44 @@
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
- open(row, type = 'add') {
|
|
|
+ async open(row, type = 'add') {
|
|
|
this.visible = true;
|
|
|
this.dialogType = type;
|
|
|
- if (row && row.id) {
|
|
|
- this.bankForm = JSON.parse(
|
|
|
- JSON.stringify({ ...defBankForm, ...row })
|
|
|
- );
|
|
|
+ const questionBankId = row && (row.id || row.questionBankId);
|
|
|
+ if (questionBankId) {
|
|
|
+ this.bankForm = {
|
|
|
+ id: questionBankId,
|
|
|
+ name: row.questionBankName || row.name || '',
|
|
|
+ deptId: row.departmentId || row.deptId || '',
|
|
|
+ deptName: row.departmentName || row.deptName || ''
|
|
|
+ };
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ await this.loadDetail(questionBankId, type === 'view');
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '获取题库详情失败');
|
|
|
+ this.cancel();
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
} else {
|
|
|
this.bankForm = JSON.parse(JSON.stringify(defBankForm));
|
|
|
+ this.questionList = createQuestionList();
|
|
|
+ this.form = JSON.parse(JSON.stringify(defQuestionForm));
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.treeRef && this.$refs.treeRef.setCurrentKey('1');
|
|
|
+ });
|
|
|
}
|
|
|
- this.form = JSON.parse(JSON.stringify(defQuestionForm));
|
|
|
this.$nextTick(() => {
|
|
|
this.$refs.bankForm && this.$refs.bankForm.clearValidate();
|
|
|
this.$refs.questionFormRef &&
|
|
|
this.$refs.questionFormRef.clearValidate();
|
|
|
- this.$refs.treeRef && this.$refs.treeRef.setCurrentKey('1');
|
|
|
});
|
|
|
},
|
|
|
cancel() {
|
|
|
this.bankForm = JSON.parse(JSON.stringify(defBankForm));
|
|
|
this.form = JSON.parse(JSON.stringify(defQuestionForm));
|
|
|
+ this.questionList = createQuestionList();
|
|
|
this.visible = false;
|
|
|
},
|
|
|
onDeptChange(deptId, node) {
|
|
|
@@ -191,12 +241,152 @@
|
|
|
console.log('搜索试题', this.questionSearch);
|
|
|
},
|
|
|
handleQuestionClick(data) {
|
|
|
- // TODO: 切换试题加载对应数据
|
|
|
- console.log('切换试题', data);
|
|
|
+ if (!data.question) return;
|
|
|
+ this.form = this.mapQuestionToForm(data.question);
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.questionFormRef &&
|
|
|
+ this.$refs.questionFormRef.clearValidate();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ async handleDeleteQuestion(data) {
|
|
|
+ try {
|
|
|
+ await this.$confirm('确认删除该试题吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ });
|
|
|
+ const message = await deleteQuestion(data.id);
|
|
|
+ await this.loadDetail(this.bankForm.id, false);
|
|
|
+ this.$message.success(message || '删除成功');
|
|
|
+ this.$emit('reload');
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel' && error !== 'close') {
|
|
|
+ this.$message.error(error.message || '删除失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mapQuestionToForm(question) {
|
|
|
+ const type = questionTypeReverseMap[question.questionType] || '1';
|
|
|
+ const options = [...(question.optionList || [])]
|
|
|
+ .sort((a, b) => Number(a.sortNo) - Number(b.sortNo))
|
|
|
+ .map((option, index) => ({
|
|
|
+ key: String.fromCharCode(65 + index),
|
|
|
+ content: option.optionContent || ''
|
|
|
+ }));
|
|
|
+ const correctAnswer = question.correctAnswer || '';
|
|
|
+ return {
|
|
|
+ id: question.id || '',
|
|
|
+ sort: question.sortNo === undefined ? '' : String(question.sortNo),
|
|
|
+ content: question.questionContent || '',
|
|
|
+ type,
|
|
|
+ category:
|
|
|
+ question.professionalType === undefined
|
|
|
+ ? ''
|
|
|
+ : String(question.professionalType),
|
|
|
+ score:
|
|
|
+ question.questionScore === undefined
|
|
|
+ ? ''
|
|
|
+ : String(question.questionScore),
|
|
|
+ options,
|
|
|
+ rightAnswer: type === '2' ? '' : correctAnswer,
|
|
|
+ rightAnswers:
|
|
|
+ type === '2' ? correctAnswer.split(',').filter((item) => item) : [],
|
|
|
+ analysis: question.questionExplanation || ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+ async loadDetail(questionBankId, selectFirstQuestion = true) {
|
|
|
+ const detail = await getById(questionBankId);
|
|
|
+ this.bankForm = {
|
|
|
+ id: detail.id || questionBankId,
|
|
|
+ name: detail.questionBankName || '',
|
|
|
+ deptId: detail.departmentId || '',
|
|
|
+ deptName: detail.departmentName || ''
|
|
|
+ };
|
|
|
+ const questions = [...(detail.questionList || [])].sort(
|
|
|
+ (a, b) => Number(a.sortNo) - Number(b.sortNo)
|
|
|
+ );
|
|
|
+ this.questionList = questions.map((question, index) => ({
|
|
|
+ id: question.id,
|
|
|
+ label: `第${question.sortNo || index + 1}题`,
|
|
|
+ question
|
|
|
+ }));
|
|
|
+ this.form =
|
|
|
+ selectFirstQuestion && questions.length
|
|
|
+ ? this.mapQuestionToForm(questions[0])
|
|
|
+ : JSON.parse(JSON.stringify(defQuestionForm));
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (selectFirstQuestion && questions.length && this.$refs.treeRef) {
|
|
|
+ this.$refs.treeRef.setCurrentKey(questions[0].id);
|
|
|
+ } else if (this.$refs.treeRef) {
|
|
|
+ this.$refs.treeRef.setCurrentKey(null);
|
|
|
+ }
|
|
|
+ });
|
|
|
},
|
|
|
handleReset() {
|
|
|
this.form = JSON.parse(JSON.stringify(defQuestionForm));
|
|
|
- this.$refs.questionFormRef && this.$refs.questionFormRef.clearValidate();
|
|
|
+ this.$refs.questionFormRef &&
|
|
|
+ this.$refs.questionFormRef.clearValidate();
|
|
|
+ },
|
|
|
+ toNumber(value) {
|
|
|
+ return value === '' || value === null || value === undefined
|
|
|
+ ? undefined
|
|
|
+ : Number(value);
|
|
|
+ },
|
|
|
+ buildSavePayload() {
|
|
|
+ const isMultiple = this.form.type === '2';
|
|
|
+ return {
|
|
|
+ questionBankId: this.bankForm.id || undefined,
|
|
|
+ questionBankName: this.bankForm.name,
|
|
|
+ departmentId: this.bankForm.deptId,
|
|
|
+ departmentName: this.bankForm.deptName,
|
|
|
+ question: {
|
|
|
+ questionId: this.form.id || undefined,
|
|
|
+ sortNo: this.toNumber(this.form.sort),
|
|
|
+ questionContent: this.form.content,
|
|
|
+ questionType: questionTypeMap[this.form.type],
|
|
|
+ professionalType: this.toNumber(this.form.category),
|
|
|
+ questionScore: this.toNumber(this.form.score),
|
|
|
+ correctAnswer: isMultiple
|
|
|
+ ? this.form.rightAnswers.join(',')
|
|
|
+ : this.form.rightAnswer,
|
|
|
+ questionExplanation: this.form.analysis,
|
|
|
+ optionList: this.form.options.map((item, index) => ({
|
|
|
+ optionContent: item.content,
|
|
|
+ sortNo: index + 1
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ async resolveQuestionBankId(saveResult) {
|
|
|
+ if (
|
|
|
+ typeof saveResult === 'number' ||
|
|
|
+ (typeof saveResult === 'string' && /^\d+$/.test(saveResult))
|
|
|
+ ) {
|
|
|
+ return saveResult;
|
|
|
+ }
|
|
|
+ const resultId =
|
|
|
+ typeof saveResult === 'object'
|
|
|
+ ? saveResult.id || saveResult.questionBankId
|
|
|
+ : undefined;
|
|
|
+ if (resultId) {
|
|
|
+ return resultId;
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await getList({
|
|
|
+ questionBankName: this.bankForm.name,
|
|
|
+ departmentId: this.bankForm.deptId,
|
|
|
+ pageNum: 1,
|
|
|
+ size: 20
|
|
|
+ });
|
|
|
+ const questionBank = (result.list || []).find(
|
|
|
+ (item) =>
|
|
|
+ item.questionBankName === this.bankForm.name &&
|
|
|
+ String(item.departmentId) === String(this.bankForm.deptId)
|
|
|
+ );
|
|
|
+ if (!questionBank) {
|
|
|
+ throw new Error('题库已保存,但未能获取题库ID');
|
|
|
+ }
|
|
|
+ return questionBank.id;
|
|
|
},
|
|
|
saveNext() {
|
|
|
this.$refs.bankForm.validate((bankValid) => {
|
|
|
@@ -204,14 +394,31 @@
|
|
|
this.$refs.questionFormRef.validate((valid) => {
|
|
|
if (valid) {
|
|
|
this.loading = true;
|
|
|
- setTimeout(() => {
|
|
|
- this.loading = false;
|
|
|
- this.$message.success('保存成功');
|
|
|
- this.form = JSON.parse(JSON.stringify(defQuestionForm));
|
|
|
- this.$refs.questionFormRef &&
|
|
|
- this.$refs.questionFormRef.clearValidate();
|
|
|
- // TODO: 切换到下一题
|
|
|
- }, 500);
|
|
|
+ const payload = this.buildSavePayload();
|
|
|
+ const saveRequest = this.bankForm.id
|
|
|
+ ? updateQuestionBank
|
|
|
+ : saveQuestionBank;
|
|
|
+ saveRequest(payload)
|
|
|
+ .then(async (saveResult) => {
|
|
|
+ if (!this.bankForm.id) {
|
|
|
+ this.bankForm.id = await this.resolveQuestionBankId(
|
|
|
+ saveResult
|
|
|
+ );
|
|
|
+ }
|
|
|
+ await this.loadDetail(this.bankForm.id, false);
|
|
|
+ this.$message.success('保存成功');
|
|
|
+ this.$emit('reload');
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.questionFormRef &&
|
|
|
+ this.$refs.questionFormRef.clearValidate();
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ this.$message.error(error.message || '保存失败');
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.loading = false;
|
|
|
+ });
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
@@ -270,6 +477,18 @@
|
|
|
border-color: #409eff;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .question-node {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ flex: 1;
|
|
|
+ padding-right: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-delete {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.right-panel {
|