yujialong 3 months ago
parent 3de7b8fad9
commit babfe30a92
  1. 64
      public/static/ueditorPlus/themes/iframe.css
  2. 28
      src/pages/ques/detail/index.vue
  3. 8
      src/pages/ques/list/index.vue
  4. 19
      src/pages/testPaper/detail/auto.vue
  5. 88
      src/pages/testPaper/detail/index.vue
  6. 36
      src/pages/testPaper/detail/repeatQues.vue
  7. 4
      src/pages/testPaper/list/index.vue
  8. 47
      src/pages/testPaper/preview/index.vue

@ -1,67 +1,3 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
font-size: 14px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
a {
color: #09f;
text-decoration: none;
}
a:hover,
a:focus {
color: #09f;
text-decoration: none;
}
blockquote {
padding: 0 0 0 15px;
margin: 0 0 18px;
border-left: 5px solid #EEE;
}
img + br {
display: block;
padding: 4px 0;
content: ' ';
}
body p {
margin-bottom: 1em;
}
iframe {
border: none;
}
img {
max-width: 100%;
}
img[data-word-image] {
cursor: pointer;
}
pre {
margin: .5em 0;
padding: .4em .6em;
border-radius: 8px;
background: #f8f8f8;
line-height: 1.5;
}
/*交互操作*/
img {
cursor: pointer;
}
.edui-quick-operate-active {
background: #E6ECFF;
}
.edui-for-gapfilling {
content: '点击插入填空题';
font-size: 14px;

@ -36,10 +36,11 @@
</el-select>
</el-form-item>
<el-form-item prop="knowledgePointIds" label="所属知识点">
<el-cascader class="m-r-10" placeholder="请选择所属知识点" v-model="form.knowledgePointIds" :options="knowledges"
<el-cascader class="m-r-10" placeholder="请选择所属知识点" :disabled="!form.questionBankId"
v-model="form.knowledgePointIds" :options="knowledges"
:props="{ value: 'id', label: 'name', multiple: true, checkStrictly: true }" :show-all-levels="false"
filterable clearable></el-cascader>
<el-button @click="toKp">设置</el-button>
<el-button v-if="!paperType" @click="toKp">设置</el-button>
</el-form-item>
</div>
<div class="item-line">
@ -175,7 +176,7 @@
<div class="btns">
<template v-if="detailType !== 2">
<el-button v-if="detailType === 1" type="primary" :loading="submiting && keep === 0"
@click="submit(1)">保存</el-button>
@click="submit(0)">保存</el-button>
<template v-else>
<el-button type="primary" :loading="submiting && keep === 0" @click="submit(0)">保存</el-button>
<el-button type="primary" :loading="submiting && keep === 1" @click="submit(1)">{{ questionId ? '保存并同步更新相似题'
@ -188,9 +189,16 @@
</div>
<el-dialog title="提示" :visible.sync="repeatVisible" width="800px" :close-on-click-modal="false">
<template v-if="form.questionBankId">
<el-alert title="您确定要保存对本题所做的更改吗?更新后,所有关联的试卷将会自动更新" type="warning" effect="dark" :closable="false" />
<p class="m-t-10 m-b-10 fs-12">系统已经识别出题库中有与本题相似的其他题目如果您选择同步更新这些相似题目那么所有关联的试卷将会自动更新</p>
<el-table class="m-t-10" :data="repeats" stripe header-align="center" row-key="id">
</template>
<el-alert v-else title="与题库中已有的试题重复(如下),是否继续保存?" type="warning" effect="dark" :closable="false" />
<el-table class="m-t-10" :data="repeats" stripe header-align="center" @selection-change="handleSelectionChange"
row-key="id">
<el-table-column v-if="form.questionBankId" type="selection" width="45" align="center"
:reserve-selection="true"></el-table-column>
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
<el-table-column prop="name" label="题型" align="center" min-width="120">
<template slot-scope="scope">{{ questionTypes.find(e => e.id === scope.row.questionType) ?
@ -303,6 +311,7 @@ export default {
keep: 0,
repeatVisible: false,
multipleSelection: [],
repeats: [],
kpVisible: false,
@ -440,7 +449,7 @@ export default {
//
if (this.paperType > 1) {
this.questionBankName = r.questionBankName
// this.questionBankCategory = r.questionBankCategory
this.questionBankCategory = r.questionBankCategory
this.$nextTick(() => {
this.getKnowledge()
})
@ -648,6 +657,9 @@ export default {
uploadSuccess (file) {
this.form.stemAttachment = file.url
},
handleSelectionChange (val) {
this.multipleSelection = val
},
//
submit (keep) {
if (this.submiting) return false
@ -754,9 +766,9 @@ export default {
const form = this.tempForm
const type = this.detailType
const paperEdit = this.curQues //
// type: 1()234createNewVersion
const res = await this.$post(this.api[paperEdit ? 'createNewVersion' : type === 1 || !form.questionId ? 'addQuestion' : 'updateQuestion'], form)
paperEdit && this.$emit('updateQues', form, res.questionVersionId)
// type: 1()234
await this.$post(this.api[type === 1 || !form.questionId ? 'addQuestion' : 'updateQuestion'], form)
paperEdit && this.$emit('updateQues', form)
Util.successMsg('保存成功')
!form.questionId && this.keep ? this.init(form) : this.back()

@ -148,7 +148,7 @@
<ul class="papers">
<li v-for="(item, i) in curUsePapers" :key="i">
<p>{{ item.name }}</p>
<i class="el-icon-edit m-l-10"></i>
<i class="el-icon-edit m-l-10" @click="toPaper(item)"></i>
</li>
</ul>
</div>
@ -401,7 +401,7 @@ export default {
this.page = val
this.getList()
},
handleSelectionChange (val) { //
handleSelectionChange (val) {
this.multipleSelection = val
},
initData () {
@ -502,6 +502,10 @@ export default {
this.delVisible = false
this.getList()
},
//
toPaper (row) {
this.$router.push(`/testPaper/detail?paperId=${row.paperId}&libraryId=${row.libraryId}&classificationId=${row.classificationId}`)
},
//
async batchRemove () {
const list = this.multipleSelection

@ -392,8 +392,7 @@ export default {
},
//
handleQuesList () {
const list = _.cloneDeep(this.$parent.form.paperOutline)
this.list = list
this.list = _.cloneDeep(this.$parent.form.paperOutline)
},
//
async clearChecked () {
@ -435,18 +434,6 @@ export default {
item.knowledges.length || this.checked.splice(i, 1) //
this.knowledgeCheck = false
},
//
async getList () {
try {
const res = await this.$post(this.api.examPaperTemplateList, {
pageNum: this.page,
pageSize: this.pageSize,
...this.filter
})
this.list = res.pageList.records
this.total = res.pageList.total
} catch (e) { }
},
//
@ -526,6 +513,8 @@ export default {
invalid = 1
break
}
total = Decimal(total).add(e.randomDifficulty || 0).toNumber()
e.count = total
totalCount = Decimal(totalCount).add(total).toNumber()
}
if (invalid) return false
@ -557,7 +546,7 @@ export default {
let invalid = 0
let hasQues = 0
list.map((e, i) => {
if (+e.questionNum !== res.list[i].questions.length) invalid = 1
if (+e.count !== res.list[i].questions.length) invalid = 1
if (e.examQuestions.length) hasQues = 1
e.score = 0
})

@ -160,14 +160,18 @@
<p class="fill-title">每空分值</p>
<div v-for="(score, k) in ques.fillScores" :key="k" class="input-wrap">
<el-input class="l-input" placeholder="请输入分值" v-model="score.val"
@change="fillScoreChange(ques)" />&emsp;填空项{{ k + 1 }}
@change="fillScoreChange(ques)" />
&emsp;填空项{{ k + 1 }}
<span
v-if="ques.answerData && ques.answerData.length && ques.answerData[k]">&emsp;建议分值占比{{
ques.answerData[k].scoreProportion }}%</span>
</div>
</div>
<div slot="reference" class="input-wrap">
查看每空分值<el-input class="l-input" placeholder="请输入分值" v-model="ques.score" readonly />
</div>
</el-popover>
<el-button v-if="!ques.isDel" type="text" @click="toQues(item, j, ques)">编辑试题</el-button>
<el-button v-if="!ques.isDel" type="text" @click="toQues(item, j, ques, 1)">编辑试题</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>
@ -178,20 +182,20 @@
</div>
</div>
</div>
<div class="stem" :id="'stem' + ques.questionVersionId" v-html="ques.stem"></div>
<div class="stem html-parse" :id="'stem' + ques.questionVersionId" v-html="ques.stem"></div>
<!-- 单选多选判断的选项 -->
<template
v-if="item.questionType !== 'fill_blank' && item.questionType !== 'essay' && ques.questionAnswerVersionsList">
<div v-for="(opt, j) in ques.questionAnswerVersionsList" :key="j" class="opt">
<span>{{ numToLetter(j) }}.&nbsp;</span>
<div class="text" v-html="opt.optionText"></div>
<div class="text html-parse" v-html="opt.optionText"></div>
</div>
</template>
<div class="bottom-line">
{{ item.questionType === 'essay' ? '参考答案' : '正确答案' }}
<div v-html="getCorrectAnswer(ques)" class="ans"></div>
<div v-html="getCorrectAnswer(ques)" class="ans html-parse"></div>
</div>
<div v-if="ques.knowledgePointList && ques.knowledgePointList.length" class="bottom-line">
@ -200,6 +204,10 @@
effect="plain">{{
kp.name }}</el-tag>
</div>
<div v-if="ques.questionBankName" class="bottom-line">
<span>来自</span>
<div>{{ ques.questionBankName }}</div>
</div>
</div>
</div>
</draggable>
@ -215,7 +223,9 @@
</div>
</div>
<div v-if="form.paperOutline && form.paperOutline.length" class="layer">
<draggable v-if="form.paperOutline && form.paperOutline.length">
<!-- <transition-group :key="1"> -->
<div class="layer" id="layer" ref="layer">
<table class="table">
<thead>
<tr>
@ -228,7 +238,7 @@
<tr v-for="(item, i) in form.paperOutline" :key="i">
<td>{{ arabicToChinese(i + 1) }}大题</td>
<td>{{ item.examQuestions.length }}/{{ item.questionNum }}</td>
<td>{{ item.examQuestions.reduce((e, j) => (e += +j.score), 0) }}/{{ item.targetScore }}</td>
<td>{{ item.examQuestions.reduce((e, j) => (e += +j.score), 0) || 0 }}/{{ item.targetScore }}</td>
</tr>
<tr>
<td>总计</td>
@ -237,8 +247,9 @@
</tr>
</tbody>
</table>
</div>
<!-- </transition-group> -->
</draggable>
<Template :visible.sync="templateVisible" />
<Manual :visible.sync="manualVisible" :questionType.sync="curType.questionType" />
@ -247,13 +258,14 @@
<el-drawer title="编辑试题" :visible.sync="addQuesVisible" size="1200px" :close-on-click-modal="false"
custom-class="editques-dia">
<QuesDetail :paperType.sync="testPaperType" :curQues.sync="curRow" @closeAdd="closeAdd"
<QuesDetail :key="quesKey" :paperType.sync="testPaperType" :curQues.sync="curRow" @closeAdd="closeAdd"
@updateQues="updateQues" />
</el-drawer>
</div>
</template>
<script>
import Draggable from 'vuedraggable'
import Sortable from 'sortablejs'
import UeditorPlus from '@/components/ueditorPlus'
import Breadcrumb from '@/components/breadcrumb'
import Template from './template'
@ -394,6 +406,7 @@ export default {
difficultSelected: false,
addQuesVisible: false,
quesKey: 1,
testPaperType: 2,
repeatVisible: false,
@ -403,14 +416,6 @@ export default {
};
},
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)
},
//
overview () {
const paper = this.form.paperOutline
@ -449,6 +454,11 @@ export default {
name: '创建试卷'
},
]
// console.log(44, Sortable, document.querySelector('#layer'))
// new Sortable(document.querySelector('#layer'), {
// group: "name",
// sort: true,
// })
this.getDetail()
this.getType()
this.getProfessional()
@ -464,6 +474,8 @@ export default {
id: paperId
})
const r = res.examPaper
delete r.createTime
delete r.updateTime
const paper = r.paperOutline
paper.map(e => {
e.shrink = false
@ -553,6 +565,7 @@ export default {
return {
questionNum: e.examQuestions.length,
targetScore: e.targetScore,
questionType: e.questionType,
}
})
})
@ -573,7 +586,8 @@ export default {
const { data } = await this.$post(this.api.avgValues, {
avgValueList: [{
questionNum: item.examQuestions.length,
targetScore: +item.targetScore
targetScore: +item.targetScore,
questionType: item.questionType,
}]
})
if (data && data.avgValueList && data.avgValueList.length) {
@ -587,10 +601,11 @@ export default {
//
async fillBlankAllocation (list) {
const param = list.map(e => {
let { answerData } = e.questionAnswerVersionsList[0]
let { answerData } = e
if (answerData) {
answerData = JSON.parse(answerData)
return {
questionType: e.questionType,
answerData: e.questionAnswerVersionsList[0].answerData,
questionNum: answerData.length,
targetScore: e.score,
questionId: e.questionVersionId
@ -606,7 +621,6 @@ export default {
// scores-1
if (!e.scores.includes(-1)) {
const { fillScores } = list[i]
debugger
if (fillScores) {
for (const j in fillScores) {
fillScores[j].val = e.scores[j]
@ -647,9 +661,8 @@ export default {
getCorrectAnswer (e) {
if (e.questionType === 'fill_blank') { //
// answerData
let data = e.questionAnswerVersionsList[0].answerData
let data = e.answerData
if (data) {
data = JSON.parse(data)
let result = ''
data.map((e, i) => {
result += `填空项${i + 1}${e.fills.map(n => n.val).join('|')}`
@ -687,7 +700,7 @@ export default {
let { answerData } = opts[0]
if (answerData) {
answerData = JSON.parse(answerData)
ques.answerData = answerData
//
let { jsonText } = ques
let scores
@ -716,7 +729,9 @@ export default {
},
//
toQues (item, i, ques) {
toQues (item, i, ques, type) {
this.testPaperType = type
this.quesKey++
this.curType = item
this.curQuesIndex = i
this.curRow = ques
@ -730,7 +745,6 @@ export default {
updateQues (ques, id) {
ques.questionAnswerVersionsList = ques.questionAnswerVersions
delete ques.questionAnswerVersions
ques.questionVersionId = id
this.curType.examQuestions[this.curQuesIndex] = Object.assign(this.curRow, ques)
this.calcDifficult()
},
@ -783,14 +797,16 @@ export default {
})
},
//
async delQues (item, i) {
async delQues (item, i, noConfirm) {
try {
if (!noConfirm) {
await this.$confirm(`确认要移除吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false,
})
}
item.examQuestions.splice(i, 1)
item.checkAll = false
this.calcDifficult()
@ -993,7 +1009,7 @@ export default {
if (typeof form.classificationId === 'object') form.classificationId = form.classificationId[form.classificationId.length - 1]
const { questionTypes } = QuesConst
form.questionType = [...new Set(paper.map(e => questionTypes.find(n => n.id === e.questionType).name))].join('') //
form.questionType = [...new Set(paper.map(e => questionTypes.find(n => n.id === e.questionType).name))].join(',') //
form.createSource = 1
if (isCopy) form.paperId = ''
this.tempForm = form
@ -1157,6 +1173,20 @@ export default {
border-radius: 2px;
}
/deep/.html-parse {
table {
display: table;
margin-bottom: 10px;
border-collapse: collapse;
th,
td {
padding: 5px 10px;
border: 1px solid #DDD;
}
}
}
.stem {
margin-bottom: 10px;
}
@ -1192,7 +1222,7 @@ export default {
.bottom-line {
display: flex;
align-items: baseline;
margin-bottom: 10px;
margin-top: 10px;
font-size: 13px;
color: #333;
}

@ -1,6 +1,7 @@
<template>
<el-dialog title="提示" :visible.sync="listVisible" width="800px" :close-on-click-modal="false" :show-close="false"
@closed="closeDia">
<el-drawer title="提示" :visible.sync="listVisible" size="800px" :close-on-press-escape="false" :wrapperClosable="false"
:show-close="false" custom-class="ques-dia" @closed="closeDia">
<div class="wrap p-20">
<el-alert title="以下试题在试卷中疑似出现重复,是否要继续发布!" type="warning" effect="dark" :closable="false" />
<el-table class="m-t-10" :data="data" stripe header-align="center" row-key="id">
<el-table-column prop="outlineName" label="大题名称" align="center" width="90"></el-table-column>
@ -10,20 +11,20 @@
<div v-html="scope.row.stem"></div>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="240">
<el-table-column label="操作" align="center" width="140">
<template slot-scope="scope">
<el-button type="text" @click="toQues(scope.row)">详情</el-button>
<el-button type="text" @click="delQues(scope.row, scope.$index)">移除</el-button>
<el-button type="text" @click="switchQues(scope.row, scope.$index)">替换</el-button>
<!-- <el-button type="text" @click="switchQues(scope.row, scope.$index)">替换</el-button> -->
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
</div>
<div class="btns">
<el-button type="primary" @click="check">检查一下</el-button>
<el-button @click="submit">继续发布</el-button>
</span>
</el-dialog>
<el-button :loading="submiting" @click="submit">继续发布</el-button>
</div>
</el-drawer>
</template>
<script>
import _ from 'lodash'
@ -52,7 +53,7 @@ export default {
//
toQues (row) {
const paper = this.$parent.form.paperOutline
this.$parent.toQues(paper[row.paperIndex], row.quesIndex, paper[row.paperIndex].examQuestions[row.quesIndex])
this.$parent.toQues(paper[row.paperIndex], row.quesIndex, paper[row.paperIndex].examQuestions[row.quesIndex], 3)
},
//
async delQues (row, i) {
@ -63,7 +64,7 @@ export default {
type: 'warning',
closeOnClickModal: false,
})
this.$parent.delQues(this.$parent.form.paperOutline[row.paperIndex], row.quesIndex)
this.$parent.delQues(this.$parent.form.paperOutline[row.paperIndex], row.quesIndex, 1)
this.data.splice(i, 1)
} catch (e) { }
},
@ -100,4 +101,15 @@ export default {
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
/deep/.ques-dia {
.el-drawer__header {
margin-bottom: 0;
}
.wrap {
max-height: calc(100vh - 110px);
overflow: auto;
}
}
</style>

@ -65,7 +65,8 @@
<li>
<label>题目类型</label>
<el-select v-model="questionType" clearable multiple placeholder="请选择题目类型" @change="initData">
<el-option v-for="(item, i) in questionTypes" :key="i" :label="item.name" :value="item.id"></el-option>
<el-option v-for="(item, i) in questionTypes" :key="i" :label="item.name"
:value="item.name"></el-option>
</el-select>
</li>
<li>
@ -121,7 +122,6 @@
show-overflow-tooltip></el-table-column>
<el-table-column prop="questionCount" label="试题总数" align="center" width="70"></el-table-column>
<el-table-column prop="score" label="总分" align="center" width="60"></el-table-column>
<el-table-column prop="medianScore" label="中分位数" align="center" width="70"></el-table-column>
<el-table-column prop="questionType" label="题型" align="center" min-width="90"
show-overflow-tooltip></el-table-column>
<el-table-column prop="difficult" label="试卷难度" align="center" width="100" sortable="custom">

@ -57,7 +57,9 @@
@click="item.shrink = !item.shrink">
</div>
<div :class="['ques', { hide: item.shrink }]">
<div v-for="(ques, j) in item.examQuestions" :key="j" class="item" :id="'ques' + ques.id">
<div v-for="(ques, j) in item.examQuestions" :key="j"
:class="['item', { disabled: !ques.status, del: ques.isDel, repeat: ques.repeat }]"
:id="'ques' + ques.id">
<div class="stem-wrap">
<span class="label">{{ j + 1 }} / {{ item.questionNum }}</span>
<span class="label">{{ item.questionTypeName }}</span>
@ -551,12 +553,53 @@ export default {
}
}
.item:not(:last-child) {
.item {
position: relative;
overflow: hidden;
&:not(:last-child) {
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 1px dashed #e1e1e1;
}
&.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);
opacity: .6;
}
}
&.del {
&:after {
content: '已 删 除';
}
}
&.repeat {
border-color: #ff962a;
&:after {
content: '存在重复题';
background-color: #ff962a;
}
}
}
.stem-wrap {
display: flex;
align-items: baseline;

Loading…
Cancel
Save