考试平台前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

616 lines
22 KiB

8 months ago
<template>
<div>
<Breadcrumb :data="crumbs" />
<div class="page">
<div class="side">
<div class="m-b-20">
<el-radio-group v-model="isNotJoin" @change="typeChange">
<div class="m-b-20">
<el-radio :label="0">所有试题</el-radio>
</div>
<div>
<el-radio :label="1">未分配知识点的试题</el-radio>
</div>
</el-radio-group>
</div>
<el-divider></el-divider>
<div>
<div class="flex-between m-b-10">
<h6 class="page-name" style="margin-bottom: 0">知识点框架</h6>
<el-button type="text" @click="toSet">设置</el-button>
</div>
<el-input class="m-b-10" placeholder="请输入知识点分类、知识点名称" prefix-icon="el-icon-search" size="small"
v-model="keyword" clearable></el-input>
<div style="height: 504px; max-height: 504px; overflow: auto">
<el-tree :data="types" default-expand-all ref="typeTree" node-key="id" highlight-current
:expand-on-click-node="false" @node-click="handleNodeClick" :props="{ label: 'name', isLeaf: 'leaf' }">
<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>
</div>
<div class="right">
<h6 class="page-name">筛选</h6>
<div class="tool">
<ul class="filter">
<li>
<label>题目类型</label>
<el-select v-model="filter.questionTypes" clearable placeholder="请选择题目类型" multiple @change="initData">
<el-option v-for="(item, i) in questionTypes" :key="i" :label="item.name" :value="item.id"></el-option>
</el-select>
</li>
<li>
<label>专业</label>
<el-select v-model="filter.specialtyIds" clearable placeholder="请选择专业" multiple @change="initData">
8 months ago
<el-option v-for="(item, i) in professionals" :key="i" :label="item.professionalName"
:value="item.professionalId"></el-option>
8 months ago
</el-select>
</li>
<li>
<label>年份</label>
<el-date-picker v-model="givenYears" type="year" placeholder="请选择年份" format="yyyy" value-format="yyyy"
multiple @change="initData">
</el-date-picker>
</li>
<li>
<label>试题难度</label>
<el-select v-model="filter.difficultys" clearable placeholder="请选择试题难度" multiple @change="initData">
<el-option v-for="(item, i) in difficults" :key="i" :label="item.name" :value="item.id"></el-option>
</el-select>
</li>
<li>
<label>知识点</label>
<el-cascader :disabled="isNotJoin !== 0" placeholder="请选择知识点" v-model="knowledgePointIds"
:options="knowledges" :props="{ value: 'id', label: 'name', multiple: true }" clearable collapse-tags
@change="initData"></el-cascader>
</li>
<li>
<label>正确率</label>
<el-input style="width: 90px;" placeholder="请输入" v-model="filter.correctRateStart" clearable />
<span style="margin: 0 10px;">% -- </span>
<el-input style="width: 90px;" placeholder="请输入" v-model="filter.correctRateEnd" clearable />
<span style="margin-left: 10px;">%</span>
</li>
<li>
<label>状态</label>
<el-select v-model="filter.status" clearable placeholder="请选择状态" @change="initData">
<el-option v-for="(item, i) in status" :key="i" :label="item.name" :value="item.id"></el-option>
</el-select>
</li>
<li>
<label>搜索</label>
<el-input style="width: 250px;" placeholder="请输入题干" prefix-icon="el-icon-search" v-model="filter.keyword"
clearable />
</li>
<div style="margin-bottom: 15px;">
<el-button type="primary" @click="add">新增试题</el-button>
<el-button type="primary" @click="batchImport">批量导入</el-button>
<!-- <el-button type="primary" @click="add">批量转移</el-button> -->
<el-button type="primary" @click="add">批量移除 </el-button>
<el-button type="primary" @click="delAllSelection">批量删除</el-button>
8 months ago
</div>
</ul>
</div>
<el-table :data="list" class="table" ref="table" stripe header-align="center"
@selection-change="handleSelectionChange" row-key="id" @sort-change="sortChange">
8 months ago
<el-table-column type="selection" width="45" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="50" label="序号" align="center"></el-table-column>
8 months ago
<el-table-column prop="stem" label="题干" align="center" min-width="120"
show-overflow-tooltip></el-table-column>
<el-table-column prop="questionType" label="题型" align="center" width="70" sortable="custom">
8 months ago
<template slot-scope="scope">{{ questionTypes.find(e => e.id === scope.row.questionType) ?
questionTypes.find(e => e.id === scope.row.questionType).name : '' }}</template>
</el-table-column>
<el-table-column prop="professionalName" label="专业" align="center" min-width="100"></el-table-column>
<el-table-column prop="knowledgePointName" label="知识点" align="center" min-width="100"></el-table-column>
<el-table-column prop="givenYear" label="年份" align="center" width="70" sortable="custom"></el-table-column>
<el-table-column prop="difficulty" label="难度" align="center" width="70" sortable="custom">
8 months ago
<template slot-scope="scope">{{ difficults.find(e => e.id === scope.row.difficulty) ? difficults.find(e =>
e.id === scope.row.difficulty).name : '' }}</template>
</el-table-column>
<el-table-column prop="correctRate" label="正确率" align="center" width="80" sortable="custom"></el-table-column>
<el-table-column prop="updateTime" label="更新时间" align="center" width="140"
sortable="custom"></el-table-column>
8 months ago
<el-table-column prop="lastEditor" label="最近编辑人" align="center" width="90"></el-table-column>
<el-table-column prop="referenceCount" label="已引用试卷(套)" align="center" width="130"
sortable="custom"></el-table-column>
8 months ago
<el-table-column label="操作" align="center" width="300">
<template slot-scope="scope">
<el-button type="text" @click="toDetail(scope.row, 1)">复制</el-button>
<el-button type="text" @click="toDetail(scope.row, 2)">查看</el-button>
<el-button type="text" @click="toDetail(scope.row, 3)">编辑</el-button>
8 months ago
<el-button type="text" @click="del(scope.row)">移除</el-button>
<el-button type="text" @click="del([scope.row])">删除</el-button>
8 months ago
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" style="margin: 0 10px 0 5px"
:active-text="scope.row.status ? '开' : '关'"
@change="switchOff($event, scope.row, scope.$index)"></el-switch>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background @current-change="currentChange" :current-page="page"
layout="total, prev, pager, next" :total="total"></el-pagination>
</div>
</div>
</div>
<Detail :visible.sync="quesVisible" :row.sync="curRow" :detailType.sync="detailType" />
<el-dialog title="提示" :visible.sync="delVisible" width="400px" :close-on-click-modal="false" custom-class="del-dia">
<div class="del-wrap">
<div class="icon el-icon-warning"></div>
<div class="flex-1">
<p>该试题已被以下试卷引用确定删除</p>
<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>
</li>
</ul>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="delVisible = false">取消</el-button>
<el-button type="primary" @click="delSubmit">确定</el-button>
</span>
</el-dialog>
8 months ago
<el-dialog title="批量导入试题" :visible.sync="importVisible" width="520px" :close-on-click-modal="false"
:modal-append-to-body="false">
<el-form label-width="110px">
<el-form-item prop="userName" label="题库分类">
<el-select style="width: 100%" v-model="form.provinceId" placeholder="请选择题库分类">
<el-option v-for="(item, i) in types" :key="i" :label="item.provinceName"
:value="item.provinceId"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="userName" label="当前题库">
<el-input placeholder="请输入题库名称" v-model="form.name" disabled></el-input>
</el-form-item>
<el-form-item prop="userName" label="模板文件">
<el-button type="primary" @click="download">模板下载<i class="el-icon-download el-icon--right"></i></el-button>
</el-form-item>
<el-form-item prop="userName" label="选择文件上传">
<el-upload name="file" accept=".xls,.xlsx" ref="upload" class="import-file" drag :before-upload="beforeUpload"
:on-remove="handleRemove" :on-error="uploadError" :on-success="uploadSuccess" :before-remove="beforeRemove"
:limit="1" :data="{
// competitionId: id,
platformId: 2
}" :disabled="uploading" :on-exceed="handleExceed" :action="this.api.batchImportPersonalData"
:file-list="uploadList" :headers="headers">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip" style="line-height: 1.8;">
<p>上传说明</p>
<p>1.请按照模板要求正确填写后上传</p>
<p>2.上传文件大小限制50M</p>
<p>3.仅支持上传.xls .xlsx文件格式</p>
</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="importVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Util from '@/libs/util'
import Setting from '@/setting'
import Ueditor from '@/components/ueditor'
import Breadcrumb from '@/components/breadcrumb'
import Detail from '../detail'
import Const from '@/const/ques'
export default {
components: { Ueditor, Breadcrumb, Detail },
data () {
return {
crumbs: [
{
name: this.$route.query.questionBankName || '题库管理',
route: '/quesBank'
},
{
name: '试题管理'
},
],
difficults: Const.difficults,
questionTypes: Const.questionTypes,
typeId: this.$route.query.id,
loading: false,
isNotJoin: 0,
createSource: 1,
keyword: '',
type: 1,
types: [],
8 months ago
professionals: [],
8 months ago
status: [
{
id: 1,
name: '启用'
},
{
id: 0,
name: '禁用'
},
],
knowledges: [],
givenYears: '',
knowledgePointIds: [],
filter: {
questionTypes: [],
correctRateEnd: '',
correctRateStart: '',
difficultys: [],
specialtyIds: [],
status: '',
keyword: '',
questionTypeSort: '',
givenYearSort: '',
difficultySort: '',
correctRateSort: '',
updateTimeSort: '',
referenceCountSort: '',
8 months ago
},
list: [],
page: 1,
pageSize: 10,
total: 0,
multipleSelection: [],
quesVisible: false,
curRow: {},
detailType: '',
delVisible: false,
curUsePapers: [],
curRow: {},
8 months ago
form: {},
importVisible: false,
uploadList: [],
uploadFaild: false,
uploadTips: '',
exportCode: '',
headers: {
token: Util.local.get(Setting.tokenKey)
},
uploading: false,
};
},
watch: {
keyword: function (val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(this.getType, 500);
},
'filter.keyword': function (val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(this.initData, 500);
},
quesVisible () {
this.quesVisible || this.getList()
}
},
mounted () {
this.getType()
8 months ago
this.getProfessional()
8 months ago
this.getKnowledge()
},
methods: {
// 获取知识点
async getType () {
try {
this.loading = true
const { data } = await this.$post(this.api.TreeStructure, {
createSource: 1,
questionBankId: this.typeId,
keyword: this.keyword,
})
this.types = data
this.getList()
} finally {
this.loading = false
}
},
// 前往知识点
toSet () {
this.$router.push(`/knowledge?id=${this.typeId}`)
},
// 类型回调
typeChange () {
this.$refs.typeTree.setCurrentKey(null)
this.initData()
},
// 点击树节点查询列表数据
handleNodeClick () {
this.isNotJoin = ''
this.initData()
this.$refs.table.clearSelection()
},
// 处理树形
handleType (list) {
list.map(e => {
if (e.children && e.children.length) {
this.handleType(e.children)
} else {
delete e.children
}
})
},
// 获取知识点
async getKnowledge () {
try {
const { data } = await this.$post(this.api.TreeStructure, {
createSource: 1,
questionBankId: this.typeId,
keyword: ''
})
this.handleType(data)
this.knowledges = data
} catch (e) { }
},
8 months ago
// 获取所属专业
async getProfessional () {
try {
const res = await this.$post(this.api.queryProfessional, {
pageNum: 1,
pageSize: 1000,
})
this.professionals = res.pageList.records
} catch (e) { }
},
// 试题列表
8 months ago
async getList () {
let type = this.$refs.typeTree.getCurrentKey()
const k = this.knowledgePointIds
if (k.length) { // 右边筛选里的知识点
type = k.map(e => {
return e[e.length - 1]
})
} else if (type) {
type = [type]
}
const { message } = await this.$post(this.api.listQuestion, {
...this.filter,
givenYears: this.givenYears ? [this.givenYears] : [],
isNotJoin: this.isNotJoin || '',
pageNum: this.page,
pageSize: this.pageSize,
questionBankId: this.typeId,
knowledgePointIds: type || [],
})
this.list = Util.removeTag(message.records)
this.total = message.total
},
// 切换页码
currentChange (val) {
this.page = val
this.getList()
},
handleSelectionChange (val) { // 多选
this.multipleSelection = val
},
initData () {
this.$refs.table.clearSelection()
this.page = 1
this.getList()
},
// 排序回调
sortChange (column) {
if (column.prop === 'questionType') this.filter.questionTypeSort = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
if (column.prop === 'givenYear') this.filter.givenYearSort = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
if (column.prop === 'difficulty') this.filter.difficultySort = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
if (column.prop === 'correctRate') this.filter.correctRateSort = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
if (column.prop === 'updateTime') this.filter.updateTimeSort = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
if (column.prop === 'referenceCount') this.filter.referenceCountSort = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
this.getList()
},
8 months ago
// 添加
add () {
this.quesVisible = true
this.curRow = {}
this.detailType = ''
8 months ago
},
// 编辑/查看 type: 1复制,2查看,3编辑
toDetail (row, type) {
8 months ago
this.quesVisible = true
this.curRow = row
this.detailType = type
8 months ago
},
// 删除
async del (rows) {
const ids = rows.map(e => e.questionId)
// 先查询是否有试卷引用了该试题
const { list } = await this.$post(this.api.checkQuestionIsUse, ids)
if (list.length) {
this.curRow = rows
this.curUsePapers = list
this.delVisible = true
} else {
try {
await this.$confirm(`确认要删除吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false,
})
await this.$post(this.api.delQuestion, ids)
Util.successMsg('删除成功')
8 months ago
this.getList()
} catch (e) { }
}
8 months ago
},
// 批量删除
async delAllSelection () {
const list = this.multipleSelection
list.length ? this.del(list) : Util.warningMsg('请选择数据')
},
// 删除提交
async delSubmit () {
await this.$post(this.api.delQuestion, this.curRow.map(e => e.questionId))
Util.successMsg('删除成功')
this.multipleSelection = []
this.$refs.table.clearSelection()
this.delVisible = false
this.getList()
},
// 禁启用
8 months ago
async switchOff (val, row) {
try {
row.status = val ? 0 : 1 // 调接口前先还原成点击前的状态,因为有几种原因会禁用启用失败
if (!val) {
await this.$confirm(`<p>确认要禁用【${row.stem}】吗?</p><p style="color: #f56c6c;">禁用后已引用该试题的试卷将会变成草稿,再次启用后,试卷将自动发布</p>`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false,
dangerouslyUseHTMLString: true,
})
}
await this.$post(`${this.api.disableOrEnableQuestion}?questionId=${row.questionId}&status=${val}`)
row.status = val
Util.successMsg(val ? '启用成功' : '禁用成功')
this.getList()
} catch (e) {
row.status = val ? 0 : 1
}
8 months ago
},
// 批量导入
batchImport () {
this.importVisible = true
this.uploadList = []
this.uploadFaild = false
},
// 模板下载
download () {
location.href = this.api[this.info.completeCompetitionSetup.competitionType ? 'competionTeamTemplate' : 'competionPersonTemplate']
},
// 上传文件
handleExceed (files, fileList) {
Util.warningMsg(
`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`
)
},
// 下载失败文件
showFaild () {
axios.get(`${this.api.TeamDataExportFailure}?exportCode=${this.exportCode}&platformId=2&type=${this.info.completeCompetitionSetup.competitionType ? 1 : 2}`, {
headers: this.headers,
responseType: 'blob'
}).then((res) => {
const name = res.headers['content-disposition']
Util.downloadFileDirect(name ? decodeURI(name) : '批量导入报名人员失败数据导出.xlsx', new Blob([res.data]))
}).catch(res => { })
},
uploadSuccess ({ data, status }) {
this.uploading = false
this.uploadFaild = false
this.uploadTips = ''
if (status === 200) {
this.init()
const { tip } = data
if (data.exportCode) {
this.exportCode = data.exportCode
this.uploadFaild = true
this.uploadTips = tip
} else {
Util[tip.includes('5000') ? 'errorMsg' : 'successMsg'](tip, 3000)
this.importVisible = false
}
} else {
Util.errorMsg(res.message || '上传失败,请检查数据', 3000)
}
},
uploadError (err, file, fileList) {
this.uploading = false
this.$message({
message: "上传出错,请重试!",
type: "error",
center: true
})
},
beforeUpload (file) {
this.uploading = true
},
beforeRemove (file, fileList) {
return this.$confirm(`确定移除 ${file.name}`)
},
handleRemove (file, fileList) {
this.uploadList = fileList
this.uploadFaild = false
},
cancelUpload () {
this.uploading = false
this.$refs.upload.abort()
this.keyword = ''
this.init()
this.importVisible = false
},
// 后台上传
backstageUpload () {
this.isBackstage = 1
this.importVisible = false
},
}
};
</script>
<style lang="scss" scoped>
.page {
display: flex;
padding: 0 24px;
.side {
width: 300px;
padding: 24px 10px 24px 0;
margin-right: 24px;
border-right: 1px solid rgba(0, 0, 0, 0.06);
}
.right {
width: calc(100% - 324px);
padding: 24px 0;
}
}
.tool {
margin-bottom: 0;
.filter {
flex-wrap: wrap;
justify-content: space-between;
li {
margin-bottom: 15px;
}
}
}
.papers {
margin-top: 10px;
li {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
i {
font-size: 14px;
color: #7a7a7a;
cursor: pointer;
}
}
8 months ago
</style>