增加试卷的编辑

This commit is contained in:
daihh
2022-12-24 13:59:18 +08:00
parent 4f7438aa83
commit 1d64b54653
4 changed files with 561 additions and 22 deletions

View File

@@ -0,0 +1,333 @@
<template>
<!--编辑试卷-->
<div >
<el-dialog title="修改试卷" :visible.sync="editShow" width="800px" >
<!--试卷的标题可以编辑-->
<div class="paper-title">
<div>{{title}}</div>
</div>
<!--查询项-->
<!-- <div>
<el-form :inline="true" size="mini">
<el-form-item>
<el-select clearable v-model="qpaper.qtype" placeholder="请选择题型" class="search-width-120">
<el-option label="单选题" :value="1"></el-option>
<el-option label="多选题" :value="2"></el-option>
<el-option label="判断题" :value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item><el-input class="search-width-120" v-model="qpaper.keyword" clearable placeholder="题干"></el-input></el-form-item>
<el-form-item>
<el-button type="primary" @click="selectQuery()">搜索</el-button>
<el-button type="primary" @click="resetQuery()">重置</el-button>
</el-form-item>
<el-form-item></el-form-item>
</el-form>
</div> -->
<div style="display: flex;justify-content: space-between;line-height:30px;">
<div>
<el-checkbox v-model="optionShow">显示选项</el-checkbox>
</div>
<div>
双击题干或选项编辑
</div>
<div style="text-align: right;font-size: 14px;">
本卷共
<span class="bigred">{{paperLength}}</span>
, 总分
<span class="bigred">{{totalScore}}</span>
</div>
</div>
<!--试卷内容-->
<div class="paper-box">
<div v-if="qitems.length>0" v-for="(item,idx) in qitems" :key="idx">
<div v-if="item.type < 900">
<div style="display: flex;justify-content: space-between;background-color: #dcf1ff;padding: 5px;">
<div>
<!-- <span style="padding-right: 5px;" v-if="paperQEdit"><el-checkbox v-model="item.checked" @change="checkedChange"></el-checkbox></span> -->
<span v-if="item.type == 1">单选题</span>
<span v-if="item.type == 2">多选题</span>
<span v-if="item.type == 3">判断题</span>
<span style="padding-left: 5px;">难度[{{item.difficulty == 1? '简单' : item.difficulty == 2? '中等': '困难'}}]</span>
<!-- <span style="padding-left: 5px;">知识点[{{item.analysis}}]</span> -->
</div>
<div>
<el-input @input="changeInput($event)" v-model="item.defaultScore" style="width: 120px;" size="mini" placeholder="分数" @blur="paperCalculation()">
<template slot="append"></template>
</el-input>
<el-button v-if="paperQEdit" icon="el-icon-delete" @click="checkDelete(idx)" size="mini"></el-button>
</div>
</div>
<div style="padding: 15px;">
<div style="font-weight: 700px;padding-bottom: 10px;cursor: pointer;" @dblclick="showCellEdit(idx,-1)">
<div> {{ item.title }}</div>
<div v-if="item.images" class="qimg"><img class="qimg-fit" :src="imageBaseUrl + item.images" alt=""> </div>
</div>
<div v-if="optionShow">
<div v-if="item.type == 3">
<div class="opt" @click="setJudgeAnswer(item,'true')" :class="{optanswer:item.answer=='true'}"><span class="optanswer-span">{{item.answer=='true'? '√':''}} </span> 1. 正确 </div>
<div class="opt" @click="setJudgeAnswer(item,'false')" :class="{optanswer:item.answer=='false'}"><span class="optanswer-span">{{item.answer=='false'? '√':''}} </span> 2. 错误 </div>
</div>
<div v-else v-for="(opt, optIdx) in item.optionList" :key="optIdx" class="opt" @click="setOptAnswer(item,opt)" @dblclick="showCellEdit(idx,optIdx)">
<div :class="{optanswer:opt.isAnswer}"><span class="optanswer-span">{{opt.isAnswer? '√':''}} </span> {{ optIdx + 1 }}, {{ opt.content }}</div>
<div v-if="opt.images" class="qimg"><img class="qimg-fit" :src="imageBaseUrl + opt.images" alt=""> </div>
</div>
</div>
</div>
</div>
<div v-if="item.type > 900">
<div style="height: 25px;">
<span style="float: right;margin-top: -5px;cursor: pointer;" class="el-icon-delete" @click="checkDelete(idx)"></span>
<div style="border-bottom: 2px dotted #000000;margin-right: 50px;"></div>
</div>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button :loading="loading" type="primary" @click="save()"> </el-button>
<el-button @click="cancel()"> 取消 </el-button>
</span>
</el-dialog>
<el-dialog :title="cellData.title" :visible.sync="cellData.show" width="500px">
<div>
<div><el-input type="textarea" rows="2" v-model="cellData.text"></el-input> </div>
<div style="height: 160px;padding-top: 10px;">
<imgupload :value="cellData.img" @success="cellImageSuccss" @remove="cellImageRemove"></imgupload>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button :loading="loading" type="primary" @click="cellConfirm()"> 确定 </el-button>
<el-button @click="cellCancel()"> 取消 </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import apiPaper from '@/api/modules/paper.js';
import imgupload from '@/components/ImageUpload/single.vue';
import apiExamTask from '@/api/modules/examTask.js';
import examQuestionApi from "@/api/modules/question";
import {numberToLetter, deepCopy} from '../../utils/tools.js';
export default {
name: 'comEditPaper',
components:{imgupload},
props:{
id:{
type:String,
default:''
}
},
data() {
return {
editShow:false,
loading:false,
paperId:'',
imageBaseUrl:process.env.VUE_APP_FILE_BASE_URL,
title:'',
optionShow:false,
qpaper:{qtype:'',keyword:''},
paperLength:0,
totalScore:0,
qitems:[],
tempItems:[],
paper: {
id:'',
testName: '',
remark: '',
difficulty: ''
},
paperQEdit:false,
cellData:{
show:false,
title:'修改题干',
item:null,
optIndex:-1,
text:'',
img:''
}
}
},
mounted() {
if(this.id){
this.paperId=this.id;
}
},
methods:{
loadTestPaper(){
if(this.paperId){
this.editShow=true;
this.loading=true;
apiPaper.detail(this.paperId).then(res=>{
if(res.status === 200) {
this.paper=res.result;
//转化试题
this.qitems=JSON.parse(res.result.paperContent);
//console.log(this.qitems,this.qitems)
this.tempItems=this.qitems;
this.paperCalculation();
} else {
this.$message.error(res.message);
}
this.loading=false;
})
}
},
show(paperId){
this.paperId=paperId;
this.editShow=true;
this.loadTestPaper();
},
selectQuery(){
this.qitems=this.tempItems;
if(this.qpaper.qtype == '' && this.qpaper.keyword == '') {
this.$forceUpdate();
return
}
// let data = deepCopy(this.paperData);
if(this.qpaper.qtype) {
this.qitems=this.qitems.filter(item=>{
if(item.type === this.qpaper.qtype){
return true;
}else{
return false;
}
})
}
if(this.qpaper.keyword) {
this.qitems = this.qitems.filter(item => {
return item.title.toLowerCase().indexOf(this.qpaper.keyword.toLowerCase()) > -1;
});
}
//this.paper.data = data;
this.$forceUpdate();
},
resetQuery(){
this.qpaper.qtype='';
this.qpaper.keyword='';
this.qitems=this.tempItems;
},
checkedChange(){
this.$forceUpdate();
},
changeInput(e){
this.$forceUpdate();
},
setJudgeAnswer(item,flag){
item.answer=flag;
},
setOptAnswer(item,opt){
if(item.type!=2){//单选的情况,先清空
item.optionList.forEach(op=>{
op.isAnswer=false;
})
}
if(opt.isAnswer){
opt.isAnswer=false;
}else{
opt.isAnswer=true;
}
},
paperCalculation() {
this.totalScore=0;
this.paperLength=0;
this.qitems.forEach(item=>{
if(item.type < 900) {
this.paperLength++;
this.totalScore += Number(item.defaultScore);
}
})
},
save(){
this.loading=true;
this.paper.paperContent=JSON.stringify(this.qitems);
apiPaper.update(this.paper).then((res)=>{
if(res.status === 200) {
this.$message.success('保存成功!')
this.editShow=false;
} else {
this.$message.error(res.message);
}
this.loading=false;
})
},
cancel(){
this.editShow=false;
},
showCellEdit(itemIdx,optIdx){
this.cellData.item=this.qitems[itemIdx];
this.cellData.optIndex=optIdx;
this.cellData.img='';
if(optIdx==-1){
this.cellData.text=this.cellData.item.title;
this.cellData.img=this.cellData.item.images;
this.cellData.title="修改题干";
}else{
this.cellData.text=this.cellData.item.optionList[this.cellData.optIndex].content;
this.cellData.img=this.cellData.item.optionList[this.cellData.optIndex].images;
this.cellData.title="修改选项";
}
//console.log(this.cellData,'this.cellData');
this.cellData.show=true;
},
cellConfirm(){
if(this.cellData.optIndex==-1){
this.cellData.item.title=this.cellData.text;
this.cellData.item.images=this.cellData.img;
}else{
this.cellData.item.optionList[this.cellData.optIndex].content=this.cellData.text;
this.cellData.item.optionList[this.cellData.optIndex].images=this.cellData.img;
}
this.cellData.show=false;
},
cellCancel(){
this.cellData.show=false;
},
cellImageSuccss(res){
//console.log(res,'res');
this.cellData.img=res.result.filePath;
},
cellImageRemove(){
this.cellData.img='';
}
}
}
</script>
<style scoped lang="scss">
.paper-title{
text-align: center;
font-size: 18px;
}
.paper-box{
max-height: 500px;
overflow-y: auto;
}
.bigred {
color: red;
font-size: 20px;
}
.opt{
padding-top:10px;cursor: pointer;
}
.optanswer{
color: green;
background-color: #fffaf7;
}
.optanswer-span{
color: green;
display: inline-block;
width: 20px;
}
.qimg{
padding-left: 30px;
max-width: 200px;
.qimg-fit{
width:100%;
object-fit:scale-down
}
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<div class="component-upload-image">
<el-upload
:action="uploadImgUrl"
list-type="picture-card"
:on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload"
:on-error="handleUploadError"
name="file"
:data="data"
:show-file-list="false"
:headers="headers"
style="display: inline-block; vertical-align: top;">
<div v-if="!imageUrl">
<div class="image-slot">
<i class="el-icon-plus" />
</div>
</div>
<div v-else class="image">
<el-image :src="imageUrl" :style="`width:148px;height:148px;`" fit="fill"/>
<div class="mask">
<div class="actions">
<span title="预览" @click.stop="dialogVisible = true">
<i class="el-icon-zoom-in" />
</span>
<span title="移除" @click.stop="removeImage">
<i class="el-icon-delete" />
</span>
</div>
</div>
</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible" title="预览" width="800" append-to-body>
<img :src="imageUrl" style="display: block; max-width: 100%; margin: 0 auto;">
</el-dialog>
</div>
</template>
<script>
import { getToken } from "@/utils/token";
export default {
data() {
return {
dialogVisible: false,
uploadImgUrl: process.env.VUE_APP_BASE_API + "/xboe/sys/xuploader/file/upload", // 上传的图片服务器地址
fileBaseUrl:process.env.VUE_APP_FILE_BASE_URL,
imageUrl:'',
headers: {
'XBOE-Access-Token': getToken(),
},
data:{
dir:this.dir
}
};
},
props: {
text:{
type:String,
default:'上传图片'
},
dir:{
type:String,
default:''
},
value: {
type: String,
default: "",
},
},
mounted() {
if(this.value){
if(this.value.startsWith('http')){
this.imageUrl=this.value;
}else{
this.imageUrl=this.fileBaseUrl+this.value;
}
}else{
this.imageUrl='';
}
//console.log(this.imageUrl,'this.imageUrl');
},
watch:{
value(newVal,oldVal){
//console.log(newVal,'newVal');
if(newVal){
if(newVal.startsWith('http')){
this.imageUrl=newVal;
}else{
this.imageUrl=this.fileBaseUrl+newVal;
}
}else{
this.imageUrl='';
}
//console.log(this.imageUrl,'this.imageUrl');
},
immediate: true
},
methods: {
removeImage() {
this.imageUrl="";
this.$emit("remove",this.value);
},
handleUploadSuccess(res) {
this.imageUrl=res.result.httpPath;
this.$emit("success", res);
this.loading.close();
},
handleBeforeUpload() {
this.loading = this.$loading({
lock: true,
text: "上传中",
background: "rgba(0, 0, 0, 0.7)",
});
},
handleUploadError() {
this.$message({
type: "error",
message: "上传失败",
});
this.loading.close();
},
}
};
</script>
<style scoped lang="scss">
.image-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.image-uploader .el-upload--picture-card{
width: 200px;
background-color: #fbfdff;
border: 1px dashed #c0ccda;
border-radius: 6px;
box-sizing: border-box;
width: 148px;
height: 148px;
cursor: pointer;
line-height: 146px;
vertical-align: top;
}
.image-uploader .el-upload:hover {
border-color: #409EFF;
}
.image-uploader-icon {
font-size: 28px;
color: #8c939d;
text-align: center;
margin-top: 50px;
display: block;
}
.icon-text{
font-size: 14px;
display: block;
height: 30px;
line-height: 35px;
}
.avatar {
width: 250px;
height: 178px;
display: block;
}
.image-slot{
width: 148px;
height: 148px;
line-height: 148px;
}
.image {
position: relative;
.mask {
opacity: 0;
position: absolute;
top: 0;
width: 100%;
height: 148px;
line-height: 148px;
background-color: rgba(0, 0, 0, 0.5);
transition: all 0.3s;
}
&:hover .mask {
opacity: 1;
}
}
</style>

View File

@@ -63,7 +63,7 @@
<template slot-scope="scope">
<!-- v-if="scope.row.collectNumber === 0" -->
<el-button type="text" v-if="!scope.row.published" @click="releaseData(scope.row,true)">发布</el-button>
<!-- // 成绩控制需要结束状态 -->
<el-button type="text" v-if="scope.row.published" @click="viewResults(scope.row)">成绩</el-button>
<el-button type="text" v-if="scope.row.published" @click="pushResults(scope.row)">推送</el-button>
@@ -74,6 +74,7 @@
<el-dropdown-menu slot="dropdown">
<!-- <el-dropdown-item command="a">查看</el-dropdown-item> -->
<el-dropdown-item command="b" @click.native="editData(scope.row)">编辑</el-dropdown-item>
<el-dropdown-item command="b" @click.native="editPaper(scope.row)">修改试卷</el-dropdown-item>
<el-dropdown-item command="c" @click.native="deleteData(scope.row)">删除</el-dropdown-item>
<el-dropdown-item command="a" @click.native="creatQrCode(scope.row)">二维码</el-dropdown-item>
<div v-if="scope.row.published">
@@ -122,7 +123,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="24" >
<el-form-item label="考试名称" prop="testName">
@@ -660,6 +661,7 @@
</el-main>
</el-container>
</el-container>
<editPaper ref="comEditPaper"></editPaper>
</div>
</template>
@@ -676,8 +678,10 @@ import usergroupApi from "@/api/modules/usergroup";
import pushRecordApi from "@/api/modules/pushRecord";
import {toScoreTow} from '@/utils/tools.js'
import apiUserBasic from '@/api/boe/userbasic.js';
import editPaper from "@/components/Exam/EditPaper";
export default {
name: 'articleItems',
components:{editPaper},
computed: {
...mapGetters(['resOwnerMap','sysTypeMap']),
},
@@ -766,6 +770,13 @@ export default {
this.loadData()
},
methods: {
editPaper(row){
if(!row.paperId){
this.$message.error("此考试还未选择试卷");
return;
}
this.$refs.comEditPaper.show(row.paperId);
},
open(row,num) {
this.$confirm('确定要下架改考试么?', '提示', {
confirmButtonText: '确定',
@@ -781,7 +792,7 @@ export default {
this.$message({
type: 'info',
message: '已取消下架'
});
});
});
},
exportsList(){

View File

@@ -48,6 +48,7 @@
<el-table-column label="操作" width="150px" fixed="right">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-folder-opened" @click="eidtVolume(scope.row)">编辑</el-button>
<!-- <el-button type="text" icon="el-icon-folder-opened" @click="showEditPaper(scope.row)">编辑试题</el-button> -->
<el-button type="text" icon="el-icon-delete" @click="deleteVolume(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -190,13 +191,13 @@
</div>
<div style="padding: 15px;">
<div>
<div> {{ item.title }}</div>
<div> {{ item.title }}</div>
<div v-if="item.images" class="xuanxiangimg"><img :src="imageBaseUrl + item.images" alt=""> </div>
</div>
<div v-if="qpaper.optShow">
<div v-if="item.type == 3">{{item.answer=='true'?'正确':'错误'}}</div>
<div v-else v-for="(opt, optIdx) in item.optionList" :key="optIdx">
<div>{{ optIdx + 1 }}, {{ opt.content }}</div>
<div>{{ optIdx + 1 }}, {{ opt.content }}</div>
<div v-if="opt.images" class="xuanxiangimg"><img :src="imageBaseUrl + opt.images" alt=""> </div>
</div>
</div>
@@ -287,6 +288,7 @@
</div>
<span slot="footer" class="dialog-footer"><el-button @click="queryLise()"> </el-button></span>
</el-dialog>
<editPaper ref="comEditPaper"></editPaper>
</div>
</template>
@@ -297,8 +299,10 @@ import examQuestionApi from "@/api/modules/question";
import { deepClone } from '../../utils';
import {numberToLetter, deepCopy} from '../../utils/tools.js';
import { mapGetters,mapActions} from 'vuex';
import editPaper from "@/components/Exam/EditPaper";
export default {
name: 'articleItems',
name: 'testPaper',
components:{editPaper},
data() {
return {
imageBaseUrl:process.env.VUE_APP_FILE_BASE_URL,
@@ -429,6 +433,9 @@ export default {
// }
},
methods: {
showEditPaper(row){
this.$refs.comEditPaper.show(row.id);
},
checkedChange(){
this.$forceUpdate();
},
@@ -493,22 +500,22 @@ export default {
this.$forceUpdate();
return
}
let data = deepCopy(this.paperData);
if(this.qpaper.qtype !== '') {
data = data.filter(item => {
if(item.type === this.qpaper.qtype){
return item;
}
});
}
if(this.qpaper.keyword !== '') {
data = data.filter(item => {
return item.title.toLowerCase()
.indexOf(this.qpaper.keyword.toLowerCase()) > -1;
});
}
this.paper.data = data;
this.$forceUpdate();
let data = deepCopy(this.paperData);
if(this.qpaper.qtype !== '') {
data = data.filter(item => {
if(item.type === this.qpaper.qtype){
return item;
}
});
}
if(this.qpaper.keyword !== '') {
data = data.filter(item => {
return item.title.toLowerCase()
.indexOf(this.qpaper.keyword.toLowerCase()) > -1;
});
}
this.paper.data = data;
this.$forceUpdate();
},
checkDelete(index) {
this.paper.data.splice(index,1);