This commit is contained in:
caoqianming 2022-02-04 23:57:44 +08:00
commit cf054db8e2
11 changed files with 342 additions and 9 deletions

View File

@ -108,6 +108,13 @@ export function deleteQuestion(id) {
method: 'delete', method: 'delete',
}) })
} }
export function deleteQuestions(data) {
return request({
url: `/question/question/deletes/`,
method: 'post',
data
})
}
export function importQuestion(data) { export function importQuestion(data) {
return request({ return request({
url: `/question/question/import/`, url: `/question/question/import/`,

View File

@ -279,7 +279,13 @@ export const asyncRoutes = [
path: 'examtest', path: 'examtest',
name: 'ExamTest', name: 'ExamTest',
component: () => import('@/views/analyse/examtest.vue'), component: () => import('@/views/analyse/examtest.vue'),
meta: { title: '答卷列表', icon: '', perms: ['examtest_view'] } meta: { title: '模考答卷', icon: '', perms: ['examtest_view'] }
},
{
path: 'examtest2',
name: 'ExamTest2',
component: () => import('@/views/analyse/examtest2.vue'),
meta: { title: '正式考试答卷', icon: '', perms: ['examtest_view'] }
}, },
{ {
path: 'chart', path: 'chart',

View File

@ -1,6 +1,6 @@
module.exports = { module.exports = {
title: '安建环培训考试平台', title: '中科辐射',
/** /**
* @type {boolean} true | false * @type {boolean} true | false

View File

@ -138,6 +138,7 @@ import Pagination from "@/components/Pagination";
const listQuery = { const listQuery = {
page: 1, page: 1,
limit: 20, limit: 20,
type:'自助模考',
search:'' search:''
}; };
export default { export default {
@ -152,8 +153,7 @@ export default {
listLoading: true, listLoading: true,
typeOptions: [ typeOptions: [
{ key: "自助模考", label: "自助模考", value: "自助模考" }, { key: "自助模考", label: "自助模考", value: "自助模考" },
{ key: "押卷模考", label: "押卷模考", value: "押卷模考"}, { key: "押卷模考", label: "押卷模考", value: "押卷模考"}
{ key: "正式考试", label: "正式考试", value: "正式考试"},
], ],
passOptions: [ passOptions: [
{ key: true, label: "通过", value: true }, { key: true, label: "通过", value: true },
@ -230,6 +230,7 @@ export default {
this.listQuery = { this.listQuery = {
page: 1, page: 1,
limit: 20, limit: 20,
type:'自助模考',
search:'', search:'',
}; };
this.value = [] this.value = []

View File

@ -0,0 +1,279 @@
<template>
<div class="app-container">
<div style="margin-top:10px">
<el-select
v-model="listQuery.is_pass"
placeholder="是否通过"
clearable
style="width: 200px"
class="filter-item"
@change="handleFilter"
>
<el-option
v-for="item in passOptions"
:key="item.key"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-date-picker
v-model="value"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
<el-input
v-model="listQuery.search"
placeholder="输入用户名或用户单位名搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="handleFilter"
/>
<el-button
class="filter-item"
type="primary"
icon="el-icon-refresh-left"
@click="resetFilter"
>刷新重置</el-button>
<el-button type="primary" icon="el-icon-download" @click="exportTest" >导出Excel</el-button>
<div style="margin-top:10px">
</div>
</div>
<el-table
:data="tableData.results"
style="width: 100%;margin-top:10px;"
border
stripe
fit
v-loading="listLoading"
highlight-current-row
max-height="600"
@sort-change="changeSort"
>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column align="left" label="类型">
<template slot-scope="scope">{{ scope.row.type }}</template>
</el-table-column>
<el-table-column align="left" label="用户">
<template slot-scope="scope">{{ scope.row.consumer_detail.name }}</template>
</el-table-column>
<el-table-column align="left" label="单位">
<template slot-scope="scope">{{ scope.row.consumer_detail.company_name }}</template>
</el-table-column>
<el-table-column align="left" label="工作类别">
<template slot-scope="scope">{{ scope.row.workscope_name }}</template>
</el-table-column>
<el-table-column align="left" label="所属考试">
<template slot-scope="scope">{{ scope.row.exam_name }}</template>
</el-table-column>
<el-table-column align="left" label="得分" sortable='custom' prop="score">
<template slot-scope="scope">{{ scope.row.score }}</template>
</el-table-column>
<el-table-column align="left" label="耗时(时分秒)" sortable='custom' prop="took">
<template slot-scope="scope">{{ scope.row.took_format }}</template>
</el-table-column>
<el-table-column align="left" label="答题时间">
<template slot-scope="scope">{{ scope.row.start_time }}</template>
</el-table-column>
<el-table-column align="center" label="操作" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.type=='正式考试'"
type="primary"
size="small"
@click="handleExport(scope)"
>生成Word</el-button>
<el-button
v-if="scope.row.type=='正式考试'"
type="warning"
size="small"
@click="handleExport2(scope)"
>重新生成</el-button>
<el-button
v-if="scope.row.type=='正式考试'"
type="danger"
size="small"
@click="handleDelete(scope)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.count>0"
:total="tableData.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList"
/>
</div>
</template>
<script>
import { getExamTestlist, exportTest, exportwTest, deleteExamtest } from "@/api/examtest";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination";
const listQuery = {
page: 1,
limit: 20,
type:'正式考试',
search:''
};
export default {
components: { Pagination },
data() {
return {
listQuery: Object.assign({}, listQuery),
tableData: {
count:0,
results:[],
},
listLoading: true,
typeOptions: [
{ key: "自助模考", label: "自助模考", value: "自助模考" },
{ key: "押卷模考", label: "押卷模考", value: "押卷模考"},
{ key: "正式考试", label: "正式考试", value: "正式考试"},
],
passOptions: [
{ key: true, label: "通过", value: true },
{ key: false, label: "未通过", value: false},
],
pickerOptions: {
shortcuts: [{
text: '最近一天',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
value: '',
};
},
computed: {},
watch:{
value:'setTimeRange',
},
created() {
this.getQuery();
},
methods: {
checkPermission,
getQuery() {
if(this.$route.params.exam){
this.listQuery.exam = this.$route.params.exam;
this.getList()
}else{
this.getList()
}
},
getList() {
this.listLoading = true;
getExamTestlist(this.listQuery).then(response => {
this.tableData = response.data;
this.listLoading = false;
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
limit: 20,
type:'正式考试',
search:'',
};
this.value = []
this.getList();
},
handleExport(scope) {
const loading = this.$loading({text: '正在生成word...',});
exportwTest(scope.row.id).then(res=>{
loading.close()
window.open(res.data.path, "_blank");
}).catch(e=>{loading.close()})
},
handleDelete(scope){
this.$confirm("确认删除该考试记录吗?将丢失数据!", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error"
})
.then(async () => {
await deleteExamtest(scope.row.id);
this.getList()
this.$message({
type: "success",
message: "成功删除!"
});
})
.catch(err => {
console.error(err);
});
},
handleExport2(scope) {
const loading = this.$loading({text: '正在重新生成word...',});
exportwTest(scope.row.id, {anew:true}).then(res=>{
loading.close()
window.open(res.data.path, "_blank");
}).catch(e=>{loading.close()})
},
exportTest() {
const loading = this.$loading();
exportTest(this.listQuery).then(response => {
loading.close()
window.open(response.data.path, "_blank");
});
},
setTimeRange(){
this.listQuery.start = this.value[0],
this.listQuery.end = this.value[1],
this.getList()
},
changeSort (val) {
if(val.order == 'ascending'){
this.listQuery.ordering = val.prop
}else{
this.listQuery.ordering = '-' + val.prop
}
this.getList()
},
}
};
</script>

View File

@ -250,7 +250,7 @@ export default {
}); });
}, },
handleView(scope){ handleView(scope){
this.$router.push({name:'ExamTest', params:{exam:scope.row.id}}) this.$router.push({name:'ExamTest2', params:{exam:scope.row.id}})
}, },
async confirmexam(form) { async confirmexam(form) {

View File

@ -70,6 +70,12 @@
<el-button slot="reference">Excel导入</el-button> <el-button slot="reference">Excel导入</el-button>
</el-popover> </el-popover>
<el-button type="primary" icon="el-icon-download" @click="exportQuestion">导出Excel</el-button> <el-button type="primary" icon="el-icon-download" @click="exportQuestion">导出Excel</el-button>
<el-button
type="danger"
@click="handleDeletes(scope)"
icon="el-icon-delete"
v-if="checkPermission(['question_delete'])"
>批量删除</el-button>
</div> </div>
</div> </div>
<el-table <el-table
@ -168,7 +174,8 @@ import {
deleteQuestion, deleteQuestion,
importQuestion, importQuestion,
exportQuestion, exportQuestion,
enableQuestions enableQuestions,
deleteQuestions
} from "@/api/question"; } from "@/api/question";
import { genTree, deepClone } from "@/utils"; import { genTree, deepClone } from "@/utils";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
@ -307,6 +314,16 @@ export default {
// console.error(err); // console.error(err);
}); });
}, },
handleDeletes(){
if (this.selects.length) {
deleteQuestions({ids:this.selects}).then(res=>{
this.$message.success("成功");
this.getList();
})
} else {
this.$message.warning("请先选择题目");
}
},
exportQuestion() { exportQuestion() {
const loading = this.$loading({ const loading = this.$loading({
text:'正在准备..' text:'正在准备..'

View File

@ -34,7 +34,7 @@ def exportw_test(obj, bool):
""" """
导出个人考试记录word版本 导出个人考试记录word版本
""" """
filename = obj.exam.name + '-' + str(obj.exam.id) + '-' + obj.consumer.name + '-' + obj.consumer.id + '.docx' filename = obj.exam.name + '-' + str(obj.exam.id) + '-' + obj.consumer_detail['name'] + '-' + str(obj.score) + '.docx'
path = '/media/export/' + filename path = '/media/export/' + filename
fullpath = BASE_DIR + path fullpath = BASE_DIR + path
if bool or (not os.path.exists(fullpath)): if bool or (not os.path.exists(fullpath)):

View File

@ -23,6 +23,7 @@ class Exam(CommonModel):
qdimgs = JSONField('签到图片', default=list) qdimgs = JSONField('签到图片', default=list)
xcimgs = JSONField('现场图片', default=list) xcimgs = JSONField('现场图片', default=list)
def __str__(self): def __str__(self):
return self.name return self.name
@ -56,6 +57,9 @@ class ExamTest(CommonModel):
verbose_name = '模拟考试' verbose_name = '模拟考试'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
def __str__(self):
return self.name
class AnswerDetail(SoftCommonModel): class AnswerDetail(SoftCommonModel):

View File

@ -235,6 +235,7 @@ class ExamTestExamListSerializer(serializers.ModelSerializer):
workscope_name = serializers.StringRelatedField(source='workscope', read_only=True) workscope_name = serializers.StringRelatedField(source='workscope', read_only=True)
took_format = serializers.SerializerMethodField() took_format = serializers.SerializerMethodField()
candidate_ = serializers.SerializerMethodField() candidate_ = serializers.SerializerMethodField()
exam_name = serializers.StringRelatedField(source='exam', read_only=True)
class Meta: class Meta:
model = ExamTest model = ExamTest

View File

@ -6,13 +6,13 @@ from rest_framework import status
from rest_framework.decorators import action, permission_classes from rest_framework.decorators import action, permission_classes
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from crm.models import PaySubject from crm.models import Consumer, PaySubject
from examtest.models import WorkScope from examtest.models import WorkScope
from server import settings from server import settings
from utils.custom import CommonPagination from utils.custom import CommonPagination
@ -116,6 +116,13 @@ class QuestionViewSet(ModelViewSet):
ret['panduan'] = queryset.filter(type='判断').count() ret['panduan'] = queryset.filter(type='判断').count()
return Response(ret) return Response(ret)
def get_queryset(self):
user = self.request.user
if isinstance(user, Consumer):
questioncats = user.workscope.questioncat.all()
self.queryset = self.queryset.filter(questioncat__in = questioncats)
return super().get_queryset()
@action(methods=['get'], detail=False, @action(methods=['get'], detail=False,
url_path='export', url_name='export_question', perms_map=[{'*':'export_question'}]) url_path='export', url_name='export_question', perms_map=[{'*':'export_question'}])
def export(self, request): def export(self, request):
@ -143,6 +150,17 @@ class QuestionViewSet(ModelViewSet):
i.save() i.save()
return Response() return Response()
@action(methods=['post'], detail=False, perms_map=[{'*':'question_delete'}])
def deletes(self, request):
"""
批量删除
"""
ids = request.data.get('ids', [])
if request.user.is_superuser:
Question.objects.filter(id__in=ids).update(is_delete=True)
return Response()
return Response({'error':'权限不足'})
@action(methods=['post'], detail=False, url_name='enable_question', permission_classes=[IsAuthenticated]) @action(methods=['post'], detail=False, url_name='enable_question', permission_classes=[IsAuthenticated])
def enable(self, request): def enable(self, request):
ids = request.data.get('ids',None) ids = request.data.get('ids',None)