|
|
|
<template>
|
|
|
|
<div class="page">
|
|
|
|
<Breadcrumb :data="crumbs" />
|
|
|
|
<p class="page-name mb">试卷</p>
|
|
|
|
<el-form :model="form" :rules="rules" class="input-form model" ref="form" label-width="140px">
|
|
|
|
<el-form-item prop="name" label="试卷名称">
|
|
|
|
<el-input style="width: 940px" placeholder="请输入试卷名称" v-model="form.name" clearable maxlength="100" />
|
|
|
|
</el-form-item>
|
|
|
|
<div class="item-line">
|
|
|
|
<el-form-item prop="particularYear" label="年份">
|
|
|
|
<el-date-picker v-model="form.particularYear" type="year" placeholder="请选择年份" format="yyyy"
|
|
|
|
value-format="yyyy">
|
|
|
|
</el-date-picker>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item required label="所属试卷分类">
|
|
|
|
<el-cascader placeholder="请选择所属试卷分类" :options="types" v-model="form.classificationId" :props="cascaderProps"
|
|
|
|
clearable style="width: 100%">
|
|
|
|
</el-cascader>
|
|
|
|
</el-form-item>
|
|
|
|
</div>
|
|
|
|
<div class="item-line">
|
|
|
|
<el-form-item prop="paperType" label="建议用途">
|
|
|
|
<el-select v-model="form.paperType" clearable placeholder="请选择建议用途">
|
|
|
|
<el-option v-for="(item, i) in paperTypes" :key="i" :label="item.name" :value="item.id"></el-option>
|
|
|
|
</el-select>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item prop="suggestTime" label="估计用时">
|
|
|
|
<el-input placeholder="请输入估计用时" v-model.number="form.suggestTime">
|
|
|
|
<template slot="append">分钟</template>
|
|
|
|
</el-input>
|
|
|
|
</el-form-item>
|
|
|
|
</div>
|
|
|
|
<div class="item-line">
|
|
|
|
<el-form-item prop="professionals" label="所属专业">
|
|
|
|
<el-select v-model="form.professionalId" clearable placeholder="请选择所属专业">
|
|
|
|
<el-option v-for="(item, i) in professionals" :key="i" :label="item.professionalName"
|
|
|
|
:value="item.professionalId" filter></el-option>
|
|
|
|
</el-select>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item prop="difficult" label="试卷难度">
|
|
|
|
<el-select v-model="form.difficult" clearable placeholder="系统自动评估,也可手动选择">
|
|
|
|
<el-option v-for="(item, i) in difficults" :key="i" :label="item.name" :value="item.id"></el-option>
|
|
|
|
</el-select>
|
|
|
|
<el-tooltip placement="top">
|
|
|
|
<div class="diff-text" slot="content">
|
|
|
|
<p>基础题:难度系数0.2</p>
|
|
|
|
<p>普通题:难度系数0.4</p>
|
|
|
|
<p>较难题:难度系数0.6</p>
|
|
|
|
<p>困难题:难度系数0.8</p>
|
|
|
|
<p>(1)<span class="em">题型难度系数</span>=对应题型所有试题的难度系数之和/总试题数</p>
|
|
|
|
<p>(2)<span class="em">试卷总难度系数</span>=所有试题的难度系数之和/总试题数</p>
|
|
|
|
<p>(3)<span class="em">难度系数评价</span>:</p>
|
|
|
|
<div class="coe">
|
|
|
|
<p>简单:系数 <=0.3</p>
|
|
|
|
<p>普通:0.3<系数<=0.4</p>
|
|
|
|
<p>较难:0.4<系数<0.5</p>
|
|
|
|
<p>困难:系数>=0.5</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<i class="el-icon-question m-l-10" style="font-size: 16px;color: #8f8f8f;cursor: pointer;"></i>
|
|
|
|
</el-tooltip>
|
|
|
|
</el-form-item>
|
|
|
|
</div>
|
|
|
|
<el-form-item prop="remarks" label="试卷说明">
|
|
|
|
<Ueditor ref="remarks" @ready="remarksReady" />
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<div class="line"></div>
|
|
|
|
|
|
|
|
<div class="flex j-between m-b-20">
|
|
|
|
<p>默认模板</p>
|
|
|
|
<el-button type="primary" size="small" @click="showTemplate">选择大纲模板</el-button>
|
|
|
|
</div>
|
|
|
|
<el-table :data="form.paperOutline" stripe header-align="center" row-key="id">
|
|
|
|
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
|
|
|
|
<el-table-column label="大题" align="center" min-width="120">
|
|
|
|
<template slot-scope="scope">第{{ arabicToChinese(scope.$index + 1) }}大题</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="name" label="题型" align="center" min-width="120">
|
|
|
|
<template slot-scope="scope">
|
|
|
|
<el-select v-model="scope.row.questionType" placeholder="请选择题型" @change="questionTypeChange(scope.row)">
|
|
|
|
<el-option v-for="(item, i) in questionTypes" :key="i" :label="item.name" :value="item.id"></el-option>
|
|
|
|
</el-select>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="name" label="目标题数" align="center" min-width="120">
|
|
|
|
<template slot-scope="scope">
|
|
|
|
<el-input placeholder="请输入目标题数" v-model.number="scope.row.questionNum" type="number" />
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="name" label="目标分值" align="center" min-width="120">
|
|
|
|
<template slot-scope="scope">
|
|
|
|
<el-input placeholder="请输入目标分值" v-model="scope.row.targetScore" />
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="操作" align="center" width="120">
|
|
|
|
<template slot-scope="scope">
|
|
|
|
<i class="el-icon-circle-plus-outline action-icon m-r-10" @click="addLine(scope.$index)"></i>
|
|
|
|
<i class="el-icon-remove-outline action-icon" @click="delLine(scope.$index)"></i>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table>
|
|
|
|
<p class="m-t-10 m-b-20 text-right">目标总题数:{{ questionCount }};目标总分:{{ score }}</p>
|
|
|
|
|
|
|
|
<div class="line"></div>
|
|
|
|
|
|
|
|
<div class="flex j-between a-center">
|
|
|
|
<h6 class="page-name" style="margin-bottom: 0">选择试题</h6>
|
|
|
|
<div>
|
|
|
|
<el-button type="primary" @click="allocationAll">一键分配分值</el-button>
|
|
|
|
<el-button type="primary" @click="showAuto">自动选题</el-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<ul class="ques">
|
|
|
|
<li v-for="(item, i) in form.paperOutline" :key="i">
|
|
|
|
<div class="top">
|
|
|
|
<div>
|
|
|
|
<span class="serial">第{{ arabicToChinese(i + 1) }}大题</span>
|
|
|
|
<el-input placeholder="请输入大题名称" v-model="item.outlineName" />
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<el-button type="primary" @click="allocation(item)">一键分配分值</el-button>
|
|
|
|
<el-button type="primary" @click="showManualDia(item)">批量添加</el-button>
|
|
|
|
<el-button type="danger" @click="batchDelQues(item)">批量删除试题</el-button>
|
|
|
|
<i class="arrow el-icon-arrow-down" @click="item.shrink = !item.shrink"></i>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<p class="sum">本大题共{{ item.questionNum }}小题,共{{ item.targetScore }}分</p>
|
|
|
|
|
|
|
|
<div :class="['ques-wrap', { hide: item.shrink }]">
|
|
|
|
<div v-for="(ques, j) in item.examQuestions" :key="j" class="ques-item">
|
|
|
|
<el-checkbox v-model="ques.check"></el-checkbox>
|
|
|
|
<div :class="['ques-info', { disabled: !ques.status, del: ques.isDel, repeat: ques.isRepeat }]">
|
|
|
|
<div class="top-line">
|
|
|
|
<span class="label">{{ j + 1 }} / 10</span>
|
|
|
|
<span class="label">{{ questionTypes.find(e => e.id === item.questionType).name }}</span>
|
|
|
|
<div v-html="ques.stem"></div>
|
|
|
|
<p v-if="ques.questionType !== 'fill_blank'">(<el-input class="score" placeholder="请输入"
|
|
|
|
v-model="ques.score" />分)</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 单选、多选、判断的选项 -->
|
|
|
|
<template
|
|
|
|
v-if="ques.questionType !== 'fill_blank' && ques.questionType !== 'essay' && ques.questionAnswerVersionsList">
|
|
|
|
<div v-for="(opt, j) in ques.questionAnswerVersionsList" :key="j" class="opt">
|
|
|
|
<span>{{ numToLetter(j) }}. </span>
|
|
|
|
<div v-html="opt.optionText"></div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<div class="bottom-line">
|
|
|
|
<div class="correct">
|
|
|
|
【{{ ques.questionType === 'essay' ? '参考答案' : '正确答案' }}】:
|
|
|
|
<div v-html="getCorrectAnswer(ques)"></div>
|
|
|
|
</div>
|
|
|
|
<div class="actions">
|
|
|
|
<div class="sort">
|
|
|
|
<span>排序</span>
|
|
|
|
<el-input class="score" v-model="ques.serialNumber" />
|
|
|
|
</div>
|
|
|
|
<el-button type="text" @click="toQues(item, j, ques)">编辑试题</el-button>
|
|
|
|
<el-button type="text" @click="showManualDia(item, j, ques)">更换试题</el-button>
|
|
|
|
<el-button type="text" @click="showManualDia(item, j)">添加试题</el-button>
|
|
|
|
<el-button type="text" @click="delQues(item, j)">移除试题</el-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
<div class="btns">
|
|
|
|
<el-button @click="submit(1)">保存草稿</el-button>
|
|
|
|
<el-button type="primary" @click="submit(0)">发布</el-button>
|
|
|
|
<el-button v-if="$route.query.id" @click="preview">预览</el-button>
|
|
|
|
<el-button @click="back">取消</el-button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<Template :visible.sync="templateVisible" />
|
|
|
|
<Manual :visible.sync="manualVisible" :questionType.sync="curType.questionType" />
|
|
|
|
<Auto :visible.sync="autoVisible" />
|
|
|
|
<Detail :visible.sync="quesVisible" :row.sync="curRow" :detailType.sync="detailType" @updateQues="updateQues" />
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import Setting from '@/setting'
|
|
|
|
import Util from '@/libs/util'
|
|
|
|
import dayjs from 'dayjs'
|
|
|
|
import Ueditor from '@/components/ueditor'
|
|
|
|
import Breadcrumb from '@/components/breadcrumb'
|
|
|
|
import QuesConst from '@/const/ques'
|
|
|
|
import TestPaperConst from '@/const/testPaper'
|
|
|
|
import Template from './template'
|
|
|
|
import Manual from './manual'
|
|
|
|
import Auto from './auto'
|
|
|
|
import Detail from '@/pages/ques/detail'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
components: { Ueditor, Breadcrumb, Template, Manual, Auto, Detail },
|
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
crumbs: [],
|
|
|
|
questionTypes: QuesConst.questionTypes,
|
|
|
|
difficults: TestPaperConst.difficults,
|
|
|
|
paperTypes: TestPaperConst.paperTypes,
|
|
|
|
arabicToChinese: Util.arabicToChinese,
|
|
|
|
numToLetter: Util.numToLetter,
|
|
|
|
paperId: this.$route.query.paperId,
|
|
|
|
libraryId: this.$route.query.libraryId,
|
|
|
|
isCopy: this.$route.query.isCopy,
|
|
|
|
headers: {
|
|
|
|
token: Util.local.get(Setting.tokenKey)
|
|
|
|
},
|
|
|
|
types: [],
|
|
|
|
cascaderProps: {
|
|
|
|
checkStrictly: true,
|
|
|
|
label: "classificationName",
|
|
|
|
value: "classificationId"
|
|
|
|
},
|
|
|
|
professionals: [],
|
|
|
|
|
|
|
|
form: {
|
|
|
|
classificationId: +this.$route.query.classificationId || '',
|
|
|
|
difficult: '',
|
|
|
|
libraryId: '',
|
|
|
|
name: '',
|
|
|
|
paperMethod: '',
|
|
|
|
paperType: '',
|
|
|
|
particularYear: '',
|
|
|
|
professionalId: '',
|
|
|
|
remarks: '',
|
|
|
|
score: '',
|
|
|
|
questionCount: 0,
|
|
|
|
suggestTime: '',
|
|
|
|
paperOutline: [
|
|
|
|
{
|
|
|
|
examQuestions: [],
|
|
|
|
outlineName: '单选题',
|
|
|
|
questionNum: '10',
|
|
|
|
questionType: 'single_choice',
|
|
|
|
targetScore: '20.0',
|
|
|
|
shrink: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
examQuestions: [],
|
|
|
|
outlineName: '多选题',
|
|
|
|
questionNum: '5',
|
|
|
|
questionType: 'multiple_choice',
|
|
|
|
targetScore: '15.0',
|
|
|
|
shrink: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
examQuestions: [],
|
|
|
|
outlineName: '判断题',
|
|
|
|
questionNum: '5',
|
|
|
|
questionType: 'judgement',
|
|
|
|
targetScore: '10.0',
|
|
|
|
shrink: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
examQuestions: [],
|
|
|
|
outlineName: '填空题',
|
|
|
|
questionNum: '5',
|
|
|
|
questionType: 'fill_blank',
|
|
|
|
targetScore: '15.0',
|
|
|
|
shrink: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
examQuestions: [],
|
|
|
|
outlineName: '问答题',
|
|
|
|
questionNum: '3',
|
|
|
|
questionType: 'essay',
|
|
|
|
targetScore: '40.0',
|
|
|
|
shrink: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
rules: {
|
|
|
|
name: [
|
|
|
|
{ required: true, message: '请输入试卷名称', trigger: 'blur' }
|
|
|
|
],
|
|
|
|
particularYear: [
|
|
|
|
{ required: true, message: '请选择年份', trigger: 'change' }
|
|
|
|
],
|
|
|
|
paperType: [
|
|
|
|
{ required: true, message: '请选择建议用途', trigger: 'change' }
|
|
|
|
],
|
|
|
|
suggestTime: [
|
|
|
|
{ required: true, message: '请输入估计用时', trigger: 'blur' }
|
|
|
|
],
|
|
|
|
name: [
|
|
|
|
{ required: true, message: '请输入试卷名称', trigger: 'blur' }
|
|
|
|
],
|
|
|
|
},
|
|
|
|
templateVisible: false,
|
|
|
|
manualVisible: false,
|
|
|
|
curType: {},
|
|
|
|
curQuesIndex: 0,
|
|
|
|
curQues: null,
|
|
|
|
|
|
|
|
autoVisible: false,
|
|
|
|
|
|
|
|
|
|
|
|
quesVisible: false,
|
|
|
|
curRow: {},
|
|
|
|
detailType: '',
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
// 目标总题数
|
|
|
|
questionCount () {
|
|
|
|
return this.form.paperOutline.reduce((e, j) => (e += +j.questionNum), 0)
|
|
|
|
},
|
|
|
|
// 目标总分
|
|
|
|
score () {
|
|
|
|
return this.form.paperOutline.reduce((e, j) => (e += +j.targetScore), 0)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted () {
|
|
|
|
this.crumbs = [
|
|
|
|
{
|
|
|
|
name: '试卷管理',
|
|
|
|
route: 'list',
|
|
|
|
query: {
|
|
|
|
id: this.libraryId
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: '创建试卷'
|
|
|
|
},
|
|
|
|
]
|
|
|
|
this.getDetail()
|
|
|
|
this.getType()
|
|
|
|
this.getProfessional()
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
// 获取试卷详情
|
|
|
|
async getDetail () {
|
|
|
|
try {
|
|
|
|
const { paperId } = this
|
|
|
|
if (paperId) {
|
|
|
|
const res = await this.$get(this.api.examPaperDetails, {
|
|
|
|
id: paperId
|
|
|
|
})
|
|
|
|
const r = res.examPaper
|
|
|
|
const paper = r.paperOutline
|
|
|
|
paper.map(e => {
|
|
|
|
e.shrink = false
|
|
|
|
e.examQuestions.map(n => {
|
|
|
|
Object.assign(n, n.question)
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
if (r.particularYear) r.particularYear = r.particularYear + ''
|
|
|
|
this.form = r
|
|
|
|
// this.answerAnalysis = opts[0].answerAnalysis
|
|
|
|
|
|
|
|
}
|
|
|
|
} catch (e) { }
|
|
|
|
},
|
|
|
|
// 获取试卷分类
|
|
|
|
async getType () {
|
|
|
|
try {
|
|
|
|
const res = await this.$post(this.api.examClassificationList, {
|
|
|
|
libraryId: this.libraryId,
|
|
|
|
})
|
|
|
|
const data = res.treeList
|
|
|
|
this.handleType(data)
|
|
|
|
this.types = data
|
|
|
|
} catch (e) { }
|
|
|
|
},
|
|
|
|
// 获取所属专业
|
|
|
|
async getProfessional () {
|
|
|
|
try {
|
|
|
|
const res = await this.$post(this.api.queryProfessional, {
|
|
|
|
pageNum: 1,
|
|
|
|
pageSize: 1000,
|
|
|
|
})
|
|
|
|
this.professionals = res.pageList.records
|
|
|
|
} catch (e) { }
|
|
|
|
},
|
|
|
|
// 处理树形
|
|
|
|
handleType (list) {
|
|
|
|
list.map(e => {
|
|
|
|
if (e.children && e.children.length) {
|
|
|
|
this.handleType(e.children)
|
|
|
|
} else {
|
|
|
|
delete e.children
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
// 试卷说明富文本加载完毕回调
|
|
|
|
remarksReady (editor) {
|
|
|
|
this.form.remarks && editor.setContent(this.form.remarks)
|
|
|
|
},
|
|
|
|
// 选择大纲模板
|
|
|
|
showTemplate () {
|
|
|
|
this.templateVisible = true
|
|
|
|
},
|
|
|
|
|
|
|
|
// 大纲题型选择回调
|
|
|
|
questionTypeChange (row) {
|
|
|
|
// 如果大题名称还是默认的名字(即5个题型的名字),那么修改了题型后需要同步修改大题名称,用户自定义了大题名称,则不修改
|
|
|
|
if (this.questionTypes.find(e => e.name === row.outlineName)) row.outlineName = this.questionTypes.find(e => e.id === row.questionType).name
|
|
|
|
},
|
|
|
|
// 试卷大纲添加行
|
|
|
|
addLine (i) {
|
|
|
|
this.form.paperOutline.splice(i + 1, 0, {
|
|
|
|
examQuestions: [],
|
|
|
|
outlineName: '',
|
|
|
|
questionNum: '',
|
|
|
|
questionType: '',
|
|
|
|
targetScore: '',
|
|
|
|
shrink: false,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
// 试卷大纲移除行
|
|
|
|
delLine (i) {
|
|
|
|
this.form.paperOutline.length > 1 && this.form.paperOutline.splice(i, 1)
|
|
|
|
},
|
|
|
|
|
|
|
|
// 一键分配所有题目的分值
|
|
|
|
async allocationAll () {
|
|
|
|
const paper = this.form.paperOutline
|
|
|
|
const { data } = await this.$post(this.api.avgValues, {
|
|
|
|
avgValueList: paper.map(e => {
|
|
|
|
return {
|
|
|
|
questionNum: e.examQuestions.length,
|
|
|
|
targetScore: e.targetScore,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
if (data && data.avgValueList && data.avgValueList.length) {
|
|
|
|
data.avgValueList.map((e, i) => {
|
|
|
|
// scores里有-1则不用循环
|
|
|
|
e.scores.includes(-1) || e.scores.map((n, j) => {
|
|
|
|
this.$set(paper[i].examQuestions[j], 'score', n)
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// 自动选题
|
|
|
|
showAuto () {
|
|
|
|
this.autoVisible = true
|
|
|
|
},
|
|
|
|
// 处理正确答案的显示
|
|
|
|
getCorrectAnswer (e) {
|
|
|
|
if (e.questionType === 'fill_blank') { // 填空题
|
|
|
|
// answerData里存了该填空题所有填空项的正确答案及分值占比,对象转化为字符串存起来了
|
|
|
|
let data = e.questionAnswerVersionsList[0].answerData
|
|
|
|
if (data) {
|
|
|
|
data = JSON.parse(data)
|
|
|
|
let result = ''
|
|
|
|
data.map((e, i) => {
|
|
|
|
result += `填空项${i + 1}:${e.fills.map(n => n.val).join('|')};`
|
|
|
|
})
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
return ''
|
|
|
|
} else if (e.questionType === 'essay') { // 问答题取参考答案
|
|
|
|
return e.questionAnswerVersionsList[0].referenceAnswer || '暂无'
|
|
|
|
} else {
|
|
|
|
const correct = e.questionAnswerVersionsList.filter(e => e.answerIsCorrect)
|
|
|
|
return correct ? (e.questionType === 'judgement' ? correct[0].optionText : correct.map(e => Util.numToLetter(e.optionNumber - 1)).join('')) : '' // 单选多选显示ABC选项,判断题显示选项内容
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// 编辑试题
|
|
|
|
toQues (item, i, ques) {
|
|
|
|
this.curType = item
|
|
|
|
this.curQuesIndex = i
|
|
|
|
this.curRow = ques
|
|
|
|
this.quesVisible = true
|
|
|
|
this.detailType = 4
|
|
|
|
},
|
|
|
|
// 编辑试题完成后把新的试题信息带过来
|
|
|
|
updateQues (ques, id) {
|
|
|
|
ques.questionAnswerVersionsList = ques.questionAnswerVersions
|
|
|
|
delete ques.questionAnswerVersions
|
|
|
|
ques.questionVersionId = id
|
|
|
|
this.curType.examQuestions[this.curQuesIndex] = Object.assign(this.curRow, ques)
|
|
|
|
},
|
|
|
|
// 添加题目
|
|
|
|
showManualDia (item, i, ques) {
|
|
|
|
this.curType = item
|
|
|
|
this.curQuesIndex = i
|
|
|
|
this.curQues = ques
|
|
|
|
this.manualVisible = true
|
|
|
|
},
|
|
|
|
// 批量删除试题
|
|
|
|
async batchDelQues (item) {
|
|
|
|
try {
|
|
|
|
const checked = item.examQuestions.filter(e => e.check)
|
|
|
|
if (checked.length) {
|
|
|
|
await this.$confirm(`确认要删除吗?`, '提示', {
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
type: 'warning',
|
|
|
|
closeOnClickModal: false,
|
|
|
|
})
|
|
|
|
checked.map(e => {
|
|
|
|
const cur = item.examQuestions.findIndex(n => n.questionVersionId === e.questionVersionId)
|
|
|
|
cur >= 0 && item.examQuestions.splice(cur, 1)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Util.warningMsg('请选择数据')
|
|
|
|
}
|
|
|
|
} catch (e) { }
|
|
|
|
},
|
|
|
|
// 大题一键分配分值
|
|
|
|
allocation (item) {
|
|
|
|
const total = +item.targetScore
|
|
|
|
const len = item.examQuestions.length
|
|
|
|
const quotient = Math.floor(total / len) // 平均分配的整数部分
|
|
|
|
let remainder = total % len // 余数
|
|
|
|
// 分配平均分
|
|
|
|
item.examQuestions.forEach(e => {
|
|
|
|
e.score = quotient
|
|
|
|
})
|
|
|
|
|
|
|
|
// 处理余数
|
|
|
|
while (remainder > 0) {
|
|
|
|
for (let i = 0; i < len && remainder > 0; i++) {
|
|
|
|
item.examQuestions[i].score += 1
|
|
|
|
remainder -= 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// 移除试题
|
|
|
|
delQues (item, i) {
|
|
|
|
item.examQuestions.splice(i, 1)
|
|
|
|
},
|
|
|
|
// 提交
|
|
|
|
async submit (status) {
|
|
|
|
if (this.submiting) return false
|
|
|
|
this.$refs.form.validate(async (valid) => {
|
|
|
|
if (valid) {
|
|
|
|
const { isCopy } = this // 复制
|
|
|
|
const form = _.cloneDeep(this.form)
|
|
|
|
if (isNaN(form.suggestTime) || form.suggestTime < 1 || form.suggestTime % 1) return Util.warningMsg('估计用时请输入正整数')
|
|
|
|
const paper = form.paperOutline
|
|
|
|
let invalid = 0
|
|
|
|
for (const i in paper) {
|
|
|
|
const e = paper[i]
|
|
|
|
if (!e.questionType) {
|
|
|
|
Util.warningMsg('请选择题型')
|
|
|
|
invalid = 1
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if (!e.questionNum) {
|
|
|
|
Util.warningMsg('请输入目标题数')
|
|
|
|
invalid = 1
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if (!e.targetScore) {
|
|
|
|
Util.warningMsg('请输入目标分值')
|
|
|
|
invalid = 1
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// 该大题里的小题总数需等于目标题数
|
|
|
|
if (+e.questionNum !== e.examQuestions.length) {
|
|
|
|
Util.warningMsg(`第${this.arabicToChinese(i + 1)}大题的小题总数跟目标题数不一致,请重新修改`)
|
|
|
|
invalid = 1
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// 该大题里的小题总分需等于目标分值
|
|
|
|
if (+e.targetScore !== e.examQuestions.reduce((e, j) => (e += +j.score), 0)) {
|
|
|
|
Util.warningMsg(`第${this.arabicToChinese(i + 1)}大题的小题总分跟目标分值不一致,请重新修改`)
|
|
|
|
invalid = 1
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// 试题里存在被禁用或者被删除的,不予提交
|
|
|
|
if (e.examQuestions.some(e => !e.status || e.isDel)) {
|
|
|
|
Util.warningMsg(`当前试卷存在已被删除或已被禁用的试题,请进行相应调整 `)
|
|
|
|
invalid = 1
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// debugger
|
|
|
|
if (invalid) return false
|
|
|
|
|
|
|
|
this.submiting = true
|
|
|
|
form.particularYear = +(dayjs(form.particularYear).format('YYYY'))
|
|
|
|
form.libraryId = this.libraryId
|
|
|
|
form.questionCount = this.questionCount
|
|
|
|
form.remarks = this.$refs.remarks.getUEContent()
|
|
|
|
form.score = this.score
|
|
|
|
form.status = status
|
|
|
|
|
|
|
|
paper.map(e => {
|
|
|
|
// 复制不需要传id
|
|
|
|
if (isCopy) {
|
|
|
|
e.paperId = ''
|
|
|
|
e.outlineId = ''
|
|
|
|
}
|
|
|
|
e.targetScore = +e.targetScore
|
|
|
|
e.examQuestions = e.examQuestions.map(n => {
|
|
|
|
return {
|
|
|
|
questionVersionId: n.questionVersionId,
|
|
|
|
serialNumber: n.serialNumber,
|
|
|
|
score: +n.score,
|
|
|
|
paperId: !isCopy && this.paperId || '',
|
|
|
|
outlineId: !isCopy && e.outlineId || '',
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
if (typeof form.classificationId === 'object') form.classificationId = form.classificationId[form.classificationId.length - 1]
|
|
|
|
form.questionType = [...new Set(paper.map(e => e.questionType))].join('、') // 试题类型
|
|
|
|
form.createSource = 1
|
|
|
|
if (isCopy) form.paperId = ''
|
|
|
|
try {
|
|
|
|
await this.$post(this.api.saveExamPaper, form)
|
|
|
|
Util.successMsg('保存成功')
|
|
|
|
this.submiting = false
|
|
|
|
this.back()
|
|
|
|
} catch (e) {
|
|
|
|
this.submiting = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
// 返回
|
|
|
|
back () {
|
|
|
|
this.$router.back()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.ques {
|
|
|
|
li {
|
|
|
|
padding: 15px;
|
|
|
|
margin: 20px 0 10px;
|
|
|
|
border: 1px solid #dbdbdb;
|
|
|
|
}
|
|
|
|
|
|
|
|
.top {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.serial {
|
|
|
|
margin-right: 10px;
|
|
|
|
font-size: 13px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.arrow {
|
|
|
|
margin-left: 10px;
|
|
|
|
font-size: 16px;
|
|
|
|
color: #a9a9a9;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
.sum {
|
|
|
|
margin: 15px 0;
|
|
|
|
font-size: 13px;
|
|
|
|
color: $main-color;
|
|
|
|
}
|
|
|
|
|
|
|
|
.ques-wrap {
|
|
|
|
// transition: 2s;
|
|
|
|
transition-property: height;
|
|
|
|
transition-duration: .25s, 1s;
|
|
|
|
|
|
|
|
&.hide {
|
|
|
|
height: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.ques-item {
|
|
|
|
display: flex;
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.ques-info {
|
|
|
|
position: relative;
|
|
|
|
width: calc(100% - 40px);
|
|
|
|
padding: 10px;
|
|
|
|
margin-left: 20px;
|
|
|
|
border: 1px solid #e9e9e9;
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
&.disabled,
|
|
|
|
&.del,
|
|
|
|
&.repeat {
|
|
|
|
border-color: #f00;
|
|
|
|
|
|
|
|
&:after {
|
|
|
|
content: '已 禁 用';
|
|
|
|
position: absolute;
|
|
|
|
top: 17px;
|
|
|
|
right: -33px;
|
|
|
|
padding: 5px 36px;
|
|
|
|
font-size: 14px;
|
|
|
|
color: #fff;
|
|
|
|
background-color: #f00;
|
|
|
|
transform: rotate(45deg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&.del {
|
|
|
|
&:after {
|
|
|
|
content: '已 删 除';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&.repeat {
|
|
|
|
&:after {
|
|
|
|
content: '存在重复题';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.top-line {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
margin-bottom: 10px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.label {
|
|
|
|
padding: 3px;
|
|
|
|
margin-right: 10px;
|
|
|
|
font-size: 12px;
|
|
|
|
line-height: 1;
|
|
|
|
color: $main-color;
|
|
|
|
border: 1px solid;
|
|
|
|
border-radius: 2px;
|
|
|
|
}
|
|
|
|
|
|
|
|
/deep/.score {
|
|
|
|
width: 70px;
|
|
|
|
margin: 0 10px;
|
|
|
|
|
|
|
|
.el-input__inner {
|
|
|
|
height: 28px;
|
|
|
|
line-height: 28px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.opt {
|
|
|
|
display: flex;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
padding-left: 10px;
|
|
|
|
margin-bottom: 5px;
|
|
|
|
font-size: 14px;
|
|
|
|
color: #707070;
|
|
|
|
line-height: 1.6;
|
|
|
|
}
|
|
|
|
|
|
|
|
.bottom-line {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.correct {
|
|
|
|
display: flex;
|
|
|
|
font-size: 13px;
|
|
|
|
color: #333;
|
|
|
|
}
|
|
|
|
|
|
|
|
.actions {
|
|
|
|
display: inline-flex;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
.el-button {
|
|
|
|
margin-left: 20px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.sort {
|
|
|
|
display: inline-flex;
|
|
|
|
align-items: center;
|
|
|
|
font-size: 12px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.diff-text {
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
|
|
.em {
|
|
|
|
font-weight: 600;
|
|
|
|
}
|
|
|
|
|
|
|
|
.coe {
|
|
|
|
padding-left: 32px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|