|
|
@ -134,7 +134,8 @@ |
|
|
|
@update="e => updateSort(e, item)"> |
|
|
|
@update="e => updateSort(e, item)"> |
|
|
|
<div v-for="(ques, j) in item.examQuestions" :key="j" class="ques-item"> |
|
|
|
<div v-for="(ques, j) in item.examQuestions" :key="j" class="ques-item"> |
|
|
|
<el-checkbox v-model="ques.check"></el-checkbox> |
|
|
|
<el-checkbox v-model="ques.check"></el-checkbox> |
|
|
|
<div :class="['ques-info', { disabled: !ques.status, del: ques.isDel, repeat: ques.isRepeat }]"> |
|
|
|
{{ ques.repeat }} |
|
|
|
|
|
|
|
<div :class="['ques-info', { disabled: !ques.status, del: ques.isDel, repeat: ques.repeat }]"> |
|
|
|
<div class="top-line"> |
|
|
|
<div class="top-line"> |
|
|
|
<div class="labels"> |
|
|
|
<div class="labels"> |
|
|
|
<span class="label">{{ j + 1 }} / {{ item.examQuestions.length }}</span> |
|
|
|
<span class="label">{{ j + 1 }} / {{ item.examQuestions.length }}</span> |
|
|
@ -189,25 +190,27 @@ |
|
|
|
<Manual :visible.sync="manualVisible" :questionType.sync="curType.questionType" /> |
|
|
|
<Manual :visible.sync="manualVisible" :questionType.sync="curType.questionType" /> |
|
|
|
<Auto :visible.sync="autoVisible" /> |
|
|
|
<Auto :visible.sync="autoVisible" /> |
|
|
|
<Detail :visible.sync="quesVisible" :row.sync="curRow" :detailType.sync="detailType" @updateQues="updateQues" /> |
|
|
|
<Detail :visible.sync="quesVisible" :row.sync="curRow" :detailType.sync="detailType" @updateQues="updateQues" /> |
|
|
|
|
|
|
|
<RepeatQues :visible.sync="repeatVisible" :list.sync="repeatQues" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</template> |
|
|
|
<script> |
|
|
|
<script> |
|
|
|
import Setting from '@/setting' |
|
|
|
|
|
|
|
import Util from '@/libs/util' |
|
|
|
|
|
|
|
import dayjs from 'dayjs' |
|
|
|
|
|
|
|
import Decimal from 'decimal.js' |
|
|
|
|
|
|
|
import Draggable from 'vuedraggable' |
|
|
|
import Draggable from 'vuedraggable' |
|
|
|
import UeditorPlus from '@/components/ueditorPlus' |
|
|
|
import UeditorPlus from '@/components/ueditorPlus' |
|
|
|
import Breadcrumb from '@/components/breadcrumb' |
|
|
|
import Breadcrumb from '@/components/breadcrumb' |
|
|
|
import QuesConst from '@/const/ques' |
|
|
|
|
|
|
|
import TestPaperConst from '@/const/testPaper' |
|
|
|
|
|
|
|
import Template from './template' |
|
|
|
import Template from './template' |
|
|
|
import Manual from './manual' |
|
|
|
import Manual from './manual' |
|
|
|
import Auto from './auto' |
|
|
|
import Auto from './auto' |
|
|
|
|
|
|
|
import RepeatQues from './repeatQues' |
|
|
|
import Detail from '@/pages/ques/detail' |
|
|
|
import Detail from '@/pages/ques/detail' |
|
|
|
|
|
|
|
import Setting from '@/setting' |
|
|
|
|
|
|
|
import Util from '@/libs/util' |
|
|
|
|
|
|
|
import dayjs from 'dayjs' |
|
|
|
|
|
|
|
import QuesConst from '@/const/ques' |
|
|
|
|
|
|
|
import TestPaperConst from '@/const/testPaper' |
|
|
|
|
|
|
|
import Decimal from 'decimal.js' |
|
|
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
export default { |
|
|
|
components: { UeditorPlus, Breadcrumb, Template, Manual, Auto, Detail, Draggable }, |
|
|
|
components: { UeditorPlus, Breadcrumb, Template, Manual, Auto, RepeatQues, Detail, Draggable }, |
|
|
|
data () { |
|
|
|
data () { |
|
|
|
return { |
|
|
|
return { |
|
|
|
crumbs: [], |
|
|
|
crumbs: [], |
|
|
@ -325,6 +328,7 @@ export default { |
|
|
|
repeatVisible: false, |
|
|
|
repeatVisible: false, |
|
|
|
repeatQues: [], |
|
|
|
repeatQues: [], |
|
|
|
tempForm: {}, |
|
|
|
tempForm: {}, |
|
|
|
|
|
|
|
submiting: false, |
|
|
|
}; |
|
|
|
}; |
|
|
|
}, |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
computed: { |
|
|
@ -338,60 +342,6 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
mounted () { |
|
|
|
mounted () { |
|
|
|
function distributeScores (objects, totalScore) { |
|
|
|
|
|
|
|
const numObjects = objects.length; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果总分数小于数组长度,每个对象只能分配到一部分分数 |
|
|
|
|
|
|
|
const baseScore = totalScore / numObjects; |
|
|
|
|
|
|
|
const hasFraction = baseScore % 1 !== 0; // 判断是否有小数部分 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 分配整数部分 |
|
|
|
|
|
|
|
for (let i = 0; i < numObjects; i++) { |
|
|
|
|
|
|
|
objects[i].score = Math.floor(baseScore); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有小数部分,计算小数部分的总和 |
|
|
|
|
|
|
|
let fractionalPartSum = 0; |
|
|
|
|
|
|
|
if (hasFraction) { |
|
|
|
|
|
|
|
for (let i = 0; i < numObjects; i++) { |
|
|
|
|
|
|
|
fractionalPartSum += baseScore % 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 分配小数部分 |
|
|
|
|
|
|
|
let remainingObjects = numObjects; |
|
|
|
|
|
|
|
while (fractionalPartSum > 0) { |
|
|
|
|
|
|
|
objects[--remainingObjects].score += 1; |
|
|
|
|
|
|
|
fractionalPartSum -= 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果总分数不足以给每个对象分配至少一分 |
|
|
|
|
|
|
|
if (totalScore < numObjects) { |
|
|
|
|
|
|
|
// 计算每个对象应该获得的小数部分 |
|
|
|
|
|
|
|
const fractionalDistribution = Math.ceil(fractionalPartSum); |
|
|
|
|
|
|
|
for (let i = 0; i < fractionalDistribution; i++) { |
|
|
|
|
|
|
|
objects[i].score += 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return objects; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 示例 |
|
|
|
|
|
|
|
const objects = [ |
|
|
|
|
|
|
|
{ id: 1 }, |
|
|
|
|
|
|
|
{ id: 2 }, |
|
|
|
|
|
|
|
{ id: 3 }, |
|
|
|
|
|
|
|
{ id: 4 } |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const totalScore = 3; // 总分数小于数组长度 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const result = distributeScores(objects, totalScore); |
|
|
|
|
|
|
|
console.log(result); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.crumbs = [ |
|
|
|
this.crumbs = [ |
|
|
|
{ |
|
|
|
{ |
|
|
|
name: '试卷管理', |
|
|
|
name: '试卷管理', |
|
|
@ -473,8 +423,8 @@ export default { |
|
|
|
|
|
|
|
|
|
|
|
// 大纲题型选择回调 |
|
|
|
// 大纲题型选择回调 |
|
|
|
questionTypeChange (row) { |
|
|
|
questionTypeChange (row) { |
|
|
|
// 如果大题名称还是默认的名字(即5个题型的名字),那么修改了题型后需要同步修改大题名称,用户自定义了大题名称,则不修改 |
|
|
|
// 如果大题名称为空,或者还是默认的名字(即5个题型的名字),那么修改了题型后需要同步修改大题名称,用户自定义了大题名称,则不修改 |
|
|
|
if (this.questionTypes.find(e => e.name === row.outlineName)) row.outlineName = this.questionTypes.find(e => e.id === row.questionType).name |
|
|
|
if (!row.outlineName || this.questionTypes.find(e => e.name === row.outlineName)) row.outlineName = this.questionTypes.find(e => e.id === row.questionType).name |
|
|
|
}, |
|
|
|
}, |
|
|
|
// 试卷大纲添加行 |
|
|
|
// 试卷大纲添加行 |
|
|
|
addLine (i) { |
|
|
|
addLine (i) { |
|
|
@ -552,7 +502,7 @@ export default { |
|
|
|
data.avgValueList.map((e, i) => { |
|
|
|
data.avgValueList.map((e, i) => { |
|
|
|
// scores里有-1则不用循环 |
|
|
|
// scores里有-1则不用循环 |
|
|
|
if (!e.scores.includes(-1)) { |
|
|
|
if (!e.scores.includes(-1)) { |
|
|
|
const stem = document.querySelector(`#stem` + e.questionId) |
|
|
|
const stem = document.querySelector(`#stem` + e.questionVersionId) |
|
|
|
if (stem) { |
|
|
|
if (stem) { |
|
|
|
const inputs = stem.querySelectorAll('.fill-input') |
|
|
|
const inputs = stem.querySelectorAll('.fill-input') |
|
|
|
if (inputs) { |
|
|
|
if (inputs) { |
|
|
@ -695,21 +645,37 @@ export default { |
|
|
|
}, |
|
|
|
}, |
|
|
|
// 检查重复试题 |
|
|
|
// 检查重复试题 |
|
|
|
hasRepeatQues (list) { |
|
|
|
hasRepeatQues (list) { |
|
|
|
const result = list.filter(e => { |
|
|
|
list.forEach(e => { |
|
|
|
if (e.questionType !== 'fill_blank' && e.questionType !== 'essay') { |
|
|
|
list.forEach(n => { |
|
|
|
|
|
|
|
if (e.questionVersionId !== n.questionVersionId && !e.repeat) { |
|
|
|
// 选择和判断题则判断题型+题干+选项内容 |
|
|
|
// 选择和判断题则判断题型+题干+选项内容 |
|
|
|
|
|
|
|
if (e.questionType !== 'fill_blank' && e.questionType !== 'essay') { |
|
|
|
|
|
|
|
// 判断每个选项的内容是否相同 |
|
|
|
const opts = e.questionAnswerVersionsList |
|
|
|
const opts = e.questionAnswerVersionsList |
|
|
|
// 判断每个选项的内容是否相同(拿第一个选项的内容作基准) |
|
|
|
let diff = 0 |
|
|
|
const first = opts[0].optionText |
|
|
|
for (const j in opts) { |
|
|
|
const someOpt = opts.every(n => n.optionText === first) |
|
|
|
// debugger |
|
|
|
|
|
|
|
if (!n.questionAnswerVersionsList[j] || n.questionAnswerVersionsList[j].optionText !== opts[j].optionText) { |
|
|
|
|
|
|
|
diff = 1 |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return e.questionType === e.questionType && e.stem === e.stem && someOpt |
|
|
|
if (e.questionType === n.questionType && e.stem === n.stem && !diff) { |
|
|
|
|
|
|
|
e.repeat = true |
|
|
|
|
|
|
|
n.repeat = true |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// 填空/问答:题干+题型一样即重复 |
|
|
|
// 填空/问答:题干+题型一样即重复 |
|
|
|
return (e.questionType === e.questionType && e.stem === e.stem) |
|
|
|
if (e.questionType === n.questionType && e.stem === n.stem) { |
|
|
|
|
|
|
|
e.repeat = true |
|
|
|
|
|
|
|
n.repeat = true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
const result = list.filter(e => e.repeat) |
|
|
|
// 如果有重复的试题,则弹框显示这些试题 |
|
|
|
// 如果有重复的试题,则弹框显示这些试题 |
|
|
|
if (result.length) { |
|
|
|
if (result.length) { |
|
|
|
this.repeatVisible = true |
|
|
|
this.repeatVisible = true |
|
|
@ -737,13 +703,12 @@ export default { |
|
|
|
try { |
|
|
|
try { |
|
|
|
await this.$post(this.api.saveExamPaper, this.tempForm) |
|
|
|
await this.$post(this.api.saveExamPaper, this.tempForm) |
|
|
|
Util.successMsg('保存成功') |
|
|
|
Util.successMsg('保存成功') |
|
|
|
this.submiting = false |
|
|
|
|
|
|
|
this.back() |
|
|
|
this.back() |
|
|
|
} catch (e) { |
|
|
|
} finally { |
|
|
|
this.submiting = false |
|
|
|
this.submiting = false |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
// 提交 |
|
|
|
// 提交验证及处理数据 |
|
|
|
async validForm (status) { |
|
|
|
async validForm (status) { |
|
|
|
if (this.submiting) return false |
|
|
|
if (this.submiting) return false |
|
|
|
const { isCopy } = this // 复制 |
|
|
|
const { isCopy } = this // 复制 |
|
|
@ -819,11 +784,16 @@ export default { |
|
|
|
invalid = 1 |
|
|
|
invalid = 1 |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
e.examQuestions.map((n, j) => { |
|
|
|
|
|
|
|
n.outlineName = e.outlineName |
|
|
|
|
|
|
|
n.paperIndex = i |
|
|
|
|
|
|
|
n.quesIndex = j |
|
|
|
|
|
|
|
n.repeat = false |
|
|
|
|
|
|
|
}) |
|
|
|
allQues.push(...e.examQuestions) |
|
|
|
allQues.push(...e.examQuestions) |
|
|
|
} |
|
|
|
} |
|
|
|
if (invalid) return false |
|
|
|
if (invalid) return false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.submiting = true |
|
|
|
this.submiting = true |
|
|
|
|
|
|
|
|
|
|
|
form.particularYear = +(dayjs(form.particularYear).format('YYYY')) |
|
|
|
form.particularYear = +(dayjs(form.particularYear).format('YYYY')) |
|
|
@ -877,8 +847,7 @@ export default { |
|
|
|
if (isCopy) form.paperId = '' |
|
|
|
if (isCopy) form.paperId = '' |
|
|
|
this.tempForm = form |
|
|
|
this.tempForm = form |
|
|
|
|
|
|
|
|
|
|
|
// debugger |
|
|
|
if (this.hasRepeatQues(allQues)) return false |
|
|
|
// if (this.hasRepeatQues(allQues)) return false |
|
|
|
|
|
|
|
this.saveTestPaper() |
|
|
|
this.saveTestPaper() |
|
|
|
}, |
|
|
|
}, |
|
|
|
// 预览 |
|
|
|
// 预览 |
|
|
@ -982,8 +951,11 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&.repeat { |
|
|
|
&.repeat { |
|
|
|
|
|
|
|
border-color: #ff962a; |
|
|
|
|
|
|
|
|
|
|
|
&:after { |
|
|
|
&:after { |
|
|
|
content: '存在重复题'; |
|
|
|
content: '存在重复题'; |
|
|
|
|
|
|
|
background-color: #ff962a; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|