parent
10b6532744
commit
9ded1da513
9 changed files with 575 additions and 72 deletions
@ -0,0 +1,443 @@ |
||||
<template> |
||||
<div> |
||||
<el-dialog title="自动选题" :visible.sync="quesVisible" width="1200px" :close-on-click-modal="false" @closed="closeDia"> |
||||
<h6 class="page-name">难度设置</h6> |
||||
<div class="tool"> |
||||
<ul class="filter"> |
||||
<li> |
||||
<label>试卷难度</label> |
||||
<el-select v-model="difficult" placeholder="请选择试卷难度" @change="difficultData"> |
||||
<el-option v-for="(item, i) in difficults" :key="i" :label="item.name" :value="item.id"></el-option> |
||||
</el-select> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
|
||||
<el-table :data="list" stripe header-align="center" row-key="libraryId"> |
||||
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column> |
||||
<el-table-column label="大题" align="center" min-width="100"> |
||||
<template slot-scope="scope">第{{ arabicToChinese(scope.$index + 1) }}大题</template> |
||||
</el-table-column> |
||||
<el-table-column prop="name" label="题型" align="center" min-width="90"> |
||||
<template slot-scope="scope"> |
||||
<p v-if="scope.row.questionType">{{ questionTypes.find(e => e.id === scope.row.questionType).name }}</p> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column prop="questionNum" label="目标题数" align="center" min-width="80"></el-table-column> |
||||
<el-table-column label="基础(道题)" align="center" width="90"> |
||||
<template slot-scope="scope"> |
||||
<el-input v-model.number="scope.row.basicDifficulty" type="number" /> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="普通(道题)" align="center" width="90"> |
||||
<template slot-scope="scope"> |
||||
<el-input v-model.number="scope.row.normalDifficulty" type="number" /> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="较难(道题)" align="center" width="90"> |
||||
<template slot-scope="scope"> |
||||
<el-input v-model.number="scope.row.hardDifficulty" type="number" /> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="难(道题)" align="center" width="90"> |
||||
<template slot-scope="scope"> |
||||
<el-input v-model.number="scope.row.veryHardDifficulty" type="number" /> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="难度随机(道题)" align="center" width="100"> |
||||
<template slot-scope="scope"> |
||||
<el-input v-model.number="scope.row.randomDifficulty" type="number" /> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
|
||||
<h6 class="page-name m-t-20">知识点设置</h6> |
||||
<div class="wrap"> |
||||
<!-- 题库 --> |
||||
<div class="item"> |
||||
<p class="total m-b-10">题库</p> |
||||
<el-input class="m-b-10" placeholder="请输入题库分类/题库名称" prefix-icon="el-icon-search" v-model="quesBankKeyword" |
||||
clearable /> |
||||
<el-tree :key="key" node-key="id" default-expand-all highlight-current ref="quesBank" :data="quesBanks" |
||||
:props="{ label: 'name' }" @node-click="getKnowledge" @check-change="quesBankCheck"></el-tree> |
||||
</div> |
||||
<!-- 知识点 --> |
||||
<div class="item"> |
||||
<p class="total m-b-10">知识点框架</p> |
||||
<el-input class="m-b-10" placeholder="请输入知识点分类/知识点名称" prefix-icon="el-icon-search" v-model="knowledgeKeyword" |
||||
clearable /> |
||||
<el-tree :data="knowledges" default-expand-all ref="knowledge" node-key="id" highlight-current |
||||
:expand-on-click-node="false" show-checkbox @check-change="knowledgeCheck" :props="{ label: 'name' }"> |
||||
<span class="custom-tree-node" slot-scope="{ node, data }"> |
||||
<img v-if="data.type" class="m-r-5" src="@/assets/images/knowledge.svg" alt=""> |
||||
<span class="org-name">{{ data.name }}</span> |
||||
</span> |
||||
</el-tree> |
||||
</div> |
||||
<!-- 已选知识点 --> |
||||
<div class="item"> |
||||
<div class="flex j-between a-center m-b-20"> |
||||
<p class="total m-b-10">已选知识点({{ allKnowledges.length }}个)</p> |
||||
<p class="clear" @click="clearChecked"> |
||||
<i class="el-icon-delete m-r-5"></i> |
||||
清空 |
||||
</p> |
||||
</div> |
||||
|
||||
<div v-for="(item, i) in checked" :key="i" class="line j-between"> |
||||
<div class="flex a-center"> |
||||
题库:{{ item.quesBank.name }} |
||||
<i class="el-icon-delete action-icon m-l-10" @click="delQuesBank(i)"></i> |
||||
</div> |
||||
<div class="knowledges"> |
||||
<el-tag v-for="(tag, j) in item.knowledges" :key="tag.name" class="m-r-10" closable |
||||
@close="delKnowledge(i, j, item)"> |
||||
{{ tag.name }} |
||||
</el-tag> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<h6 class="page-name m-t-20">年份设置</h6> |
||||
<div class="years flex"> |
||||
<el-checkbox style="margin-right: 20px" v-model="yearAll" @change="yearAllChange">全选</el-checkbox> |
||||
<el-checkbox-group v-model="yearCheck" @change="yearChange"> |
||||
<el-checkbox v-for="(item, i) in years" :key="i" :label="item"></el-checkbox> |
||||
</el-checkbox-group> |
||||
</div> |
||||
|
||||
|
||||
<span slot="footer" class="dialog-footer"> |
||||
<el-button @click="quesVisible = false">取消</el-button> |
||||
<el-button type="primary" :loading="submiting" @click="submit">自动选题</el-button> |
||||
</span> |
||||
</el-dialog> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import Setting from '@/setting' |
||||
import Util from '@/libs/util' |
||||
import QuesConst from '@/const/ques' |
||||
import TestPaperConst from '@/const/testPaper' |
||||
import _ from 'lodash' |
||||
// import Decimal from 'decimal.js' |
||||
export default { |
||||
props: ['visible'], |
||||
data () { |
||||
return { |
||||
arabicToChinese: Util.arabicToChinese, |
||||
difficults: TestPaperConst.difficults, |
||||
questionTypes: QuesConst.questionTypes, |
||||
quesVisible: false, |
||||
difficult: '', |
||||
|
||||
quesBankKeyword: '', |
||||
knowledgeKeyword: '', |
||||
searchTimer: null, |
||||
key: 1, |
||||
quesBanks: [], |
||||
knowledges: [], |
||||
checked: [], |
||||
|
||||
list: [], |
||||
years: ['暂无年份', '2024', '2023', '2022', '2021', '2020', '2019', '2018', '2017', '2016', '更早'], |
||||
yearAll: true, |
||||
yearCheck: [], |
||||
submiting: false, |
||||
}; |
||||
}, |
||||
computed: { |
||||
allKnowledges () { |
||||
const result = [] |
||||
this.checked.map(e => { |
||||
result.push(...e.knowledges) |
||||
}) |
||||
return result |
||||
}, |
||||
}, |
||||
watch: { |
||||
'quesBankKeyword': function (val) { |
||||
clearTimeout(this.searchTimer) |
||||
this.searchTimer = setTimeout(this.getQuesBank, 500) |
||||
}, |
||||
'knowledgeKeyword': function (val) { |
||||
clearTimeout(this.searchTimer) |
||||
this.searchTimer = setTimeout(this.getKnowledge, 500) |
||||
}, |
||||
visible () { |
||||
this.quesVisible = this.visible |
||||
this.visible && this.init() |
||||
} |
||||
}, |
||||
mounted () { |
||||
|
||||
}, |
||||
methods: { |
||||
// 初始化 |
||||
init () { |
||||
this.yearCheck = this.years |
||||
this.handleQuesList() |
||||
this.getQuesBank() |
||||
}, |
||||
// 获取题库 |
||||
async getQuesBank () { |
||||
try { |
||||
const { list } = await this.$post(this.api.questionBankStructureLevel, { |
||||
keyword: this.quesBankKeyword, |
||||
createSource: 1, |
||||
}) |
||||
this.quesBanks = list |
||||
} catch (e) { } |
||||
}, |
||||
// 获取知识点 |
||||
async getKnowledge () { |
||||
try { |
||||
const id = this.$refs.quesBank.getCurrentKey() |
||||
if (id) { |
||||
const { data } = await this.$post(this.api.TreeStructure, { |
||||
createSource: 1, |
||||
questionBankId: id, |
||||
keyword: this.knowledgeKeyword, |
||||
}) |
||||
this.knowledges = data |
||||
} |
||||
} catch (e) { } |
||||
}, |
||||
// 题库勾选回调 |
||||
quesBankCheck (data, checked) { |
||||
// debugger |
||||
if (checked) { |
||||
// this.$refs.quesBank.setCurrentKey(data.id) |
||||
this.getKnowledge() |
||||
} |
||||
this.$refs.knowledge.setCheckedNodes(checked ? this.knowledges : []) |
||||
}, |
||||
// 知识点勾选回调 |
||||
knowledgeCheck (data, checked) { |
||||
// 选中的是知识点才需要显示在最右边 |
||||
if (data.type) { |
||||
const checkQues = this.$refs.quesBank.getCurrentNode() |
||||
const ques = this.checked.find(e => e.quesBank.id === checkQues.id) |
||||
// 已选中的题库数组里有该题库,则往该题库的知识点数组里 |
||||
if (ques) { |
||||
const i = ques.knowledges.findIndex(e => e === data.id) |
||||
if (checked && i === -1) { |
||||
ques.knowledges.push(data) |
||||
} else if (!checked && i >= 0) { |
||||
ques.knowledges.splice(i, 1) |
||||
} |
||||
} else { |
||||
this.checked.push({ |
||||
quesBank: checkQues, |
||||
knowledges: [data] |
||||
}) |
||||
} |
||||
} |
||||
}, |
||||
// 处理大纲列表 |
||||
handleQuesList () { |
||||
const list = _.cloneDeep(this.$parent.form.paperOutline) |
||||
this.list = list |
||||
}, |
||||
// 清空已选 |
||||
async clearChecked () { |
||||
try { |
||||
await this.$confirm(`确认要清空吗?`, '提示', { |
||||
confirmButtonText: '确定', |
||||
cancelButtonText: '取消', |
||||
type: 'warning', |
||||
closeOnClickModal: false, |
||||
}) |
||||
this.checked = [] |
||||
} catch (e) { } |
||||
}, |
||||
// 删除题库 |
||||
delQuesBank (i) { |
||||
this.checked.splice(i, 1) |
||||
}, |
||||
// 删除知识点 |
||||
delKnowledge (i, j, item) { |
||||
item.knowledges.splice(j, 1) |
||||
item.knowledges.length || this.checked.splice(i, 1) // 知识点清空了则把该题库删除 |
||||
}, |
||||
// 获取模板列表 |
||||
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) { } |
||||
}, |
||||
|
||||
|
||||
calculateQuestionNumbers (list, target) { |
||||
// Decimal(e.score).add(Decimal(score)) |
||||
const difficultyWeights = [0.2, 0.4, 0.6, 0.8] |
||||
const names = ['basicDifficulty', 'normalDifficulty', 'hardDifficulty', 'veryHardDifficulty'] |
||||
// 遍历题型数组 |
||||
list.forEach(e => { |
||||
const total = e.questionNum |
||||
const nums = { |
||||
basicDifficulty: 0, |
||||
normalDifficulty: 0, |
||||
hardDifficulty: 0, |
||||
veryHardDifficulty: 0, |
||||
} |
||||
|
||||
// 初始化题目数量 |
||||
for (let i = 0; i < 4; i++) { |
||||
nums[names[i]] = 0 |
||||
} |
||||
|
||||
// 初始分配题目数量 |
||||
let totalWeight = total * target |
||||
const maxCount = Math.ceil(total / 4) |
||||
for (let i = 0; i < 4; i++) { |
||||
console.log("🚀 ~ calculateQuestionNumbers ~ totalWeight:", totalWeight) |
||||
if (totalWeight > 0) { |
||||
const random = Math.floor(Math.random() * (maxCount)) + 1 |
||||
const weight = difficultyWeights[i] |
||||
totalWeight -= weight * random |
||||
e[names[i]] = totalWeight > 0 ? random : 0 |
||||
} |
||||
} |
||||
|
||||
// e.nums = nums |
||||
}); |
||||
|
||||
return list |
||||
}, |
||||
// 试卷难度选择回调 |
||||
difficultData (val) { |
||||
// debugger |
||||
console.log(111, this.calculateQuestionNumbers(this.list, 0.3)) |
||||
}, |
||||
// 年份全选回调 |
||||
yearAllChange (val) { |
||||
this.yearCheck = val ? this.years : [] |
||||
}, |
||||
// 年份选择回调 |
||||
yearChange (val) { |
||||
this.yearAll = val.length === 11 |
||||
}, |
||||
// 提交 |
||||
async submit () { |
||||
if (this.submiting) return false |
||||
const { list } = this |
||||
// let invalid = 0 |
||||
// for (const e of list) { |
||||
// if (!e.questionType) { |
||||
// Util.warningMsg('请选择题型') |
||||
// invalid = 1 |
||||
// break |
||||
// } |
||||
// } |
||||
// if (invalid) return false |
||||
this.submiting = true |
||||
const k = this.allKnowledges.map(e => e.id) |
||||
|
||||
const years = [] |
||||
this.yearCheck.map(e => { |
||||
if (e === '暂无年份') { |
||||
years.push('') |
||||
} else if (e === '更早') { |
||||
// 选的是更早则直接传1990~2005所有年份 |
||||
for (let i = 1990; i < 2006; i++) { |
||||
years.push(i) |
||||
} |
||||
} else { |
||||
years.push(+e) |
||||
} |
||||
}) |
||||
list.map(e => { |
||||
e.randomDifficulty = 0 |
||||
e.givenYears = years |
||||
e.knowledgePointsIds = k |
||||
}) |
||||
try { |
||||
const res = await this.$post(this.api.selectQuestionsByTypeAndDifficulty, list) |
||||
|
||||
let invalid = 0 |
||||
let hasQues = 0 |
||||
list.map((e, i) => { |
||||
console.log("🚀 ~ submit ~ e:", e) |
||||
if (e.questionNum !== res.list[i].questions.length) invalid = 1 |
||||
if (e.examQuestions.length) hasQues = 1 |
||||
}) |
||||
|
||||
// 3种情况提示不一样 |
||||
const tips = invalid && hasQues ? |
||||
'此操作会清空当前试卷已添加的试题,并且满足自动选题设置的试题数量不足,确定要继续自动选题吗?' : |
||||
invalid && !hasQues ? |
||||
'满足自动选题设置的试题数量不足,确认要继续自动选题吗?' : |
||||
!invalid && hasQues ? '此操作会清空当前试卷已添加的试题,是否确定要继续自动选题吗?' : |
||||
'' |
||||
|
||||
if (tips) { |
||||
await this.$confirm(tips, '提示', { |
||||
confirmButtonText: '确定', |
||||
cancelButtonText: '取消', |
||||
type: 'warning', |
||||
closeOnClickModal: false, |
||||
}) |
||||
} |
||||
list.map((e, i) => { |
||||
this.$parent.form.paperOutline[i].examQuestions = res.list[i].questions |
||||
}) |
||||
this.quesVisible = false |
||||
this.submiting = false |
||||
} catch (e) { |
||||
this.submiting = false |
||||
} |
||||
}, |
||||
// 弹框关闭回调 |
||||
closeDia () { |
||||
this.$emit('update:visible', false) |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.wrap { |
||||
display: flex; |
||||
margin-top: 20px; |
||||
border: 1px solid #eee; |
||||
|
||||
.item { |
||||
width: 30%; |
||||
max-height: calc(100vh - 190px); |
||||
padding: 15px; |
||||
border-right: 1px solid #eee; |
||||
overflow: auto; |
||||
|
||||
&:last-child { |
||||
width: 40%; |
||||
border-right: 0; |
||||
} |
||||
} |
||||
|
||||
.total { |
||||
font-size: 16px; |
||||
color: #333; |
||||
} |
||||
|
||||
.clear { |
||||
display: inline-flex; |
||||
align-items: center; |
||||
font-size: 14px; |
||||
color: #8b8b8b; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.knowledges { |
||||
padding-left: 44px; |
||||
margin-top: 10px; |
||||
} |
||||
} |
||||
</style> |
Loading…
Reference in new issue