:feat:新增 考试人员导出功能
This commit is contained in:
parent
9222e2ecdf
commit
8e6e0d4c93
File diff suppressed because one or more lines are too long
|
@ -27,7 +27,7 @@ export function getQi(id) {
|
|||
|
||||
export function updateQi(id, data) {
|
||||
return request({
|
||||
url: `/info/faq/${id}/`,
|
||||
url: `/info/faqch/${id}/`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
|
|
|
@ -14,6 +14,14 @@ export function getRoleAll() {
|
|||
})
|
||||
}
|
||||
|
||||
export function getRoleList(params) {
|
||||
return request({
|
||||
url: '/system/role/',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function createRole(data) {
|
||||
return request({
|
||||
url: '/system/role/',
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
|
||||
export function getUserList(query) {
|
||||
return request({
|
||||
url: '/system/userexam/',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function createUser(data) {
|
||||
return request({
|
||||
url: '/system/userexam/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUser(id, data) {
|
||||
return request({
|
||||
url: `/system/userexam/${id}/`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function deleteUserExam(id, data) {
|
||||
return request({
|
||||
url: `/system/userexam/${id}/`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function resetUserpw(id) {
|
||||
return request({
|
||||
url: `/system/user/${id}/resetpw/`,
|
||||
method: 'put',
|
||||
})
|
||||
}
|
||||
|
||||
export function changePassword(data) {
|
||||
return request({
|
||||
url: '/system/user/password/',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
|
@ -559,20 +559,20 @@ export const asyncRoutes = [
|
|||
component: Layout,
|
||||
redirect: '/exam/questions',
|
||||
name: 'exam',
|
||||
meta: { title: '考试', icon: 'PT', perms: ['pt_view'] },
|
||||
meta: { title: '考试', icon: 'Exam', perms: ['Exam'] },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'classify',
|
||||
name: '题目分类',
|
||||
component: () => import('@/views/exam/classify.vue'),
|
||||
meta: { title: '题目分类', perms: ['pt_view'] }
|
||||
meta: { title: '题目分类', perms: ['CateQues'] }
|
||||
},
|
||||
{
|
||||
path: 'questions',
|
||||
name: '题目列表',
|
||||
component: () => import('@/views/exam/questions.vue'),
|
||||
meta: { title: '题目列表', perms: ['pt_view'] }
|
||||
meta: { title: '题目列表', perms: ['CateQues'] }
|
||||
},
|
||||
{
|
||||
path: 'questionCreate',
|
||||
|
@ -592,7 +592,7 @@ export const asyncRoutes = [
|
|||
path: 'testPaper',
|
||||
name: '考试试卷',
|
||||
component: () => import('@/views/exam/testPaper.vue'),
|
||||
meta: { title: '考试试卷', perms: ['pt_view'] }
|
||||
meta: { title: '考试试卷', perms: ['Paper'] }
|
||||
},
|
||||
{
|
||||
path: 'paperCreate',
|
||||
|
@ -612,13 +612,19 @@ export const asyncRoutes = [
|
|||
path: 'index',
|
||||
name: '考试',
|
||||
component: () => import('@/views/exam/index.vue'),
|
||||
meta: { title: '考试', perms: ['pt_view'] }
|
||||
meta: { title: '考试', perms: ['Paper'] }
|
||||
},
|
||||
{
|
||||
path: 'record',
|
||||
name: '考试记录',
|
||||
component: () => import('@/views/exam/examRecord.vue'),
|
||||
meta: { title: '考试记录', perms: ['pt_view'] }
|
||||
meta: { title: '考试记录', perms: ['RecordExam'] }
|
||||
},
|
||||
{
|
||||
path: 'userExam',
|
||||
name: '用户考试管理',
|
||||
component: () => import('@/views/system/userExam.vue'),
|
||||
meta: { title: '用户考试管理', perms: ['UserExam'] }
|
||||
},
|
||||
]
|
||||
},
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
<el-table-column label="变更日期" prop="change_date"></el-table-column>
|
||||
<el-table-column align="center" label="操作" width="120px" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-link :disabled="!checkPermission(['infoCollect_QIN'])" type="primary" size="small"
|
||||
<el-link type="primary" size="small"
|
||||
@click="handleEdit(scope)">编辑</el-link>
|
||||
<el-divider direction="vertical"
|
||||
v-if="checkPermission(['infoCollect_QIN']) && checkPermission(['infoCollect_QIN'])"></el-divider>
|
||||
<el-link :disabled="!checkPermission(['infoCollect_QIN'])" type="danger" size="small"
|
||||
></el-divider>
|
||||
<el-link type="danger" size="small"
|
||||
@click="handleDelete(scope)">删除</el-link>
|
||||
<!-- <el-link type="primary" @click="handleChange(scope)">变更记录</el-link> -->
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="6">
|
||||
<el-col :xs="24" :md="6">
|
||||
<el-card >
|
||||
<div slot="header" class="clearfix">
|
||||
<span>部门</span>
|
||||
</div>
|
||||
<el-input v-model="filterOrgText" placeholder="输入部门名进行过滤" />
|
||||
<el-tree
|
||||
ref="tree"
|
||||
v-loading="treeLoding"
|
||||
class="filter-tree"
|
||||
:data="orgData"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
style="margin-top: 10px;max-height:650px;overflow-y: auto;"
|
||||
@node-click="handleOrgClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="18">
|
||||
<el-card>
|
||||
<div slot="header" class="clearfix">
|
||||
<span>用户</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-select
|
||||
v-model="listQuery.is_active"
|
||||
placeholder="状态"
|
||||
clearable
|
||||
style="width: 90px"
|
||||
class="filter-item"
|
||||
@change="handleFilter"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in enabledOptions"
|
||||
:key="item.key"
|
||||
:label="item.display_name"
|
||||
:value="item.key"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="listQuery.roles"
|
||||
placeholder="角色"
|
||||
style="width: 90px"
|
||||
class="filter-item"
|
||||
@change="handleFilter"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<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-search"
|
||||
@click="handleFilter"
|
||||
>搜索</el-button>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
style="margin-left: 10px"
|
||||
type="primary"
|
||||
icon="el-icon-refresh-left"
|
||||
@click="resetFilter"
|
||||
>重置</el-button>
|
||||
<!-- <el-button
|
||||
class="filter-item"
|
||||
style="margin-left: 10px"
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
@click="exportExcel"
|
||||
:loading="listLoading"
|
||||
>导出</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"
|
||||
style="width: 100%; margin-top: 10px"
|
||||
border
|
||||
fit
|
||||
stripe
|
||||
highlight-current-row
|
||||
max-height="600"
|
||||
>
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column align="center" label="姓名">
|
||||
<template slot-scope="scope">{{ scope.row.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="header-center" label="账户">
|
||||
<template slot-scope="scope">{{ scope.row.username }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="header-center" label="部门">
|
||||
<template v-if="scope.row.dept_name != null" slot-scope="scope">{{
|
||||
scope.row.dept_name
|
||||
}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="header-center" label="角色">
|
||||
<template slot-scope="scope">
|
||||
<el-tag
|
||||
style="margin: 2px"
|
||||
effect="plain"
|
||||
v-for="(item, index) in scope.row.roles_"
|
||||
:key="index"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建日期">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.date_joined }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
width="200px"
|
||||
fixed="right"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="checkPermission(['admin'])"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handeReset(scope)"
|
||||
>重置密码</el-button
|
||||
>
|
||||
<el-button
|
||||
:disabled="!checkPermission(['user_exam_create'])"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
@click="handleEdit(scope)"
|
||||
/>
|
||||
<el-button
|
||||
v-if="!scope.row.is_superuser"
|
||||
:disabled="!checkPermission(['user_exam_delete'])"
|
||||
type="danger"
|
||||
size="small"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="userList.count > 0"
|
||||
:total="userList.count"
|
||||
:page.sync="listQuery.page"
|
||||
:limit.sync="listQuery.page_size"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</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>
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import {
|
||||
getUserList,
|
||||
createUser,
|
||||
deleteUserExam,
|
||||
updateUser,
|
||||
resetUserpw,
|
||||
} from "@/api/userexam";
|
||||
import { getSubOrgList } from "@/api/org";
|
||||
import { getRoleList } from "@/api/role";
|
||||
import { genTree } from "@/utils";
|
||||
import checkPermission from "@/utils/permission";
|
||||
import { upUrl, upHeaders } from "@/api/file";
|
||||
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import {generateExcel} from "@/utils/exportExcel.js";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
const defaultUser = {
|
||||
id: "",
|
||||
name: "",
|
||||
username: "",
|
||||
dept: null,
|
||||
avatar: "/media/default/avatar.png",
|
||||
};
|
||||
export default {
|
||||
components: { Pagination, Treeselect },
|
||||
data() {
|
||||
return {
|
||||
user: defaultUser,
|
||||
upHeaders: upHeaders(),
|
||||
upUrl: upUrl(),
|
||||
userList: { count: 0 },
|
||||
roles: [],
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
},
|
||||
enabledOptions: [
|
||||
{ key: "true", display_name: "激活" },
|
||||
{ key: "false", display_name: "禁用" },
|
||||
],
|
||||
dialogVisible: false,
|
||||
dialogType: "new",
|
||||
rule1: {
|
||||
name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
username: [
|
||||
{ required: true, message: "请输入账号", trigger: "change" },
|
||||
],
|
||||
roles: [{ required: true, message: "请选择角色", trigger: "change" }],
|
||||
// password: [
|
||||
// { required: true, message: '请输入密码', trigger: 'change' }
|
||||
// ],
|
||||
},
|
||||
filterOrgText: "",
|
||||
treeLoding: false,
|
||||
orgData: [],
|
||||
params : {name:'考试'},
|
||||
columns : [
|
||||
{ header: '姓名', key: 'name', wpx: 60 },
|
||||
{ header: '账户', key: 'username', wch: 32 },
|
||||
{ header: '部门', key: 'dept_name', width: 30 },
|
||||
{ header: '角色', key: 'roles', width: 70 }
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
filterOrgText(val) {
|
||||
this.$refs.tree.filter(val);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getSubOrgList();
|
||||
this.getRoleAll();
|
||||
},
|
||||
methods: {
|
||||
checkPermission,
|
||||
handleAvatarSuccess(res, file) {
|
||||
if (res.code >= 200) {
|
||||
this.user.avatar = res.data.path;
|
||||
} else {
|
||||
this.$message.error("头像上传失败!");
|
||||
}
|
||||
},
|
||||
beforeAvatarUpload(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt2M) {
|
||||
this.$message.error("上传头像图片大小不能超过 2MB!");
|
||||
}
|
||||
return isLt2M;
|
||||
},
|
||||
filterNode(value, data) {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
},
|
||||
handleOrgClick(obj, node, vue) {
|
||||
this.listQuery.page = 1;
|
||||
this.listQuery.dept = obj.id;
|
||||
this.getList();
|
||||
},
|
||||
getList() {
|
||||
this.listLoading = true;
|
||||
getUserList(this.listQuery).then((response) => {
|
||||
if (response.data) {
|
||||
this.userList = response.data;
|
||||
}
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
getSubOrgList() {
|
||||
this.treeLoding = true;
|
||||
getSubOrgList().then((response) => {
|
||||
this.orgData = genTree(response.data);
|
||||
this.treeLoding = false;
|
||||
});
|
||||
},
|
||||
getRoleAll() {
|
||||
getRoleList(this.params).then((response) => {
|
||||
this.roles = genTree(response.data);
|
||||
});
|
||||
},
|
||||
resetFilter() {
|
||||
this.listQuery = {
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
};
|
||||
this.getList();
|
||||
},
|
||||
handleFilter() {
|
||||
this.listQuery.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleAddUser() {
|
||||
this.user = Object.assign({}, defaultUser);
|
||||
this.dialogType = "new";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
handleEdit(scope) {
|
||||
this.user = Object.assign({}, scope.row); // copy obj
|
||||
this.dialogType = "edit";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
handleDelete(scope) {
|
||||
this.$confirm("确认删除?", "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "error",
|
||||
})
|
||||
.then(async () => {
|
||||
await deleteUserExam(scope.row.id);
|
||||
this.userList.splice(scope.row.index, 1);
|
||||
this.$message.success("成功");
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
});
|
||||
},
|
||||
async confirm(form) {
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (valid) {
|
||||
const isEdit = this.dialogType === "edit";
|
||||
if (isEdit) {
|
||||
updateUser(this.user.id, this.user).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.$message.success("成功");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
createUser(this.user).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.$message.success("成功");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
handeReset(scope) {
|
||||
this.$confirm("确认重置密码为0000吗?", "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
resetUserpw(scope.row.id)
|
||||
.then((res) => {
|
||||
this.$message.success('成功');
|
||||
})
|
||||
.catch((e) => {});
|
||||
});
|
||||
},
|
||||
//导出
|
||||
exportExcel(){
|
||||
let that = this;
|
||||
this.listLoading = true;
|
||||
//获取全部人员数据
|
||||
let promises = [1,2, 3, 4].map(function (page) {
|
||||
return getUserList({page:page,page_size:500}).then((res)=>{return res.data.results})
|
||||
});
|
||||
Promise.all(promises).then(function (posts) {
|
||||
let data = JSON.parse(JSON.stringify(posts))
|
||||
let list = [...data[0], ...data[1],...data[2], ...data[3]];
|
||||
let exportData = [];
|
||||
for(let i=0;i<list.length;i++){
|
||||
let obj = {};
|
||||
obj.name=list[i].name;
|
||||
obj.username=list[i].username;
|
||||
obj.dept_name=list[i].dept_name;
|
||||
let rolesList = list[i].roles_;
|
||||
let roles = rolesList.map((item)=>{
|
||||
return item.name;
|
||||
})
|
||||
obj.roles=roles.toString();
|
||||
exportData.push(obj)
|
||||
}
|
||||
generateExcel(that.columns,exportData,'账号清单');
|
||||
that.listLoading = false;
|
||||
}).catch(function(reason){
|
||||
console.log('出错了',reason)
|
||||
that.listLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -20,6 +20,8 @@ from apps.exam.filters import ExamRecordFilter, ExamFilter
|
|||
from datetime import timedelta
|
||||
from apps.system.mixins import CreateUpdateCustomMixin
|
||||
from apps.edu.serializers import CertificateSerializer
|
||||
from utils.queryset import get_child_queryset2
|
||||
from apps.system.permission import has_permission
|
||||
from datetime import datetime
|
||||
# Create your views here.
|
||||
|
||||
|
@ -354,7 +356,15 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
|
|||
search_fields = ('create_by__name', 'create_by__username', 'exam__name', 'belong_dept__name')
|
||||
filterset_class = ExamRecordFilter
|
||||
|
||||
# 排序
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
# if self.request.method == 'GET':
|
||||
# return qs
|
||||
# else:
|
||||
# return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept))
|
||||
if has_permission('ability_review_jygl', self.request.user):
|
||||
return qs
|
||||
return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept))
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
|
@ -397,7 +407,6 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
|
|||
提交答卷
|
||||
'''
|
||||
er = self.get_object()
|
||||
print('er----------------', er)
|
||||
now = timezone.now()
|
||||
if er.create_by != request.user:
|
||||
raise ParseError('提交人有误')
|
||||
|
|
|
@ -95,6 +95,7 @@ class UserListSerializer(serializers.ModelSerializer):
|
|||
queryset = queryset.prefetch_related('roles',)
|
||||
return queryset
|
||||
|
||||
|
||||
class UserModifySerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
用户编辑序列化
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from django.urls import path, include
|
||||
|
||||
from .views import CityViewSet, ProviceViewSet, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, InitAreaView, sendMsg
|
||||
from .views import CityViewSet, ProviceViewSet, UserExamViewset, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, InitAreaView, sendMsg
|
||||
from rest_framework import routers
|
||||
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('user', UserViewSet, basename="user")
|
||||
router.register('userexam', UserExamViewset, basename="userexam")
|
||||
router.register('organization', OrganizationViewSet, basename="organization")
|
||||
router.register('permission', PermissionViewSet, basename="permission")
|
||||
router.register('role', RoleViewSet, basename="role")
|
||||
|
|
|
@ -45,26 +45,84 @@ import requests
|
|||
import json
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
from django.db import transaction
|
||||
logger = logging.getLogger('log')
|
||||
# logger.info('请求成功! response_code:{};response_headers:{};response_body:{}'.format(response_code, response_headers, response_body[:251]))
|
||||
# logger.error('请求出错:{}'.format(error))
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
permission_classes = []
|
||||
|
||||
def get(self, request, *args, **kwargs): # 可将token加入黑名单
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.system.permission import has_permission
|
||||
from openpyxl import load_workbook
|
||||
import random
|
||||
import smtplib
|
||||
import string
|
||||
from email.header import Header
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formataddr
|
||||
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
logger = logging.getLogger('log')
|
||||
# logger.info('请求成功! response_code:{};response_headers:{};response_body:{}'.format(response_code, response_headers, response_body[:251]))
|
||||
# logger.error('请求出错:{}'.format(error))
|
||||
|
||||
|
||||
class ImpMixin:
|
||||
def get_queryset(self):
|
||||
mydept = self.request.user.dept
|
||||
qs = super().get_queryset()
|
||||
if has_permission('task2', self.request.user):
|
||||
return qs
|
||||
return qs.filter(belong_dept=mydept)
|
||||
|
||||
def format_date(self, ind, val):
|
||||
new_val = val
|
||||
if isinstance(val, datetime.datetime):
|
||||
new_val = val.date()
|
||||
elif isinstance(val, datetime.date):
|
||||
new_val = val
|
||||
elif isinstance(val, str):
|
||||
try:
|
||||
new_val = datetime.datetime.strptime(val, '%Y-%m-%d').date()
|
||||
except ValueError:
|
||||
raise ParseError(f'第{ind}行, 日期时间格式错误')
|
||||
elif val is None:
|
||||
pass
|
||||
else:
|
||||
raise ParseError(f'第{ind}行, 日期时间格式错误')
|
||||
return new_val
|
||||
|
||||
def get_enum(self, val, atuple, ind):
|
||||
for i in atuple:
|
||||
if i[1] == val:
|
||||
return i[0]
|
||||
raise ParseError('第{}: 请选择固定选项值'.format(ind))
|
||||
|
||||
def F(self, data, sheet, i, etype):
|
||||
raise NotImplementedError()
|
||||
|
||||
def gen_imp_view(self, request, start: int, mySerializer):
|
||||
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)
|
||||
wb = load_workbook(fullpath,data_only=True)
|
||||
sheet = wb.active
|
||||
# 遍历Excel文件中的数据
|
||||
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)
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return Response({'uploaded': 'File uploaded successfully'}, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
permission_classes = []
|
||||
|
||||
def get(self, request, *args, **kwargs): # 可将token加入黑名单
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
def get_tokens_for_user(user):
|
||||
refresh = RefreshToken.for_user(user)
|
||||
|
@ -246,9 +304,69 @@ class RoleViewSet(ModelViewSet):
|
|||
serializer_class = RoleSerializer
|
||||
pagination_class = None
|
||||
search_fields = ['name']
|
||||
filterset_fields = []
|
||||
ordering_fields = ['id']
|
||||
ordering = 'id'
|
||||
|
||||
class UserExamViewset(ImpMixin, ModelViewSet):
|
||||
"""
|
||||
用户考试:增删改查
|
||||
"""
|
||||
perms_map = {'get': '*', 'post': 'user_exam_create',
|
||||
'put': 'user_exam_create', 'delete': 'user_exam_delete'}
|
||||
queryset = User.objects.all().order_by('-id')
|
||||
serializer_class = UserListSerializer
|
||||
filterset_class = UserFilter
|
||||
search_fields = ['username', 'name', 'phone', 'email']
|
||||
ordering_fields = ['-id']
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = self.queryset
|
||||
dept = self.request.user.dept.id
|
||||
deptqueryset = get_child_queryset2(Organization.objects.get(pk=dept))
|
||||
queryset = queryset.filter(dept__in=deptqueryset)
|
||||
return queryset
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
# 创建用户默认添加密码
|
||||
password = request.data['password'] if 'password' in request.data else None
|
||||
if password:
|
||||
password = make_password(password)
|
||||
else:
|
||||
# password = make_password(''.join(random.sample(string.ascii_letters + string.digits, 8)))
|
||||
password = make_password('0000')
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save(password=password)
|
||||
return Response(serializer.data)
|
||||
|
||||
def build_data(self, sheet, start):
|
||||
data_list = []
|
||||
exam_role = Role.objects.get(name='考试')
|
||||
if not exam_role:
|
||||
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': '部门不存在'})
|
||||
serializer_data = {
|
||||
'name': row[1],
|
||||
'username':row[2],
|
||||
'dept':dept.id,
|
||||
'roles':[exam_role.id],
|
||||
'avatar': "/media/default/avatar.png"
|
||||
}
|
||||
data_list.append(serializer_data)
|
||||
return data_list
|
||||
@action(detail=False, methods=['post'])
|
||||
@transaction.atomic
|
||||
def imp(self, request, *args, **kwargs):
|
||||
"""
|
||||
导入数据
|
||||
"""
|
||||
return self.gen_imp_view(request, 2, UserListSerializer)
|
||||
|
||||
|
||||
class UserViewSet(PageOrNot, ModelViewSet):
|
||||
"""
|
||||
|
@ -532,5 +650,6 @@ class InitAreaView(APIView):
|
|||
for i in cs:
|
||||
City.objects.create(id=i['code'], name=i['name'], parent=Province.objects.get(id=i['provinceCode']))
|
||||
return Response()
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue