This commit is contained in:
shijing 2024-06-18 15:38:35 +08:00
commit acb25ae76e
29 changed files with 666 additions and 160 deletions

File diff suppressed because one or more lines are too long

View File

@ -27,10 +27,12 @@
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"pdfobject": "^2.3.0",
"sortablejs": "^1.15.2",
"vod-js-sdk-v6": "^1.4.12",
"vue": "2.6.10",
"vue": "2.7",
"vue-pdf": "^4.2.0",
"vue-router": "3.0.6",
"vuedraggable": "^2.24.3",
"vuex": "3.1.0",
"xlsx": "^0.15.5"
},

View File

@ -27,10 +27,11 @@ export function updateQuestioncat(id, data) {
//删除题目类型
export function deleteQuestioncat(id) {
return request({
url: `/exam/questioncat/${id}/`,
url: `/exam/question/${id}/`,
method: 'delete'
})
}
//题目列表
export function getQuestionList(query) {
return request({
@ -69,6 +70,14 @@ export function deleteQuestion(id) {
method: 'delete'
})
}
//批量删除题目类型
export function deletesQuestion(data) {
return request({
url: `/exam/question/deletes/`,
method: 'post',
data
})
}
//导入题目
export function importQuestion(data) {
return request({
@ -82,7 +91,7 @@ export function exportQuestion(data) {
return request({
url: `/exam/question/export/`,
method: 'get',
params: query
params: data
})
}
//启用题目
@ -213,6 +222,15 @@ export function updateExamRecord(id, data) {
data
})
}
//导出考试记录
export function exportRecord(data) {
return request({
url: `/exam/examrecord/export/`,
method: 'get',
params: data,
})
}
//删除考试记录
export function deleteExamRecord(id) {
return request({

View File

@ -0,0 +1 @@
<svg t="1717725053175" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3023" width="200" height="200"><path d="M153.6 58.88v901.632h706.56V58.88H153.6z m641.024 834.56H218.624v-768h576v768z" fill="#ffffff" p-id="3024"></path><path d="M399.36 228.352h-35.84c-2.048 0-4.608 1.536-5.12 3.584l-31.232 186.368c-0.512 4.096 2.56 7.68 6.144 7.68h32.768c2.048 0 4.096-1.536 4.096-3.584l33.28-188.416c1.024-3.072-1.024-5.632-4.096-5.632zM537.088 242.176c-8.704-10.24-23.552-15.36-39.424-15.36-15.872 0-32.256 5.12-44.544 15.36-12.288 9.728-19.968 22.016-23.552 42.496l-15.36 86.016c-3.584 20.48 0 32.256 8.704 42.496 8.704 10.24 23.552 15.36 39.424 15.36 15.872 0 32.256-5.12 44.544-15.36 12.288-9.728 19.968-22.016 23.552-42.496l15.36-86.016c3.584-20.48 0-32.768-8.704-42.496z m-35.328 40.96l-15.872 89.6c-2.048 10.752-8.704 16.384-16.896 16.384s-12.8-5.12-11.264-16.384l15.872-89.6c2.048-10.752 8.704-16.384 16.896-16.384 8.192 0 13.312 5.12 11.264 16.384zM676.352 242.176c-8.704-10.24-23.552-15.36-39.424-15.36s-32.256 5.12-44.544 15.36c-12.288 9.728-19.968 22.016-23.552 42.496l-15.36 86.016c-3.584 20.48 0 32.256 8.704 42.496 8.704 10.24 23.552 15.36 39.424 15.36 15.872 0 32.256-5.12 44.544-15.36 12.288-9.728 19.968-22.016 23.552-42.496l15.36-86.016c3.584-20.48 0-32.768-8.704-42.496z m-35.328 40.96l-15.872 89.6c-2.048 10.752-8.704 16.384-16.896 16.384s-12.8-5.12-11.264-16.384l15.872-89.6c2.048-10.752 8.704-16.384 16.896-16.384 8.192 0 13.312 5.12 11.264 16.384zM313.856 515.584h347.648V573.44H313.856zM313.856 638.976h275.968v57.856H313.856zM313.856 767.488h275.968v57.856H313.856z" fill="#ffffff" p-id="3025"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -55,8 +55,11 @@
@sort-change="changeSort"
>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column align="left" label="类型">
<!-- <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.exam_name }}</template>
</el-table-column>
<el-table-column align="left" label="用户">
<template slot-scope="scope">{{ scope.row.create_by_name}}</template>
@ -82,20 +85,32 @@
<el-table-column align="left" label="答题时间">
<template slot-scope="scope">{{ scope.row.start_time }}</template>
</el-table-column>
<el-table-column align="left" label="证书路径" prop="cert_path">
<template slot-scope="scope">
<img v-if="scope.row.cert_path" :src="scope.row.cert_path" @click="handlePictureCardPreview(scope.row.cert_path)" alt="图片" height="30" width="50"/>
</template>
</el-table-column>
<el-table-column align="left" label="单位名称">
<template slot-scope="scope">{{ scope.row.blong_dept_name }}</template>
</el-table-column>
<el-table-column align="left" label="考试时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column align="center" label="操作" fixed="right">
<template slot-scope="scope">
<el-button
<!-- <el-button
v-if="scope.row.type=='正式考试'"
type="primary"
size="small"
@click="handleExport(scope)"
>生成Word</el-button>
<el-button
>生成Word</el-button> -->
<!-- <el-button
v-if="scope.row.type=='正式考试'"
type="warning"
size="small"
@click="handleExport2(scope)"
>重新生成</el-button>
>重新生成</el-button> -->
<el-button
v-if="scope.row.type=='正式考试'"
type="danger"
@ -116,14 +131,13 @@
</template>
<script>
import { getExamRecordList, exportTest, exportwTest, deleteExamRecord,issue } from "@/api/exam";
import { getExamRecordList, exportRecord, exportwTest, deleteExamRecord,issue } from "@/api/exam";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination";
const listQuery = {
page: 1,
limit: 20,
type:'正式考试',
search:''
};
export default {
@ -193,13 +207,13 @@
methods: {
checkPermission,
getQuery() {
if(this.$route.params.exam){
this.listQuery.exam = this.$route.params.exam;
this.getList()
}else{
this.getList()
if(this.$route.query.exam){
this.listQuery.exam = this.$route.query.exam;
}
this.getList()
},
handlePictureCardPreview(url){
window.open(url);
},
getList() {
this.listLoading = true;
@ -216,11 +230,10 @@
this.listQuery = {
page: 1,
limit: 20,
type:'正式考试',
search:'',
};
this.value = []
this.getList();
// this.getList();
},
handleExport(scope) {
const loading = this.$loading({text: '正在生成word...',});
@ -236,7 +249,7 @@
type: "error"
})
.then(async () => {
await deleteExamtest(scope.row.id);
await deleteExamRecord(scope.row.id);
this.getList()
this.$message({
type: "success",
@ -256,10 +269,10 @@
},
exportTest() {
const loading = this.$loading();
exportTest(this.listQuery).then(response => {
loading.close()
window.open(response.data.path, "_blank");
});
exportRecord(this.listQuery).then(res=>{
loading.close()
window.open(res.data.path, "_blank");
}).catch(e=>{loading.close()})
},
setTimeRange(){
this.listQuery.start = this.value[0],

View File

@ -39,9 +39,9 @@
<el-table-column label="考试地点">
<template slot-scope="scope">{{ scope.row.place }}</template>
</el-table-column>
<el-table-column label="参考机会">
<!-- <el-table-column label="参考机会">
<template slot-scope="scope">{{ scope.row.chance }}</template>
</el-table-column>
</el-table-column> -->
<el-table-column label="开启时间">
<template slot-scope="scope">{{ scope.row.open_time }}</template>
</el-table-column>
@ -96,9 +96,9 @@
<el-form-item label="考试地点" prop="place">
<el-input v-model="exam.place" placeholder="考试地点" />
</el-form-item>
<el-form-item label="参考机会" prop="chance">
<!-- <el-form-item label="参考机会" prop="chance">
<el-input-number v-model="exam.chance" placeholder="参考机会" :min="1"/>
</el-form-item>
</el-form-item> -->
<el-form-item label="开启时间" prop="open_time">
<el-date-picker
v-model="exam.open_time"
@ -154,17 +154,27 @@
</el-select>
</el-form-item>
<el-form-item label="考试部门" prop="participant_dep" style="width:100%" clearable collapse-tags filterable >
<el-button @click="selectAll">全选部门</el-button>
<el-select v-model="exam.participant_dep" multiple collapse-tags filterable placeholder = "选择部门" >
<!-- <el-button @click="selectAll">全选部门</el-button> -->
<!-- <el-select v-model="exam.participant_dep" multiple collapse-tags filterable placeholder = "选择部门" >
<el-option
v-for="item in depOptions"
:key = "item.id"
:label = "item.name"
:value = "item.id">
</el-option>
</el-select>
</el-select> -->
<div>
<el-transfer
:props="{key: 'id',label: 'name'}"
:data="depOptions"
v-model="exam.participant_dep"
:titles="['未选部门','指定考试部门']"
@change="handleChange"
@on-change="handleOnChange"
></el-transfer>
</div>
</el-form-item>
<!-- <el-form-item label="考试人员" prop="participant_user" style="width:100%" clearable>
<el-select v-model="exam.participant_user" multiple filterable placeholder = "请选择人员">
<el-option
@ -184,6 +194,7 @@
:total="total"
></el-pagination>
</el-form-item> -->
</el-form>
<div style="text-align:right;">
<el-button type="danger" @click="dialogVisible=false">取消</el-button>
@ -197,7 +208,11 @@
import {getUserList} from "@/api/user";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"
import Vue from 'vue'
// import ElementUI from 'element-ui'
// import 'element-ui/lib/theme-chalk/index.css'
// Vue.use(ElementUI)
const defaultexam = {
id: "",
@ -207,7 +222,7 @@
close_time: null,
proctor_name:'',
proctor_phone:'',
chance:3,
chance:1,
paper:'',
certificate:'',
course_name:[],
@ -270,9 +285,9 @@
this.paperOptions = res.data
})
},
selectAll(){
this.exam.participant_dep = this.depOptions.map(option => option.id);
},
// selectAll(){
// this.exam.participant_dep = this.depOptions.map(option => option.id);
// },
getList() {
this.listLoading = true;
debugger;
@ -281,6 +296,12 @@
this.listLoading = false;
});
},
handleChange(value, direction, movedKeys) {
console.log(value, direction, movedKeys)
},
handleOnChange(value, direction, movedKeys) {
console.log(value, direction, movedKeys)
},
handleSizeChange(val) {
// 改变每页显示的条数
this.pageSize = val;
@ -353,7 +374,7 @@
});
})
.catch(err => {
// console.error(err);
console.error(err);
});
},
handleView(scope){

View File

@ -36,7 +36,8 @@
<el-button slot="reference">Excel导入</el-button>
</el-popover>
<el-button type="primary" icon="el-icon-download" @click="exportQuestion">导出Excel</el-button>
<el-button type="primary" icon="el-icon-download" @click="exportQuestions">导出Excel</el-button>
<el-button type="danger" v-if="checkPermission(['question_batch_delete'])" :disabled="this.selects.length==0" @click="batchDelete()">批量删除</el-button>
</div>
</div>
<el-table :data="tableData" style="width: 100%;margin-top:10px;" border stripe fit v-loading="listLoading"
@ -104,6 +105,7 @@
importQuestion,
exportQuestion,
enableQuestions,
deletesQuestion,
} from "@/api/exam";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination";
@ -159,7 +161,6 @@ export default {
handleUploadSuccess(res, file) {
if (res.code == 201) {
const loading = this.$loading({ text: "正在导入中..." })
console.log(res.data);
importQuestion(res.data).then(response => {
loading.close()
if (response.code == 200) {
@ -235,7 +236,7 @@ export default {
// console.error(err);
});
},
exportQuestion() {
exportQuestions() {
const loading = this.$loading({
text: '正在准备..'
});
@ -244,6 +245,34 @@ export default {
window.open(response.data.path, "_blank");
}).catch(e => { loading.close() });
},
batchDelete() {
if (this.selects.length === 0) {
this.$message({
message: '请选择要删除的项',
type: 'warning'
});
return;
}else{
// 删除前的提示
this.$confirm("确认删除记录吗?", "提示", {
type: "warning",
}).then(() => {
console.log(this.selects)
this.deleteItems(this.selects);
this.getList()});
}
},
deleteItems(ids){
let item = {ids:ids};
deletesQuestion(item).then(response => {
if (response.code == 200) {
this.$message.success("删除成功");
this.getList();
} else {
this.$message.error(response.msg);
}
});
},
changeSort(val) {
if (val.order == "ascending") {
this.listQuery.ordering = val.prop;

View File

@ -86,7 +86,7 @@
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('Form')" :loading="submitLoding">保存</el-button>
<el-button type="warning" @click="goBack()">返回</el-button>
<el-button type="warning" @click="goBack()" v-if="qid==null">返回</el-button>
</el-form-item>
</el-form>
</div>
@ -97,6 +97,7 @@ import { upUrl } from "@/api/file";
import { getToken } from "@/utils/auth";
export default {
components:{ },
props:["qid"],
data() {
return {
upHeaders: { Authorization: "JWT " + getToken() },
@ -135,7 +136,12 @@ export default {
watch:{
},
created() {
this.Form.id = this.$route.query.id //接收参数
if(this.qid) {
this.Form.id = this.qid
}else{
this.Form.id = this.$route.query.id //接收参数
}
this.getQuestion();
this.getQuestioncatAll()
},
@ -169,7 +175,12 @@ export default {
type: "success",
message: "修改成功!"
});
this.goBack()
if (this.qid){
this.$emit("updateQuestion", this.Form)
}else{
this.goBack()
}
}
});
} else {

View File

@ -54,12 +54,12 @@
icon="el-icon-edit"
:disabled="!checkPermission(['paper_update'])"
></el-button>
<el-button
<!-- <el-button
type="warning"
size="small"
@click="handleClone(scope)"
:disabled="!checkPermission(['paper_clone'])"
>克隆</el-button>
>克隆</el-button> -->
<el-button
type="danger"
size="small"

View File

@ -1,8 +1,8 @@
<template>
<div class="app-container">
<el-row>
<el-row :gutter="6">
<el-col :span="8">
<h3>基本信息</h3>
<el-card header="试卷信息">
<el-form :model="Form" :rules="rules" ref="Form" label-width="100px" status-icon>
<el-form-item label="名称" prop="name">
<el-input v-model="Form.name" style="width:80%"></el-input>
@ -42,9 +42,10 @@
<el-button type="warning" @click="goBack()">返回</el-button>
</el-form-item>
</el-form>
</el-card>
</el-col>
<el-col :span="16">
<h3>选题信息</h3>
<el-card header="试题信息">
<!-- <div> -->
<div style="display: flex;">
<el-button type="primary" @click="handleChoose" icon="el-icon-plus" style="margin-right: 10px;">选择试题</el-button>
@ -86,34 +87,45 @@
</div>
</el-dialog>
</div>
<div v-for="(item, index) in questions">
<h4>
<el-button
type="danger"
size="small"
@click="handleDelete(index)"
icon="el-icon-delete"
></el-button>
{{ index+1 }} -
<draggable v-model="questions">
<el-card style="margin-top: 4px" shadow="never" v-for="(item, index) in questions" v-bind:key="index">
<div slot="header" style="display: flex;">
<span>{{ index+1 }}</span>
<el-link style="margin-left: auto; margin-right: 8px" type="primary" @click="handleEdit(item, index)" icon="el-icon-edit"></el-link>
<el-link style="margin-right: 4px" type="danger" @click="handleDelete(index)" icon="el-icon-remove"></el-link>
<el-link style="margin-right: 4px" type="danger" @click="handleDelete2(item, index)" icon="el-icon-delete"></el-link>
</div>
<div style="font-weight: bold;">
<el-tag>{{item.type}}</el-tag>
{{ item.name }}
<span>(正确答案:{{item.right}})</span>
</h4>
<div v-for="(value, name) in item.options">{{ name }}: {{ value }}</div>
</div>
</div>
<div style="font-size: 14px;" v-for="(value, name) in item.options">{{ name }}: {{ value }}</div>
</el-card>
</draggable>
</el-card>
</el-col>
</el-row>
<Questionchoose v-bind:chooseVisible="chooseVisible" @closeDg="closeDg" @choseQ="choseQ"></Questionchoose>
<el-dialog title="编辑题目" :visible.sync="qDialog">
<QuestionUpdate v-if="qDialog" :qid="qid" @updateQuestion="updateQuestion"></QuestionUpdate>
</el-dialog>
</div>
</template>
<script>
import { getQuestioncatList, createPaper, UploadPaper } from "@/api/exam";
import { getQuestioncatList, createPaper, UploadPaper, deleteQuestion } from "@/api/exam";
import Questionchoose from "@/views/exam/questionChoose";
import { upUrl, upHeaders } from "@/api/file";
import draggable from 'vuedraggable';
import QuestionUpdate from "./questionupdate.vue";
export default {
components: { Questionchoose },
components: { Questionchoose, draggable, QuestionUpdate },
data() {
return {
qDialog: false,
qid: null,
currentEditIndex: null,
questions: [],
Form: {
name: "",
@ -164,6 +176,15 @@
this.getQuestioncat();
},
methods: {
updateQuestion(data){
this.qDialog = false;
this.questions[this.currentEditIndex] = data;
},
handleEdit(item, index) {
this.qDialog = true;
this.qid = item.id;
this.currentEditIndex = index;
},
getQuestioncat() {
getQuestioncatList().then(response => {
this.workscopeData = response.data.results;
@ -180,10 +201,8 @@
let that =this;
if (res.code == 201){
that.impForm.doc_path = res.data.path;
console.log(that.impForm)
UploadPaper(that.impForm).then(res => {
if (res.code == 200) {
console.log(res.data)
that.questions = res.data;
that.$message({
@ -236,6 +255,21 @@
handleDelete(val) {
this.questions.splice(val, 1);
},
handleDelete2(item, val){
deleteQuestion(item.id).then(res=>{
console.log(res)
if (res.code ==204) {
this.$message({
type: "success",
message: "删除成功!"
});
this.questions.splice(val, 1);
}
}).catch(res=>{
console.log(res)
})
},
calScore() {
let danxuan_count = 0,
duoxuan_count = 0,

View File

@ -1,8 +1,8 @@
<template>
<div class="app-container">
<el-row>
<el-row :gutter="6">
<el-col :span="8">
<h3>基本信息</h3>
<el-card header="试卷信息">
<el-form :model="Form" :rules="rules" ref="Form" label-width="100px" status-icon>
<el-form-item label="名称" prop="name">
<el-input v-model="Form.name" style="width:80%"></el-input>
@ -42,38 +42,48 @@
<el-button type="warning" @click="goBack()">返回</el-button>
</el-form-item>
</el-form>
</el-card>
</el-col>
<el-col :span="16">
<h3>选题信息</h3>
<el-card header="试题信息">
<el-button type="primary" @click="handleChoose" icon="el-icon-plus">选择试题</el-button>
<div v-for="(item, index) in questions">
<h4>
<el-button
type="danger"
size="small"
@click="handleDelete(index)"
icon="el-icon-delete"
></el-button>
{{ index+1 }} -
<draggable v-model="questions">
<el-card style="margin-top: 4px" shadow="never" v-for="(item, index) in questions" v-bind:key="index">
<div slot="header" style="display: flex;">
<span>{{ index+1 }}</span>
<el-link style="margin-left: auto; margin-right: 8px" type="primary" @click="handleEdit(item, index)" icon="el-icon-edit"></el-link>
<el-link style="margin-right: 4px" type="danger" @click="handleDelete(index)" icon="el-icon-delete"></el-link>
</div>
<div style="font-weight: bold;">
<el-tag>{{item.type}}</el-tag>
{{ item.name }}
<span>(正确答案:{{item.right}})</span>
</h4>
<div v-for="(value, name) in item.options">{{ name }}: {{ value }}</div>
</div>
</div>
<div style="font-size: 14px;" v-for="(value, name) in item.options">{{ name }}: {{ value }}</div>
</el-card>
</draggable>
</el-card>
</el-col>
</el-row>
<Questionchoose v-bind:chooseVisible="chooseVisible" @closeDg="closeDg" @choseQ="choseQ"></Questionchoose>
<el-dialog title="编辑题目" :visible.sync="qDialog">
<QuestionUpdate v-if="qDialog" :qid="qid" @updateQuestion="updateQuestion"></QuestionUpdate>
</el-dialog>
</div>
</template>
<script>
import { getQuestioncatList, updatePaper,getPaperDetail } from "@/api/exam";
import { genTree } from "@/utils";
import Questionchoose from "@/views/exam/questionChoose";
import draggable from 'vuedraggable';
import QuestionUpdate from "./questionupdate.vue";
export default {
components: { Questionchoose },
components: { Questionchoose, draggable, QuestionUpdate },
data() {
return {
qDialog: false,
qid: null,
currentEditIndex: null,
questions: [],
Form: {
name: "",
@ -116,6 +126,16 @@
this.getPaperDetail();
},
methods: {
updateQuestion(data){
this.qDialog = false;
data["question"] = data["id"]
this.questions[this.currentEditIndex] = data;
},
handleEdit(item, index) {
this.qDialog = true;
this.qid = item.question;
this.currentEditIndex = index;
},
getQuestioncat() {
getQuestioncatList().then(response => {
this.workscopeData = genTree(response.data);

View File

@ -42,7 +42,7 @@
:value="item.key"
/>
</el-select>
<el-select
<!-- <el-select
v-model="listQuery.roles"
placeholder="角色"
style="width: 90px"
@ -55,7 +55,7 @@
:label="item.label"
:value="item.value"
/>
</el-select>
</el-select> -->
<el-input
v-model="listQuery.search"
placeholder="姓名/邮箱"
@ -115,16 +115,16 @@
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="close_dialog">取消</el-button>
<el-button type="primary" @click="submitUpload" :disabled="saveLoading">确认</el-button>
<el-button type="primary" @click="submitUpload">确认</el-button>
</div>
</el-dialog>
</el-card>
</div>
<div style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleAddUser"
>新增</el-button
>
</div>
<div style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleAddUser"
>新增</el-button>
</div>
<el-table
v-loading="listLoading"
:data="userList.results"
@ -147,7 +147,7 @@
scope.row.dept_name
}}</template>
</el-table-column>
<el-table-column align="header-center" label="角色">
<!-- <el-table-column align="header-center" label="角色">
<template slot-scope="scope">
<el-tag
style="margin: 2px"
@ -158,7 +158,7 @@
{{ item.name }}
</el-tag>
</template>
</el-table-column>
</el-table-column> -->
<el-table-column label="创建日期">
<template slot-scope="scope">
<span>{{ scope.row.date_joined }}</span>
@ -196,6 +196,7 @@
</template>
</el-table-column>
</el-table>
<pagination
v-show="userList.count > 0"
:total="userList.count"
@ -206,6 +207,66 @@
</el-card>
</el-col>
</el-row>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑用户' : '新增用户'"
>
<el-form
ref="Form"
:model="user"
label-width="80px"
label-position="right"
:rules="rule1"
>
<el-form-item label="姓名" prop="name">
<el-input v-model="user.name" placeholder="姓名" />
</el-form-item>
<el-form-item label="账户" prop="username">
<el-input v-model="user.username" placeholder="账户" />
</el-form-item>
<el-form-item label="所属部门" prop="dept">
<treeselect
v-model="user.dept"
:multiple="false"
:options="orgData"
placeholder="所属部门"
/>
</el-form-item>
<!-- <el-form-item label="角色" prop="roles">
<el-select
v-model="user.roles"
multiple
placeholder="请选择"
style="width: 100%"
>
<el-option
v-for="item in roles"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> -->
<el-form-item label="头像" prop="dept">
<el-upload
class="avatar-uploader"
:action="upUrl"
accept="image/jpeg, image/gif, image/png, image/bmp"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:headers="upHeaders"
>
<img v-if="user.avatar" :src="user.avatar" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirm('Form')">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<style>
@ -266,9 +327,12 @@ export default {
upHeaders: upHeaders(),
upUrl: upUrl(),
userList: { count: 0 },
roles: [],
// roles: [],
saveLoading:false,
fileList: [],
impForm:{
file:''
},
impDialogVisible: false,
listLoading: true,
downloadUrl: process.env.VUE_APP_BASE_API,
@ -300,7 +364,7 @@ export default {
{ header: '姓名', key: 'name', wpx: 60 },
{ header: '账户', key: 'username', wch: 32 },
{ header: '部门', key: 'dept_name', width: 30 },
{ header: '角色', key: 'roles', width: 70 }
// { header: '角色', key: 'roles', width: 70 }
],
};
},
@ -353,11 +417,9 @@ export default {
this.impForm.file = '';
},
handleUpSuccess(res, file, filelist) {
this.Content.file = res.data.id;
this.impForm.file = res.data.path;
},
handleRemove(file, filelist) {
this.Content.file = null;
this.impForm.file = '';
},
handlePreview(file) {
@ -438,12 +500,16 @@ export default {
type: "error",
})
.then(async () => {
await deleteUserExam(scope.row.id);
this.userList.splice(scope.row.index, 1);
this.$message.success("成功");
deleteUserExam(scope.row.id).then((res) => {
console.log(res);
if (res.code >= 200) {
// this.userList.splice(scope.row.index, 1);
this.$message.success("成功");
this.getList();
};
});
})
.catch((err) => {
});
},
async confirm(form) {
@ -454,9 +520,11 @@ export default {
updateUser(this.user.id, this.user).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.dialogVisible = true;
this.impForm.file = ''
this.$message.success("成功");
}
};
this.impForm.file = ''
});
} else {
createUser(this.user).then((res) => {

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-06-04 09:28
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('exam', '0001_initial'),
('edu', '0006_certificate_examrecord'),
]
operations = [
migrations.AlterField(
model_name='certificate',
name='examrecord',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cert_er', to='exam.examrecord'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-06-06 01:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('edu', '0007_alter_certificate_examrecord'),
]
operations = [
migrations.AddField(
model_name='certificate',
name='用户ID',
field=models.IntegerField(blank=True, max_length=32, null=True),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.12 on 2024-06-06 02:26
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('edu', '0008_certificate_用户id'),
]
operations = [
migrations.AlterField(
model_name='certificate',
name='用户ID',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -26,7 +26,8 @@ class Certificate(CommonADModel):
是否需要北京标研培训合格 = models.BooleanField(default=False)
课程列表 = models.ManyToManyField(Course, blank=True)
培训日期 = models.DateField(null=True, blank=True)
培训结束日期 = models.DateField(null=True, blank=True)
培训结束日期 = models.DateField(null=True, blank=True)
发证日期 = models.DateField(null=True, blank=True)
证书地址 = models.CharField(max_length=100, null=True, blank=True)
examrecord = models.ForeignKey('exam.ExamRecord', on_delete=models.CASCADE, null=True, blank=True)
用户ID = models.ForeignKey(null=True, blank=True, to='system.User', on_delete=models.CASCADE)
examrecord = models.OneToOneField('exam.ExamRecord', on_delete=models.CASCADE, null=True, blank=True, related_name='cert_er')

View File

@ -5,7 +5,7 @@ from openpyxl.styles import Font, Fill
import json
import os
def export_question(questions):
def export_question(questions:object):
'''
params: serializer questions
return: xlsx path
@ -24,4 +24,33 @@ def export_question(questions):
if not os.path.exists(full_path):
os.makedirs(full_path)
wb.save(full_path+filename)
return path + filename
return path + filename
def export_record(records:object):
'''
params: serializer records
return: xlsx path
'''
wb = Workbook()
ws1 = wb.active
ws1.title = '答题记录表'
ws1.append(['类型', '用户', '是否通过', '得分', '总分', '耗时(时分秒)', '答题时间', '部门'])
row = ws1.row_dimensions[1]
row.font = Font(bold=True)
for i in records:
tookformat = get_took_format(i.took)
ws1.append([i.type, i.create_by.name, i.is_pass, i.score, i.total_score, str(tookformat), str(i.start_time), i.belong_dept.name])
filename = 'records' + datetime.now().strftime("%Y%m%d%H%M%S") +'.xlsx'
path = '/media/temp/'
full_path = settings.BASE_DIR + '/media/temp/'
if not os.path.exists(full_path):
os.makedirs(full_path)
wb.save(full_path+filename)
return path + filename
def get_took_format(took:int):
m, s = divmod(took, 60)
h, m = divmod(m, 60)
return "%02d:%02d:%02d" % (h, m, s)

View File

@ -1,6 +1,9 @@
from django_filters import rest_framework as filters
from django.utils import timezone
from .models import ExamRecord, Exam
from django.db.models import Q
class ExamRecordFilter(filters.FilterSet):
is_my = filters.BooleanFilter(method='filter_is_my')
class Meta:
model = ExamRecord
fields = {
@ -8,9 +11,17 @@ class ExamRecordFilter(filters.FilterSet):
'is_pass': ['exact'],
'type': ['exact'],
'is_submited': ['exact'],
'exam': ['exact']
}
def filter_is_my(self, queryset, name, value):
if value:
user = self.request.user
return queryset.filter(create_by=user)
return queryset
class ExamFilter(filters.FilterSet):
can_attend = filters.BooleanFilter(method='filter_can_attend')
is_my = filters.BooleanFilter(method='filter_is_my')
class Meta:
model = Exam
fields = {
@ -18,4 +29,17 @@ class ExamFilter(filters.FilterSet):
'paper': ['exact'],
'code': ['exact'],
'is_open': ['exact']
}
}
def filter_can_attend(self, queryset, name, value):
if value:
now = timezone.now()
return queryset.filter(open_time__lte=now, close_time__gte=now)| queryset.filter(close_time__isnull=True)
return queryset
def filter_is_my(self, queryset, name, value):
if value:
user = self.request.user
return queryset.filter(Q(participant_user=user)|Q(participant_dep=user.dept)|Q(is_open=True))
return queryset

View File

@ -1,5 +1,5 @@
from django.db import models
from apps.system.models import CommonAModel, CommonBModel
from apps.system.models import CommonAModel, CommonADModel, CommonBDModel
from django.contrib.postgres.fields import JSONField
from utils.model import BaseModel
from apps.edu.models import Course
@ -20,7 +20,7 @@ class Questioncat(CommonAModel):
return self.questioncat.count()
class Question(CommonAModel):
class Question(CommonADModel):
type_choices = (
('单选', '单选'),
('多选', '多选'),
@ -99,7 +99,7 @@ class Exam(CommonAModel):
return self.name
class ExamRecord(CommonBModel):
class ExamRecord(CommonBDModel):
'''
考试记录表
'''

View File

@ -94,6 +94,6 @@ def interpret_text(start:int, excel_path:str, doc_path:str, field=None):
if __name__ == '__main__':
doc_path = "C:\code\data\\test.docx"
doc_path = "C:\code\data\\判断题.docx"
excel_path = "C:\code\data\question.xlsx"
interpret_text(3, excel_path, doc_path)

View File

@ -88,6 +88,7 @@ class ExamListSerializer(ModelSerializer):
course_ = CourseSerializer(source='course_name', read_only=True)
user_count = serializers.SerializerMethodField()
submit_count = serializers.SerializerMethodField()
limit = CharField(source='paper.limit', read_only=True)
class Meta:
model = Exam
fields = '__all__'
@ -132,6 +133,8 @@ class ExamRecordListSerializer(serializers.ModelSerializer):
source='exam.name', read_only=True)
blong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
cert_number = serializers.CharField(source='cert_er.证书编号', read_only=True)
cert_path = serializers.CharField(source='cert_er.证书地址', read_only=True)
class Meta:
model = ExamRecord
exclude = ('detail',)

View File

@ -1,7 +1,7 @@
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import ListModelMixin, DestroyModelMixin, RetrieveModelMixin
from apps.exam.exports import export_question
from apps.exam.exports import export_question, export_record
from apps.exam.models import Question, Questioncat, PaperQuestion
from apps.exam.serializers import (QuestionSerializer, QuestioncatSerializer, PaperSerializer, ExamDetailSerializer, ExamRecordDetailSerializer, ExamListSerializer,
ExamCreateUpdateSerializer, ExamListSerializer, ExamRecordSubmitSerializer, PaperDetailSerializer, PaperCreateUpdateSerializer, AnswerDetailOutSerializer, ExamRecordListSerializer)
@ -11,7 +11,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.exceptions import ParseError
from openpyxl import Workbook, load_workbook
from django.conf import settings
from apps.exam.models import Paper, Exam, ExamRecord, AnswerDetail
from apps.exam.models import Paper, Exam, ExamRecord, AnswerDetail, PaperQuestion
from django.utils import timezone
from django.db import transaction
from rest_framework.serializers import Serializer
@ -20,11 +20,13 @@ from apps.exam.filters import ExamRecordFilter, ExamFilter
from datetime import timedelta
from apps.system.mixins import CreateUpdateCustomMixin
from apps.edu.serializers import CertificateSerializer
from apps.edu.models import Certificate
from utils.queryset import get_child_queryset2
from apps.system.permission import has_permission
from apps.exam.parse_word import interpret_text
import os
import shutil
from django.db.models import Q
# Create your views here.
EXCEL_PATH = os.path.join(settings.BASE_DIR, "media/default/question.xlsx")
@ -63,22 +65,21 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post':'question', 'put':'question', 'delete':'question'}
queryset = Question.objects.all()
serializer_class = QuestionSerializer
filterset_fields = ['level', 'type', 'year']
filterset_fields = ['level', 'type', 'year', 'questioncat']
search_fields = ['name', 'options', 'resolution']
def destroy(self, request, *args, **kwargs):
"""
删除题目
"""
id = kwargs.get('pk', None)
# 删除前进行校验,如果存在考试中不允许删除。
paperquestion = PaperQuestion.objects.filter(question_id=id).exists()
if paperquestion:
raise ParseError('此试题存在考试中不允许删除')
Question.objects.filter(id=id).delete()
return Response("删除成功")
@action(methods=['get'], detail=False,
url_path='export', url_name='export_question', perms_map=[{'get': '*'}], serializer_class=Serializer)
def export_question(self, request):
"""
导出题目
导出题目
"""
queryset = self.filter_queryset(self.get_queryset())
path = export_question(queryset)
return Response({'path': path})
@action(methods=['post'], detail=False, url_name='enable_question', perms_map={'post': 'question'}, serializer_class=Serializer)
def enable(self, request):
"""
@ -90,6 +91,30 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
if ids:
Question.objects.filter(pk__in=ids).update(enabled=True)
return Response(status=200)
@action(methods=['post'], detail=False, perms_map={'*':'question_delete'})
def deletes(self, request):
"""
批量删除
"""
ids = request.data.get('ids', [])
if request.user.is_superuser:
# 删除前进行校验,如果存在考试中不允许删除。
paperquestion = PaperQuestion.objects.filter(question__id__in=ids).exists()
if paperquestion:
return Response('此试题存在考试中不允许删除')
Question.objects.filter(id__in=ids).update(is_deleted=True)
return Response()
return Response({'msg':'权限不足'},status=401)
@action(methods=['get'], detail=False, perms_map={'get':'export_question'})
def export(self, request):
"""
导出题目
"""
queryset = self.filter_queryset(self.get_queryset())
path = export_question(queryset)
return Response({'path': path})
@action(methods=['post'], detail=False,
url_path='import', url_name='import_question', perms_map={'post': 'question'}, serializer_class=Serializer)
@ -269,6 +294,34 @@ class PaperViewSet(ModelViewSet):
PaperQuestion.objects.filter(paper=paper).delete()
PaperQuestion.objects.bulk_create(q_list)
return Response()
@action(methods=['put'], detail=True, url_path='clone', url_name='clone_paper', perms_map={'put':'clone_paper'})
def clone(self, request, pk=None):
'''
克隆试卷
'''
paper = self.get_object()
obj = Paper()
obj.name = '克隆卷-'+paper.name
obj.workscope = paper.workscope
obj.limit = paper.limit
obj.total_score = paper.total_score
obj.pass_score = paper.pass_score
obj.danxuan_count = paper.danxuan_count
obj.danxuan_score = paper.danxuan_score
obj.duoxuan_count = paper.duoxuan_count
obj.duoxuan_score = paper.duoxuan_score
obj.panduan_count = paper.panduan_count
obj.panduan_score = paper.panduan_score
obj.save()
for i in PaperQuestion.objects.filter(paper=paper):
o = PaperQuestion()
o.paper = obj
o.question = i.question
o.total_score = i.total_score
o.save()
return Response(status=200)
@action(methods=['post'], detail=False, perms_map={'post': 'question'}, serializer_class=Serializer)
def upload_paper(self, request):
@ -276,7 +329,6 @@ class PaperViewSet(ModelViewSet):
question_type = request.data.get('question_type')
excel_path = settings.BASE_DIR + "/media/default/question.xlsx"
doc_path = settings.BASE_DIR + doc_path
# excel_path = "C:\code\data\question.xlsx"
timenow = timezone.now().strftime('%Y%m%d%H%M%S')
question_excel_name = "question_excel_"+timenow
question_excel = os.path.join(os.path.dirname(excel_path), question_excel_name)
@ -425,15 +477,23 @@ class ExamViewSet(CreateUpdateCustomMixin, ModelViewSet):
elif self.action in ['retrieve']:
return ExamDetailSerializer
return super().get_serializer_class()
def get_queryset(self):
qs = super().get_queryset()
user = self.request.user
if has_permission("exam", user):
return qs
return qs.filter(Q(participant_user=user)|Q(participant_dep=user.dept)|Q(is_open=True))
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if ExamRecord.objects.filter(exam=instance).exists():
raise ParseError('存在考试记录,禁止删除')
instance.delete(soft=False)
instance.delete()
return Response(status=204)
@action(methods=['post'], detail=True, perms_map=[{'post': '*'}], serializer_class=Serializer, permission_classes = [IsAuthenticated])
@action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=Serializer, permission_classes = [IsAuthenticated])
@transaction.atomic
def start(self, request, *args, **kwargs):
"""
@ -441,7 +501,8 @@ class ExamViewSet(CreateUpdateCustomMixin, ModelViewSet):
开始考试具体题目信息
"""
exam = self.get_object()
# exam = self.get_object()
exam = Exam.objects.get(id=kwargs['pk'])
# 查询本次考试对应哪些人
participants = exam.participant_user.all()
participants_ids = [i.id for i in participants]
@ -489,7 +550,7 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
考试记录列表和详情
"""
perms_map = {'get': '*', 'post': '*', 'delete':'examrecord'}
queryset = ExamRecord.objects.select_related('create_by')
queryset = ExamRecord.objects.select_related('create_by', 'cert_er')
serializer_class = ExamRecordListSerializer
ordering_fields = ['create_time', 'score', 'took', 'update_time', 'belong_dept']
ordering = ['-update_time']
@ -505,24 +566,25 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
if has_permission('ctc_manager', self.request.user):
return qs
# 如果是部门管理员,只能看到自己部门下的考试记录
# return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept))
elif has_permission('exam_manager', self.request.user):
return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept))
# 如果是普通员工,只能看到自己考试记录
else:
return qs.filter(create_by=self.request.user)
def get_serializer_class(self):
if self.action == 'retrieve':
return ExamRecordDetailSerializer
now = timezone.now()
if now > self.get_object().exam.close_time:
return ExamRecordDetailSerializer
return super().get_serializer_class()
def perform_destroy(self, instance): # 考试记录物理删除
instance.delete(soft=False)
instance.delete()
@action(methods=['post'], detail=False, perms_map=[{'post': '*'}], serializer_class=Serializer, permission_classes = [IsAuthenticated])
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=Serializer, permission_classes = [IsAuthenticated])
def clear(self, request, pk=None):
"""
清除七日前未提交的考试记录
清除七日前未提交的考试记录
"""
now = timezone.now
@ -530,19 +592,19 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
ExamRecord.objects.filter(create_time__lte=days7_ago, is_submited=False).delete(soft=False)
return Response(status=False)
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
def self(self, request, pk=None):
'''
个人考试记录
个人考试记录
'''
queryset = ExamRecord.objects.filter(create_by=request.user).order_by('-update_time')
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
@action(methods=['get'], detail=False,
url_path='export', url_name='export_record', perms_map={'*': 'export_ansrecord'}, serializer_class=Serializer)
def export(self, request):
"""
导出答题记录
"""
queryset = self.filter_queryset(self.get_queryset())
path = export_record(queryset)
return Response({'path': path})
@action(methods=['post'], detail=True, perms_map=[{'post': '*'}], serializer_class=ExamRecordSubmitSerializer, permission_classes = [IsAuthenticated])
@action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=ExamRecordSubmitSerializer, permission_classes = [IsAuthenticated])
@transaction.atomic
def submit(self, request, pk=None):
'''
@ -594,13 +656,23 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
now_data = datetime.now()
course = exam.course_name.all()
courese_ids = [i.id for i in course]
current_date = now_data.strftime('%Y-%m-%d')
current_date = now_data.strftime('%Y-%m-%d')
cer_number = now_data.strftime('%Y%m%d')
# 查询证明编号创建时间为最后一个
cer = Certificate.objects.latest('证书编号')
if cer:
cer_number = int(cer.证书编号[5:]) + 1
cer_number = 'CTCZL' + str(cer_number)
data_dict = {
'姓名': request.user.name,
'证书编号': 'CTCZL'+ current_date,
'用户ID': request.user.id,
'证书编号': cer_number,
'证书方案': '202312',
'单位名称': request.user.dept.name,
'所属单位': '国检测试控股集团'+request.user.dept.name,
'发证日期': current_date,
'培训日期':current_date,
'培训结束日期': current_date,
'课程列表': courese_ids,
'examrecord': er.id,
}
@ -610,7 +682,7 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
er.took = (now - er.create_time).total_seconds()
er.end_time = now
er.belong_dept=request.user.dept.id
er.belong_dept=request.user.dept
er.is_submited = True
er.save()
return Response(ExamRecordListSerializer(instance=er).data)

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-06-04 09:28
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0047_alter_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 6, 4, 9, 28, 52, 579869, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-06-06 01:44
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0048_alter_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 6, 6, 1, 44, 19, 206626, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-06-06 02:26
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0049_alter_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 6, 6, 2, 26, 43, 23224, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-06-14 05:35
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0050_alter_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 6, 14, 5, 35, 33, 230280, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-06-14 06:51
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0051_alter_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 6, 14, 6, 51, 53, 747831, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

@ -100,8 +100,6 @@ class ImpMixin:
if 'file' not in request.data:
raise ParseError('请提供文件')
path = request.data['file']
print(path, "---------ssss")
if not str(path).endswith('.xlsx'):
raise ParseError('请提供xlsx格式文件')
fullpath = settings.BASE_DIR + str(path)
@ -111,7 +109,7 @@ class ImpMixin:
data_list = self.build_data(sheet, start)
serializer = mySerializer(data=data_list, many=True, context={'request': request})
if serializer.is_valid():
serializer.save(create_by=request.user, belong_dept=request.user.dept)
serializer.save()
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@ -335,6 +333,8 @@ class UserExamViewset(ImpMixin, ModelViewSet):
else:
# password = make_password(''.join(random.sample(string.ascii_letters + string.digits, 8)))
password = make_password('0000')
exam_role = Role.objects.get(name='考试')
request.data['roles'] = [exam_role.id]
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(password=password)
@ -347,12 +347,14 @@ class UserExamViewset(ImpMixin, ModelViewSet):
return Response({'msg': '考试角色不存在'})
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[0] is not None:
dept = Organization.objects.get(name=row[3])
if not dept:
return Response({'msg': '部门不存在'})
dept = Organization.objects.get(name=row[2])
user_depts = get_child_queryset2(self.request.user.dept).order_by('sort')
depts = any(i.name==dept.name for i in user_depts)
if depts is False:
return Response({'msg': f'本公司下不存在此部门{row[2]}'})
serializer_data = {
'name': row[1],
'username':row[2],
'name': row[0],
'username':row[1],
'dept':dept.id,
'roles':[exam_role.id],
'avatar': "/media/default/avatar.png"
@ -510,6 +512,7 @@ class UserViewSet(PageOrNot, ModelViewSet):
while sheet['b'+str(i)].value:
name = sheet['b'+str(i)].value
email = sheet['e'+str(i)].value
dept = Organization.objects.get(name=sheet['j'+str(i)].value)
if not User.objects.filter(username=email).exists():
user = User.objects.create(name=name,
username=email,
@ -517,7 +520,7 @@ class UserViewSet(PageOrNot, ModelViewSet):
dept=dept)
else:
user = User.objects.get(username=email)
dept = Organization.objects.get(name=sheet['j'+str(i)].value)
if sheet['f'+str(i)].value:
user.roles.add(role1)
if sheet['g'+str(i)].value:

View File

@ -28,6 +28,4 @@ DATABASES = {
# 'HOST':'1.203.161.101',
'PORT': '5432',
}
}