题目引入编辑器
This commit is contained in:
parent
e3366af9ec
commit
ff9a181c99
@ -1,14 +1,20 @@
|
||||
<template>
|
||||
<el-dialog :model-value="visible" title="题目详情" width="720px" @close="handleClose">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="题目">{{ q?.question_title || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="题目">
|
||||
<div v-if="q?.question_title" v-html="q.question_title"></div>
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="题型">{{ displayType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="分值">{{ q?.score ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="答案">
|
||||
<span v-if="Array.isArray(answerDisplay)">{{ (answerDisplay || []).join(', ') }}</span>
|
||||
<span v-else>{{ answerDisplay || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="解析">{{ q?.question_analysis || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="解析">
|
||||
<div v-if="q?.question_analysis" v-html="q.question_analysis"></div>
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ formatTime(q?.create_time || q?.created_at) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ formatTime(q?.update_time || q?.updated_at) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@ -16,7 +22,11 @@
|
||||
<div v-if="Array.isArray(optionsDisplay) && optionsDisplay.length" style="margin-top: 16px;">
|
||||
<el-table :data="optionsDisplay" size="small" style="width: 100%">
|
||||
<el-table-column prop="label" label="选项" width="80" />
|
||||
<el-table-column prop="content" label="内容" />
|
||||
<el-table-column label="内容">
|
||||
<template #default="{ row }">
|
||||
<div v-html="row.content"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
@ -42,7 +52,11 @@ const typeOptions = ref([])
|
||||
|
||||
const q = computed(() => (props.model && (props.model.Question || props.model)) || null)
|
||||
const displayType = computed(() => {
|
||||
const typeValue = String(q.value?.question_type ?? '')
|
||||
// 兼容多种返回字段:question_type / QuestionType,根节点或 Question 内部
|
||||
const raw = q.value
|
||||
? (q.value.question_type ?? q.value.QuestionType)
|
||||
: (props.model?.question_type ?? props.model?.QuestionType)
|
||||
const typeValue = raw != null ? String(raw) : ''
|
||||
const found = typeOptions.value.find(opt => String(opt.dict_value) === typeValue)
|
||||
return found ? found.dict_label : typeValue
|
||||
})
|
||||
@ -89,4 +103,10 @@ function handleClose() {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 固定描述列表第一列宽度 */
|
||||
:deep(.el-descriptions__body) tr td:first-child {
|
||||
/* width: 300px; */
|
||||
min-width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<el-input-number v-model="form.score" :step="1" :min="0" :max="100" :precision="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="题目" prop="question_title">
|
||||
<el-input v-model="form.question_title" placeholder="请输入题目" type="textarea" :rows="3" />
|
||||
<WangEditor v-model="form.question_title" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
</template>
|
||||
|
||||
<el-form-item label="解析">
|
||||
<el-input v-model="form.question_analysis" type="textarea" :rows="3" placeholder="可选" />
|
||||
<WangEditor v-model="form.question_analysis" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
@ -115,10 +115,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue'
|
||||
import { ref, reactive, computed, watch, nextTick, onBeforeUnmount } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getDictItemsByCode } from '@/api/dict'
|
||||
import { getExamQuestions } from '@/api/exam'
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
@ -136,6 +137,16 @@ const duplicateDialogVisible = ref(false)
|
||||
const duplicateList = ref([])
|
||||
const dialogTitle = computed(() => (props.mode === 'edit' ? '编辑题目' : '新建题目'))
|
||||
|
||||
// 富文本编辑器:题目 & 解析
|
||||
const titleEditorRef = ref(null)
|
||||
const titleEditorHtml = ref('')
|
||||
const analysisEditorRef = ref(null)
|
||||
const analysisEditorHtml = ref('')
|
||||
|
||||
const toolbarConfig = {}
|
||||
const titleEditorConfig = { placeholder: '请输入题目' }
|
||||
const analysisEditorConfig = { placeholder: '可选' }
|
||||
|
||||
const defaultOptions = () => ([
|
||||
{ key: 'A', label: 'A', content: '' },
|
||||
{ key: 'B', label: 'B', content: '' },
|
||||
@ -159,6 +170,22 @@ const rules = {
|
||||
answer: [{ validator: validateAnswer, trigger: 'change' }]
|
||||
}
|
||||
|
||||
function handleTitleEditorCreated(editor) {
|
||||
titleEditorRef.value = editor
|
||||
}
|
||||
|
||||
function handleTitleEditorChange() {
|
||||
form.question_title = titleEditorHtml.value || ''
|
||||
}
|
||||
|
||||
function handleAnalysisEditorCreated(editor) {
|
||||
analysisEditorRef.value = editor
|
||||
}
|
||||
|
||||
function handleAnalysisEditorChange() {
|
||||
form.question_analysis = analysisEditorHtml.value || ''
|
||||
}
|
||||
|
||||
function validateAnswer(rule, value, callback) {
|
||||
if (!showAnswer.value) return callback()
|
||||
if (isMultipleType.value) {
|
||||
@ -216,7 +243,7 @@ function initForm() {
|
||||
if (props.model) {
|
||||
form.question_title = props.model.question_title || ''
|
||||
form.question_type = props.model.question_type != null ? String(props.model.question_type) : ''
|
||||
form.score = props.model.score ?? 5
|
||||
form.score = props.model.score ?? 2
|
||||
form.question_analysis = props.model.question_analysis || ''
|
||||
form.options = Array.isArray(props.model.options) && props.model.options.length > 0 ? props.model.options.map((o, i) => ({ key: o.key || String.fromCharCode(65 + i), label: o.label || String.fromCharCode(65 + i), content: o.content || '' })) : (isObjective.value ? defaultOptions() : [])
|
||||
if (Array.isArray(props.model.answer)) {
|
||||
@ -229,12 +256,16 @@ function initForm() {
|
||||
} else {
|
||||
form.question_title = ''
|
||||
form.question_type = ''
|
||||
form.score = 5
|
||||
form.score = 2
|
||||
form.question_analysis = ''
|
||||
form.options = defaultOptions()
|
||||
form.answer = ''
|
||||
multiAnswer.value = []
|
||||
}
|
||||
|
||||
// 同步富文本内容
|
||||
titleEditorHtml.value = form.question_title || ''
|
||||
analysisEditorHtml.value = form.question_analysis || ''
|
||||
}
|
||||
|
||||
async function handleNext() {
|
||||
@ -382,6 +413,15 @@ function highlightMatchedTitle(title) {
|
||||
}).join('')
|
||||
return highlightedTitle
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (titleEditorRef.value) {
|
||||
titleEditorRef.value.destroy()
|
||||
}
|
||||
if (analysisEditorRef.value) {
|
||||
analysisEditorRef.value.destroy()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -417,6 +457,12 @@ function highlightMatchedTitle(title) {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.rich-editor {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
.duplicate-tip {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user