This commit is contained in:
shijing 2024-05-30 17:36:36 +08:00
commit 1c18dddf66
110 changed files with 8426 additions and 819 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,56 @@
import request from '@/utils/request'
export function getCtAll(query) {
return request({
url: '/info/contact/',
method: 'get',
params: query
})
}
export function getCt(id) {
return request({
url: `/info/contact/${id}/`,
method: 'get'
})
}
export function updateContact(id, data) {
return request({
url: `/info/contact/${id}/`,
method: 'put',
data
})
}
export function createCt(data) {
return request({
url: `/info/contact/`,
method: 'post',
data
})
}
export function del_contact(id) {
return request({
url: `/info/contact/${id}/`,
method: 'delete'
})
}
export function impData(data) {
return request({
url: `/info/contact/imp/`,
method: 'post',
data
})
}

View File

@ -0,0 +1,61 @@
import request from '@/utils/request'
export function getMyEas(query) {
return request({
url: '/info/ea/',
method: 'get',
params: query
})
}
export function getOneEa(id) {
return request({
url: `/info/ea/${id}/`,
method: 'get'
})
}
export function updateEa(id, data) {
return request({
url: `/info/ea/${id}/`,
method: 'put',
data
})
}
export function createEa(data) {
return request({
url: `/info/ea/`,
method: 'post',
data
})
}
export function delEa(id) {
return request({
url: `/info/ea/${id}/`,
method: 'delete'
})
}
export function exportData(data) {
return request({
url: `/info/ea/filter_by_date/`,
method: 'post',
data
})
}
export function impData(data) {
return request({
url: `/info/ea/imp/`,
method: 'post',
data
})
}

View File

@ -51,7 +51,6 @@ export function createCertificate(data) {
//课程
export function getCourseList(query) {
return request({

View File

@ -117,6 +117,15 @@ export function createPaper(data) {
data
})
}
//导入试卷
export function UploadPaper(data) {
return request({
url: `/exam/paper/upload_paper/`,
method: 'post',
data
})
}
//编辑试卷
export function updatePaper(id, data) {
return request({
@ -210,4 +219,20 @@ export function deleteExamRecord(id) {
url: `/exam/examrecord/${id}/`,
method: 'delete'
})
}
}
// 获取课程列表
export function getCourse() {
return request({
url: `/edu/course/`,
method: 'get'
})
}
// 获取当前部门下的所有部门
export function getDepartment() {
return request({
url: `/system/organization/sub/`,
method: 'get'
})
}

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function sendMsg(data) {
return request({
url: '/info/sendmsg_info/',
method: 'post',
data
})
}

67
client/src/api/qa.js Normal file
View File

@ -0,0 +1,67 @@
import request from '@/utils/request'
export function getgetMyRcs(query) {
return request({
url: '/info/qa/',
method: 'get',
params: query
})
}
export function getRc(id) {
return request({
url: `/info/qa/${id}/`,
method: 'get'
})
}
export function updateRc(id, data) {
return request({
url: `/info/qa/${id}/`,
method: 'put',
data
})
}
export function createRc(data) {
return request({
url: `/info/qa/`,
method: 'post',
data
})
}
export function getRc_activate() {
return request({
url: `/info/qa/activate_info/`,
method: 'get'
})
}
export function del_activate(id) {
return request({
url: `/info/qa/${id}/`,
method: 'delete'
})
}
export function exportData(data) {
return request({
url: `/info/qa/filter_by_date/`,
method: 'post',
data
})
}
export function impData(data) {
return request({
url: `/info/qa/imp/`,
method: 'post',
data
})
}

64
client/src/api/qc.js Normal file
View File

@ -0,0 +1,64 @@
import request from '@/utils/request'
export function getMyRcs(query) {
return request({
url: '/info/qc/',
method: 'get',
params: query
})
}
export function getRc(id) {
return request({
url: `/info/qc/${id}/`,
method: 'get'
})
}
export function updateRc(id, data) {
return request({
url: `/info/qc/${id}/`,
method: 'put',
data
})
}
export function createRc(data) {
return request({
url: `/info/qc/`,
method: 'post',
data
})
}
export function getRc_activate() {
return request({
url: `/info/qc/activate_info/`,
method: 'get'
})
}
export function del_commend(id) {
return request({
url: `/info/qc/${id}/`,
method: 'delete'
})
}
export function exportData(data) {
return request({
url: `/info/qc/filter_by_date/`,
method: 'post',
data
})
}
export function impData(data) {
return request({
url: `/info/qc/imp/`,
method: 'post',
data
})
}

View File

@ -0,0 +1,76 @@
import request from '@/utils/request'
export function getAllQi(query) {
return request({
url: '/info/faq/',
method: 'get',
params: query
})
}
export function getMyQi(query) {
return request({
url: '/info/faqch/',
method: 'get',
params: query
})
}
export function getQi(id) {
return request({
url: `/info/ad/?instance_id=${id}`,
method: 'get'
})
}
export function updateQi(id, data) {
return request({
url: `/info/faqch/${id}/`,
method: 'put',
data
})
}
export function createQi(data) {
return request({
url: `/info/faq/`,
method: 'post',
data
})
}
export function delQi(id) {
return request({
url: `/info/faq/${id}/`,
method: 'delete'
})
}
export function exportData(data) {
return request({
url: `/info/faq/filter_by_date/`,
method: 'post',
data
})
}
export function getChangeInfo(query) {
return request({
url: `/info/faqch/`,
method: 'get',
params: query
})
}
export function getMyOneQi(query) {
return request({
url: `/info/faq/`,
method: 'get',
params: query
})
}

24
client/src/api/report.js Normal file
View File

@ -0,0 +1,24 @@
import request from '@/utils/request'
export function parsePDF(data) {
return request({
url: `/info/pdf/parse_pdf/`,
method: 'post',
data
})
}
export function pdfAll(query) {
return request({
url: '/info/pdf/',
method: 'get',
params: query
})
}
export function getpdf(id) {
return request({
url: `/info/pdf/${id}/`,
method: 'get'
})
}

62
client/src/api/review.js Normal file
View File

@ -0,0 +1,62 @@
import request from '@/utils/request'
export function getMyRs(query) {
return request({
url: '/info/ar/',
method: 'get',
params: query
})
}
export function getRs(id) {
return request({
url: `/info/ar/${id}/`,
method: 'get'
})
}
export function updateRs(id, data) {
return request({
url: `/info/ar/${id}/`,
method: 'put',
data
})
}
export function createRs(data) {
return request({
url: `/info/ar/`,
method: 'post',
data
})
}
export function delRs(id) {
return request({
url: `/info/ar/${id}/`,
method: 'delete'
})
}
export function exportData(data) {
return request({
url: `/info/ar/filter_by_date/`,
method: 'post',
data
})
}
export function impData(data) {
return request({
url: `/info/ar/imp/`,
method: 'post',
data
})
}

View File

@ -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/',

View File

@ -501,4 +501,45 @@ export function taskAnalyse(data) {
method: 'post',
data
})
}
export function delPts(data) {
return request({
url: '/supervision/pt/bulk_delete/',
method: 'delete',
data
})
}
export function delRcs(data) {
return request({
url: '/supervision/rc/bulk_delete/',
method: 'delete',
data
})
}
export function delRicks(data) {
return request({
url: '/supervision/risk/bulk_delete/',
method: 'delete',
data
})
}
export function delComplaints(data) {
return request({
url: '/supervision/complaint/bulk_delete/',
method: 'delete',
data
})
}
export function delOinspects(data) {
return request({
url: '/supervision/oinspect/bulk_delete/',
method: 'delete',
data
})
}

View File

@ -0,0 +1,59 @@
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
})
}
export function impData(data) {
return request({
url: '/system/userexam/imp/',
method: 'post',
data
})
}

View File

@ -412,9 +412,21 @@ export const asyncRoutes = [
component: Layout,
redirect: '/infoCollect/',
name: 'informationCollect',
meta: { title: '信息收集', icon: 'PT', perms: ['infoCollect'] },
meta: { title: '信息收集', icon: 'PT'},
alwaysShow: true,
children: [
{
path: 'qualificInfo',
name: 'qualificInfo',
component: () => import('@/views/informatiomCollect/qualificInfo.vue'),
meta: { title: '资质情况'}
},
{
path: 'qualiChange',
name: 'qualiChange',
component: () => import('@/views/informatiomCollect/qualiChange.vue'),
meta: { title: '资质变更情况', perms: ['infoCollect_QICH'] }
},
{
path: 'reviewStatus',
name: 'reviewStatus',
@ -446,11 +458,23 @@ export const asyncRoutes = [
meta: { title: '外审员情况', perms: ['infoCollect_EA'] }
},
{
path: 'inspectionStats',
name: 'inspectionStats',
component: () => import('@/views/informatiomCollect/inspectionStats.vue'),
meta: { title: '检验检测统计', perms: ['infoCollect_IS'] }
path: 'yearReport',
name: 'yearReport',
component: () => import('@/views/informatiomCollect/yearReport.vue'),
meta: { title: '实验室年度报告', perms: ['infoCollect_report'] }
},
{
path: 'smsMessage',
name: 'smsMessage',
component: () => import('@/views/informatiomCollect/smsMessage.vue'),
meta: { title: '发送邮件', perms: ['infoCollect_SM']}
},
// {
// path: 'inspectionStats',
// name: 'inspectionStats',
// component: () => import('@/views/informatiomCollect/inspectionStats.vue'),
// meta: { title: '检验检测统计', perms: ['infoCollect_IS'] }
// },
]
},
{
@ -535,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',
@ -568,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',
@ -588,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'] }
},
]
},

View File

@ -30,7 +30,7 @@
<el-table-column type="index" width="50" />
<el-table-column label="名称" min-width="110">
<template slot-scope="scope" v-if="scope.row.file_.file">
<a v-if="scope.row.file_.file.indexOf('.pdf')>-1 " :href="`/static/build/generic/web/viewer.html?file=${scope.row.file_.file}`" target="view_window" class="flv">
<a v-if="scope.row.file_.file.indexOf('.pdf')>-1 " :href="scope.row.file_.file" target="view_window" class="flv">
<el-link type="primary">{{ scope.row.name }}</el-link>
</a>
<el-link v-else :href="scope.row.file_.file" type="primary">{{ scope.row.name }}</el-link>

View File

@ -53,6 +53,12 @@
<span>{{ scope.row.create_admin_username }}</span>
</template>
</el-table-column>
<el-table-column label="参加人数">
<template slot-scope="scope">{{ scope.row.user_count }}</template>
</el-table-column>
<el-table-column label="完成人数">
<template slot-scope="scope">{{ scope.row.submit_count }}</template>
</el-table-column>
<el-table-column align="center" label="操作" fixed="right">
<template slot-scope="scope">
<el-button
@ -125,6 +131,59 @@
></el-option>
</el-select>
</el-form-item>
<el-form-item label = "出具证书">
<el-select v-model="exam.certificate" placeholder="是否出具证书" style="width:100%" clearable>
<el-option
label = ""
value = 'true'>
</el-option>
<el-option
label = ''
value= 'false'>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="课程名称" prop="course_name" v-if="exam.certificate==='true'">
<el-select v-model="exam.course_name" multiple placeholder = "请选择课程名称">
<el-option
v-for="item in courseOptions"
:key = "item.id"
:label = "item.name"
:value = "item.id">
</el-option>
</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-option
v-for="item in depOptions"
:key = "item.id"
:label = "item.name"
:value = "item.id">
</el-option>
</el-select>
</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
v-for="item in userOptions"
:key = "item.id"
:label = "item.name"
:value = "item.id">
</el-option>
</el-select>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</el-form-item> -->
</el-form>
<div style="text-align:right;">
<el-button type="danger" @click="dialogVisible=false">取消</el-button>
@ -134,10 +193,12 @@
</div>
</template>
<script>
import { getPaperList,getExamList, createExam, updateExam,deleteExam} from "@/api/exam";
import { getPaperList,getExamList, createExam, updateExam,deleteExam, getCourse, getDepartment} from "@/api/exam";
import {getUserList} from "@/api/user";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"
const defaultexam = {
id: "",
name: "",
@ -147,13 +208,17 @@
proctor_name:'',
proctor_phone:'',
chance:3,
paper:''
paper:'',
certificate:'',
course_name:[],
participant_dep:[],
participant_user:[],
};
const listQuery = {
page: 1,
limit: 20,
search: ""
}
};
export default {
components: { Pagination },
data() {
@ -163,6 +228,10 @@
id: "",
name: "",
},
// has_certificate:'true',
courseOptions:[],
depOptions:[],
userOptions:[],
listQuery:listQuery,
tableData: {count:0},
listLoading: true,
@ -170,6 +239,9 @@
dialogType: "new",
workscopeOptions:[],
paperOptions:[],
currentPage: 1, // 当前页码
pageSize: 10, // 每页显示的条数
total: 0, // 总条数
rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }],
place: [{ required: true, message: "请输入", trigger: "change" }],
@ -186,17 +258,21 @@
mounted() {
this.getPaperOptions();
this.getList();
this.getCourseList();
this.getDepartmentList();
this.getUser();
},
methods: {
checkPermission,
getPaperOptions(){
getPaperList({page:0}).then(res=>{
debugger;
console.log(res)
this.paperOptions = res.data
})
},
selectAll(){
this.exam.participant_dep = this.depOptions.map(option => option.id);
},
getList() {
this.listLoading = true;
debugger;
@ -205,6 +281,35 @@
this.listLoading = false;
});
},
handleSizeChange(val) {
// 改变每页显示的条数
this.pageSize = val;
this.getUser();
},
handleCurrentChange(val) {
// 改变当前页码
this.currentPage = val;
this.getUser();
},
getDepartmentList(){
getDepartment().then(response =>{
this.depOptions = response.data
return this.depOptions
})
},
getUser(){
getUserList({page: this.currentPage, size: this.pageSize}).then(response =>{
this.userOptions = response.data.results
this.total = response.data.total;
// console.log(this.userOptions)
})
},
getCourseList(){
getCourse(this.listQuery).then(response =>{
this.courseOptions = response.data.results
this.listLoading = false;
})
},
resetFilter() {
this.listQuery = {
page: 1,
@ -257,6 +362,7 @@
async confirmexam(form) {
this.$refs[form].validate(valid => {
if (valid) {
this.exam.certificate = this.exam.certificate === 'true';
const isEdit = this.dialogType === "edit";
if (isEdit) {
updateExam(this.exam.id, this.exam).then(() => {

View File

@ -24,8 +24,12 @@
<div style="text-align: left; margin: 0;">
<el-link href="/media/muban/question.xlsx" target="_blank" @click="popovervisible = false"
type="primary">下载模板</el-link>
<el-upload :action="upUrl" :on-success="handleUploadSuccess" accept=".xlsx" :headers="upHeaders"
:show-file-list="false">
<el-upload
:action="upUrl"
:on-success="handleUploadSuccess"
accept=".xlsx"
:headers="upHeaders"
:show-file-list="false">
<el-button size="small" type="primary" @click="popovervisible = false">上传导入</el-button>
</el-upload>
</div>
@ -153,8 +157,9 @@ export default {
methods: {
checkPermission,
handleUploadSuccess(res, file) {
if (res.code == 200) {
if (res.code == 201) {
const loading = this.$loading({ text: "正在导入中..." })
console.log(res.data);
importQuestion(res.data).then(response => {
loading.close()
if (response.code == 200) {

View File

@ -45,7 +45,47 @@
</el-col>
<el-col :span="16">
<h3>选题信息</h3>
<el-button type="primary" @click="handleChoose" icon="el-icon-plus">选择试题</el-button>
<!-- <div> -->
<div style="display: flex;">
<el-button type="primary" @click="handleChoose" icon="el-icon-plus" style="margin-right: 10px;">选择试题</el-button>
<el-button type="primary" @click="handleChoose1" icon="el-icon-plus" style="margin-right: 10px;">上传试题</el-button>
<el-dialog
:visible.sync="impDialogVisible"
title="上传试题"
:left="'200px'"
:close-on-click-modal="false"
:height="'600px'"
>
<el-row>
<el-col :span="12">
<el-select v-model="impForm.question_type" style="margin-right: 10px;" placeholder="题目分类">
<el-option
v-for="item in type_list"
:key="item.id"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</el-col>
<el-upload
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx">
<el-col :span="12">
<el-button icon="el-icon-plus" type="primary" style="margin-right: 10px;">上传试题</el-button>
</el-col>
</el-upload>
</el-row>
<div style="text-align: right">
<el-button type="danger" @click="impDialogVisible=false">取消</el-button>
<el-button type="primary" @click="impDialogVisible=false">确认</el-button>
</div>
</el-dialog>
</div>
<div v-for="(item, index) in questions">
<h4>
<el-button
@ -67,8 +107,9 @@
</div>
</template>
<script>
import { getQuestioncatList, createPaper } from "@/api/exam";
import { getQuestioncatList, createPaper, UploadPaper } from "@/api/exam";
import Questionchoose from "@/views/exam/questionChoose";
import { upUrl, upHeaders } from "@/api/file";
export default {
components: { Questionchoose },
data() {
@ -88,7 +129,17 @@
panduan_score: 2,
panduan_count: 0
},
upUrl: upUrl(),
impDialogVisible: false,
fileList: [],
type_list:[],
upHeaders: upHeaders(),
submitLoding: false,
impForm:{
doc_path:'',
question_type:'',
},
rules: {
name: [
{ required: true, message: "名称不能为空", trigger: "blur" }
@ -116,8 +167,38 @@
getQuestioncat() {
getQuestioncatList().then(response => {
this.workscopeData = response.data.results;
this.type_list = response.data.results;
});
},
handleChoose1(){
this.impDialogVisible = true;
},
closeDg() {
this.chooseVisible = false;
},
handleUpSuccess(res, file, filelist) {
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({
type: "success",
message: "上传成功"
})
}
}
)
}
},
handleRemove() {
this.fileList = [];
this.impForm.file = '';
},
submitForm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
@ -126,7 +207,7 @@
this.submitLoding = false;
this.$message({
type: "success",
message: "编辑成功!"
message: "新增成功!"
});
this.goBack();
}).catch(res=>{

View File

@ -15,19 +15,19 @@
单选题
<span style="color:darkred;font-weight:bold">{{Form.danxuan_count}} </span>
每道
<el-input-number v-model="Form.danxuan_score" :min="0"></el-input-number>
<el-input-number v-model="Form.danxuan_score" :min="0" @change="calScore"></el-input-number>
</div>
<div>
多选题
<span style="color:darkred;font-weight:bold">{{Form.duoxuan_count}} </span>
每道
<el-input-number v-model="Form.duoxuan_score" :min="0"></el-input-number>
<el-input-number v-model="Form.duoxuan_score" :min="0" @change="calScore"></el-input-number>
</div>
<div>
判断题
<span style="color:darkred;font-weight:bold">{{Form.panduan_count}} </span>
每道
<el-input-number v-model="Form.panduan_score" :min="0"></el-input-number>
<el-input-number v-model="Form.panduan_score" :min="0" @change="calScore"></el-input-number>
</div>
<div>
总分
@ -125,7 +125,6 @@
let that = this;
getPaperDetail(this.Form.id).then(response => {
that.Form = response.data;
debugger;
that.questions = response.data.questions_;
});
},
@ -167,7 +166,7 @@
this.questions.splice(val, 1);
},
calScore() {
let that = this;
let that = this;
let danxuan_count = 0,
duoxuan_count = 0,
panduan_count = 0,
@ -188,7 +187,7 @@
total_score = that.Form.panduan_score;
break;
}
questions.push({question:that.questions[i].id,total_score:total_score})
questions.push({id:that.questions[i].id,total_score:total_score, question:that.questions[i].question})
}
that.Form.danxuan_count = danxuan_count;
that.Form.duoxuan_count = duoxuan_count;

View File

@ -2,62 +2,142 @@
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" @click="handleImport('ea')">导入</el-button>
<el-button type="primary" @click="showExportDialog = true">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading" :data="tableData.results" :hieght="tableHeight" border fit stripe
<el-table v-loading="listLoading"
:data="tableData.results"
border fit stripe height="500px"
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="文件名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="文件描述">
<template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column>
<el-table-column label="文件格式">
<template slot-scope="scope">{{ scope.row.file_.mime }}</template>
</el-table-column>
<el-table-column align="center" label="文件下载">
<template slot-scope="scope" v-if="scope.row.file_.file">
<el-link :href="scope.row.file_.file" type="primary">下载</el-link>
</template>
</el-table-column>
<el-table-column label="公司名称" prop="name_company"></el-table-column>
<el-table-column label="姓名" prop="name"></el-table-column>
<el-table-column label="评审类型" prop="review_types"></el-table-column>
<el-table-column label="证书有效期" prop="certificate_expiration"></el-table-column>
<el-table-column label="联系方式" prop="contact"></el-table-column>
<el-table-column label="评审领域" prop="judging_areas"></el-table-column>
<el-table-column label="备注" prop="remark"></el-table-column>
<el-table-column align="center" label="操作" width="120px" fixed="right">
<template slot-scope="scope">
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="primary" size="small"
<el-link :disabled="!checkPermission(['infoCollect_EA'])" type="primary" size="small"
@click="handleEdit(scope)">编辑</el-link>
<el-divider direction="vertical"
v-if="checkPermission(['infoCollect_RS']) && checkPermission(['infoCollect_RS']) "></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="danger" size="small"
v-if="checkPermission(['infoCollect_EA']) && checkPermission(['infoCollect_EA'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_EA'])" type="danger" size="small"
@click="handleDelete(scope)">删除</el-link>
</template>
</el-table-column>
<el-col :span="12" style="margin-top: 5px;">
<div class="cardHead">
<span class="cardTitle">外审员情况</span>
</div>
</el-col>
</el-table>
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑评审' : '新增评审'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="评审名称" prop="name">
<el-input v-model="Content.name" placeholder="评审名称" />
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="100px" label-position="right" :rules="rule">
<el-form-item label="公司名称" prop="name_company">
<el-input v-model="Content.name_company" placeholder="公司名称"/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="Content.name" placeholder="姓名"/>
</el-form-item>
<el-form-item label="评审类型">
<el-input v-model="Content.review_types" placeholder="评审类型"/>
</el-form-item>
<el-form-item label="证书有效期" prop="certificate_expiration">
<el-date-picker
v-model="Content.certificate_expiration"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
>
</el-date-picker>
<el-form-item label="" prop="description">
<el-input type="textarea" :rows="2" v-model="Content.description" placeholder="评审描述" />
</el-form-item>
<el-form-item label="评审文件" prop="file" v-if="dialogVisible">
<el-upload ref="upload" :action="upUrl" :on-preview="handlePreview" :on-success="handleUpSuccess"
:on-remove="handleRemove" :headers="upHeaders" :file-list="fileList" :limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<el-form-item label="联系方式" prop="contact">
<el-input v-model="Content.contact" ></el-input>
</el-form-item>
<el-form-item label="评审领域" prop="judging_areas">
<el-input v-model="Content.judging_areas"></el-input>
</el-form-item>
<el-form-item label="备注" prop="remark" >
<el-input v-model="Content.remark"></el-input>
</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>
<el-dialog
:visible.sync="impDialogVisible"
title="导入"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="80px"
label-position="right"
:rules="rule"
>
<el-form-item label="下载模板" prop="path">
<a :href="downloadUrl">点击下载模板</a>
</el-form-item>
<el-form-item label="导入文件" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>
</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>
</div>
</el-dialog>
<el-dialog
title="选择时间范围"
:visible.sync="showExportDialog"
width="50%"
@close="showExportDialog = false"
>
<el-form :model="exportForm" ref="exportForm" label-width="120px">
<el-form-item label="开始日期">
<el-date-picker
v-model="exportForm.startDate"
type="date"
placeholder="选择开始日期"
></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker
v-model="exportForm.endDate"
type="date"
placeholder="选择结束日期"
></el-date-picker>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showExportDialog = false">取消</el-button>
<el-button type="primary" @click="handleExport">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
@ -66,16 +146,34 @@ import Pagination from "@/components/Pagination"; // secondary package based on
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import { getMyEas, delEa, createEa, updateEa, exportData, impData } from "@/api/eaduitors";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
name: "",
description: "",
file: null
};
name_company:"",
name: "",
review_types: "",
certificate_expiration: "",
contact: "",
judging_areas: "",
remark: ""
};
export default {
components: { Pagination, Treeselect },
data() {
data(){
return {
form: {
name_company:"",
name: "",
review_types: "",
certificate_expiration: "",
contact: "",
judging_areas: "",
remark: ""
},
downloadUrl: process.env.VUE_APP_BASE_API,
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
@ -83,10 +181,20 @@ export default {
typeOptions: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
saveLoading:false,
impDialogVisible: false,
exportForm: {
startDate: '', // 开始日期
endDate: '', // 结束日期
},
listQuery: {
page: 1,
page_size: 20,
},
impForm:{
file:''
},
tableData: {
count: 0
},
@ -95,44 +203,83 @@ export default {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
},
filterOrgText: "",
tableHeight:null,
};
},
computed: {},
watch: {
filterOrgText(val) {
this.$refs.tree.filter(val);
},
},
created() {
// tableHeight: '300px',
reviewOptions:[
{key:'CNAS',value:0},
{key:'CMA',value:1},
{key:'DICA',value:2}
],
}
},
mounted() {
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
console.log(that.tableHeight)
that.getList();
},
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
console.log(that.tableHeight)
that.getList();
},
methods: {
getTableList() {
this.getList();
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
this.impForm.file = '';
},
handleCreate() {
this.Content = Object.assign({}, defaultContent);
this.dialogType = "new";
this.dialogVisible = true;
this.fileList = []
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
impData(this.impForm).then(res=>{
console.log(this.impForm)
if(res.code>=200){
this.saveLoading = false;
this.$message.success("提交成功");
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
}
})
},
handleImport(type){
this.impDialogVisible = true;
this.impType = type;
let file_url = '';
if(type=='ea'){
file_url = "media/default/外审员情况表.xlsx";
}
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
getTableList(){
this.getList()
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
} else {
window.open(file.response.data.path);
}
if ("response" in file) {
window.open(file.response.data.path);
} else {
window.open(file.value);
}
},
handleUpSuccess(res, file, filelist) {
this.Content.file = res.data.id;
handleUpSuccess(res, file, fileList) {
console.log(res, file, fileList)
this.impForm.file = res.data.path;
console.log(this.impForm)
},
handleRemove(file, filelist) {
this.Content.file = null;
handleRemove(file) {
this.fileList = [];
this.impForm.file = '';
},
checkPermission,
@ -140,11 +287,58 @@ export default {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
getList() {
onSubmit(){
},
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getMyEas(this.listQuery).then((response) => {
console.log(response);
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
});
},
handleExport() {
if (!this.exportForm.startDate || !this.exportForm.endDate) {
this.$message.error('请选择完整的起止日期!');
return;}
console.log(this.exportForm)
exportData(this.exportForm).then((response) => {
if (response.data) {
let filename = '外审员情况表.xlsx';
let tableData = response.data;
console.log(tableData)
const ws = XLSX.utils.json_to_sheet(tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
}else {
this.$message.error(data.message || '导出失败');
}
})
// this.showExportDialog = false;
.catch(error => {
this.$message.error('请求失败,请稍后再试');
console.error(error);
})
.finally(() => {
this.handleDialogClose();
});
},
handleDialogClose(){
this.showExportDialog = false;
this.startDate = '';
this.endDate = '';
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
@ -181,9 +375,12 @@ export default {
type: "error",
})
.then(async () => {
// await deleteDoc(scope.row.id);
// this.getList();
// this.$message.success("成功");
await delEa(scope.row.id).then(() => {
this.getList();
this.$message.success("成功");
this.getTableList();
})
})
.catch((err) => {
console.error(err);
@ -195,17 +392,35 @@ export default {
const isEdit = this.dialogType === "edit";
if (isEdit) {
console.log(this.Content)
updateEa(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createEa(this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
}
});
},
onCancel() {
this.$emit("closed");
},
},
};
</script>
<style>
</style>
<style></style>

View File

@ -2,32 +2,50 @@
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleImport">导入</el-button>
<el-button type="primary" @click="handleExport">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading" :data="tableData.results" :hieght="tableHeight" border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="文件名称">
<el-table-column label="公司名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="文件描述">
<template slot-scope="scope">{{ scope.row.description }}</template>
<el-table-column label="地址">
<template slot-scope="scope">{{ scope.row.address }}</template>
</el-table-column>
<el-table-column label="文件格式">
<template slot-scope="scope">{{ scope.row.file_.mime }}</template>
<el-table-column label="负责人">
<template slot-scope="scope">{{ scope.row.header }}</template>
</el-table-column>
<el-table-column align="center" label="文件下载">
<template slot-scope="scope" v-if="scope.row.file_.file">
<el-link :href="scope.row.file_.file" type="primary">下载</el-link>
</template>
<el-table-column label="负责人电话">
<template slot-scope="scope">{{ scope.row.tel }}</template>
</el-table-column>
<el-table-column label="负责人邮箱">
<template slot-scope="scope">{{ scope.row.email }}</template>
</el-table-column>
<el-table-column label="技术负责人">
<template slot-scope="scope">{{ scope.row.tel_technology }}</template>
</el-table-column>
<el-table-column label="技术负责人邮箱">
<template slot-scope="scope">{{ scope.row.email_technology }}</template>
</el-table-column>
<el-table-column label="质量负责人">
<template slot-scope="scope">{{ scope.row.head_quality }}</template>
</el-table-column>
<el-table-column label="质量负责人电话">
<template slot-scope="scope">{{ scope.row.tel_quality }}</template>
</el-table-column>
<el-table-column label="质量负责人邮箱">
<template slot-scope="scope">{{ scope.row.email_quality }}</template>
</el-table-column>
<el-table-column align="center" label="操作" width="120px" fixed="right">
<template slot-scope="scope">
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="primary" size="small"
<el-link v-if="currentDept=scope.row.belong_dept" :disabled="!checkPermission(['infoCollect_LC'])" type="primary" size="small"
@click="handleEdit(scope)">编辑</el-link>
<el-divider direction="vertical"
v-if="checkPermission(['infoCollect_RS']) && checkPermission(['infoCollect_RS'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="danger" size="small"
v-if="checkPermission(['infoCollect_LC']) && checkPermission(['infoCollect_LC'])"></el-divider>
<el-link v-if="currentDept=scope.row.belong_dept" :disabled="!checkPermission(['infoCollect_LC'])" type="danger" size="small"
@click="handleDelete(scope)">删除</el-link>
</template>
</el-table-column>
@ -35,29 +53,81 @@
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑评审' : '新增评审'">
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="评审名称" prop="name">
<el-input v-model="Content.name" placeholder="评审名称" />
<el-form-item label="姓名" prop="name">
<el-input v-model="Content.name" placeholder="" />
</el-form-item>
<el-form-item label="" prop="description">
<el-input type="textarea" :rows="2" v-model="Content.description" placeholder="评审描述" />
<el-form-item label="地址" prop="address">
<el-input v-model="Content.address" placeholder="" />
</el-form-item>
<el-form-item label="评审文件" prop="file" v-if="dialogVisible">
<el-upload ref="upload" :action="upUrl" :on-preview="handlePreview" :on-success="handleUpSuccess"
:on-remove="handleRemove" :headers="upHeaders" :file-list="fileList" :limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<el-form-item label="负责人" prop="header">
<el-input v-model="Content.header" placeholder="" />
</el-form-item>
<el-form-item label="负责人电话" prop="tel">
<el-input v-model="Content.tel" placeholder="" />
</el-form-item>
<el-form-item label="负责人邮箱" prop="email">
<el-input v-model="Content.email" placeholder="" />
</el-form-item>
<el-form-item label="技术负责人" prop="head_technology">
<el-input v-model="Content.head_technology" placeholder="" />
</el-form-item>
<el-form-item label="技术负责人电话" prop="tel_technology">
<el-input v-model="Content.tel_technology" placeholder="" />
</el-form-item>
<el-form-item label="技术负责人邮箱" prop="email_technology">
<el-input v-model="Content.email_technology" placeholder="" />
</el-form-item>
<el-form-item label="质量负责人" prop="head_quality">
<el-input v-model="Content.head_quality" placeholder="" />
</el-form-item>
<el-form-item label="质量负责人电话" prop="tel_quality">
<el-input v-model="Content.tel_quality" placeholder="" />
</el-form-item>
<el-form-item label="质量负责人邮箱" prop="email_quality">
<el-input v-model="Content.email_quality" placeholder="" />
</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>
<el-dialog
:visible.sync="impDialogVisible"
title="导入"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="80px"
label-position="right"
:rules="rule"
>
<el-form-item label="下载模板" prop="path">
<a :href="downloadUrl">点击下载模板</a>
</el-form-item>
<el-form-item label="导入文件" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="close_dialog">取消</el-button>
<el-button type="primary" @click="submitUpload">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
@ -66,16 +136,41 @@ import Pagination from "@/components/Pagination"; // secondary package based on
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import {createCt, del_contact, updateContact, getCtAll, getCt, impData} from "@/api/contacts";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
name: "",
description: "",
file: null
name: "",
address: "",
header: "",
tel: "",
email: "",
head_technology: "",
tel_technology: "",
email_technology: "",
head_quality: "",
tel_quality: "",
email_quality: ""
};
export default {
components: { Pagination, Treeselect },
data() {
data(){
return {
form: {
name: "",
address: "",
header: "",
tel: "",
email: "",
head_technology: "",
tel_technology: "",
email_technology: "",
head_quality: "",
tel_quality: "",
email_quality: ""
},
currentDept: this.$store.state.user.dept,
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
@ -83,6 +178,9 @@ export default {
typeOptions: [],
listLoading: false,
dialogVisible: false,
saveLoading:false,
impDialogVisible: false,
downloadUrl: process.env.VUE_APP_BASE_API,
listQuery: {
page: 1,
page_size: 20,
@ -90,6 +188,9 @@ export default {
tableData: {
count: 0
},
impForm:{
file:''
},
dialogType: "new",
rule: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
@ -120,6 +221,36 @@ export default {
getTableList() {
this.getList();
},
handleImport(){
this.impForm.file = '';
this.fileList = [];
this.impDialogVisible = true;
let file_url = '';
file_url = "media/default/实验室联系方式.xlsx";
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
this.impForm.file = '';
},
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
impData(this.impForm).then(res=>{
console.log(this.impForm)
if(res.code>=200){
this.$message.success("提交成功");
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
this.fileList = [];
}
})
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
@ -129,10 +260,12 @@ export default {
},
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 = '';
},
checkPermission,
@ -142,9 +275,29 @@ export default {
},
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getCtAll(this.listQuery).then((response) => {
console.log(response);
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
});
},
handleExport() {
let filename = '实验室联系方式表.xlsx';
console.log(this.tableData)
const ws = XLSX.utils.json_to_sheet(this.tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
@ -162,6 +315,8 @@ export default {
this.Content = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit";
this.dialogVisible = true;
console.log(this.Content);
console.log(this.currentDept)
if (this.Content.file) {
this.fileList = [
{
@ -181,9 +336,9 @@ export default {
type: "error",
})
.then(async () => {
// await deleteDoc(scope.row.id);
// this.getList();
// this.$message.success("成功");
await del_contact(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
@ -194,10 +349,24 @@ export default {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
console.log(this.Content)
console.log(this.Content);
updateContact(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createCt(this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
@ -205,6 +374,8 @@ export default {
});
},
},
};
</script>
<style></style>
@/api/contacts

View File

@ -0,0 +1,399 @@
<template>
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" @click="handleExport">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading"
:data="tableData.results"
border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="公司名称" prop="company_name"></el-table-column>
<el-table-column label="资质名称" prop="name"></el-table-column>
<el-table-column label="资质类型" prop="quali_type">
<template slot-scope="scope">
<span>{{ac_options[scope.row.quali_type]}}</span>
</template>
</el-table-column>
<el-table-column label="发证单位" prop="org"></el-table-column>
<el-table-column label="发证日期" prop="org_date"></el-table-column>
<el-table-column label="截至日期" prop="expiration_date"></el-table-column>
<el-table-column label="资质范围" prop="scope"></el-table-column>
<el-table-column label="参数数量" prop="number"></el-table-column>
<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 type="primary" size="small"
@click="handleEdit(scope)">编辑</el-link>
<el-divider direction="vertical"
></el-divider>
<el-link type="danger" size="small"
@click="handleDelete(scope)">删除</el-link>
<!-- <el-link type="primary" @click="handleChange(scope)">变更记录</el-link> -->
</template>
</el-table-column>
</el-table>
<!-- <el-dialog title="变更记录" :visible.sync="dialogTableVisible">
<el-table :data="changeTableData" style="width: 100%">
<el-table-column label="公司名称" prop="company_name"></el-table-column>
<el-table-column label="变更时间" prop="change_time" ></el-table-column>
<el-table-column prop="change_reason" label="变更原因"></el-table-column>
<el-table-column label="资质名称" >
<template v-slot="scope">
<span>{{scope.row.val_new.name}}</span>
</template>
</el-table-column>
<el-table-column label="变更详情" width="500">
<template slot-scope="scope">
<el-tag v-for="(diff, index) in scope.row.difference" :key="index">
{{ resultsMap[diff.name] }}: {{ diff.old }} -> {{ diff.new }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-dialog> -->
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="公司名称" prop="company_name">
<el-input v-model="Content.company_name" placeholder="公司名称"/>
</el-form-item>
<el-form-item label="资质名称" prop="name">
<el-input v-model="Content.name" placeholder="资质名称"/>
</el-form-item>
<el-form-item label="资质类型">
<el-select
v-model="Content.quali_type"
placeholder="请选择"
>
<el-option
v-for="item in activateOptions"
:key="item.value"
:label="item.key"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="发证单位" prop="org">
<el-input v-model="Content.org" ></el-input>
</el-form-item>
<el-form-item label="发证日期" prop="org_date">
<el-date-picker
v-model="Content.org_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
>
</el-date-picker>
</el-form-item>
<el-form-item label="截至日期" prop="expiration_date">
<el-date-picker
v-model="Content.expiration_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
>
</el-date-picker>
</el-form-item>
<el-form-item label="资质范围" prop="scope">
<el-input v-model="Content.scope" ></el-input>
</el-form-item>
<el-form-item label="变更原因" prop="change_reason">
<el-input v-model="Content.change_reason" ></el-input>
</el-form-item>
<el-form-item label="参数数量" prop="number" >
<el-input-number
v-model="Content.number"
:min="1"
controls-position="right"
:step="1"
/>
</el-form-item>
<el-form-item label="证书上传" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传证书</el-button>
</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>
<script>
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import { getMyQi, updateQi, createQi, delQi} from "@/api/qualificationInfo";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
company_name: "",
name: "",
quali_type: "",
org: "",
org_date: "",
expiration_date: "",
scope: "",
number: "",
cie_path: "",
change_date:"",
change_reason:""
};
export default {
components: { Pagination, Treeselect },
data(){
return {
form: {
company_name: "",
name: "",
quali_type: "",
org: "",
org_date: "",
expiration_date: "",
scope: "",
number: "",
cie_path: "",
change_date:"",
change_reason:""
},
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
Content: defaultContent,
typeOptions: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
dialogTableVisible:false,
exportForm: {
startDate: '', // 开始日期
endDate: '', // 结束日期
},
listQuery: {
page: 1,
page_size: 20,
},
tableData: {
count: 0
},
changeTableData:[],
dialogType: "new",
rule: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
},
filterOrgText: "",
// tableHeight: '300px',
activateOptions:[
{key:'国家级',value:'国家级'},
{key:'省级',value:'省级'}
],
ac_options:{'国家级':'国家级', '省级':'省级'},
resultsMap:{
"company_name":"公司名称",
"name":"资质名称",
"quali_type":"资质类型",
"org":"发证单位",
"org_date":"发证日期",
"expiration_date":"截至日期",
"scope":"资质范围",
"number":"参数数量",
"cie_path":"证书路径",
}
}
},
mounted() {
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
that.getList();
},
methods: {
//提交表单
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.dialogType == "new") {
this.addData();
} else {
this.editData();
}
} else {
console.log("error submit!!");
return false;
}
});
},
getTableList(){
this.getList()
},
handleExport() {
// if (!this.exportForm.startDate || !this.exportForm.endDate) {
// this.$message.error('请选择完整的起止日期!');
// return;}
getMyQi().then((response) => {
if (response.data) {
let filename = '资质变更情况统计表.xlsx';
let tableData = response.data;
const ws = XLSX.utils.json_to_sheet(tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
}else {
this.$message.error(data.message || '导出失败');
}
})
// this.showExportDialog = false;
.catch(error => {
this.$message.error('请求失败,请稍后再试');
console.error(error);
})
.finally(() => {
this.handleDialogClose();
});
},
handleDialogClose(){
this.showExportDialog = false;
this.startDate = '';
this.endDate = '';
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
} else {
window.open(file.response.data.path);
}
},
handleUpSuccess(res, file, filelist) {
this.Content.cie_path = res.data.path;
this.Content.file = res.data.id;
},
handleRemove(file, filelist) {
this.Content.file = null;
},
checkPermission,
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getMyQi(this.listQuery).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
}
);
},
handleAddFile() {
this.Content = Object.assign({}, defaultContent);
this.dialogType = "new";
this.dialogVisible = true;
this.fileList = []
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.Content = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit";
this.dialogVisible = true;
if (this.Content.file) {
this.fileList = [
{
name: this.Content.file_.name,
url: this.Content.file,
},
];
}
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await delQi(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
async confirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
console.log(this.Content, "edited");
updateQi(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createQi(this.Content).then((response) => {
if (response.data) {
console.log(response.data, "created");
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
}
});
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,409 @@
<template>
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" @click="handleExport">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading"
:data="tableData.results"
border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="公司名称" prop="company_name"></el-table-column>
<el-table-column label="资质名称" prop="name"></el-table-column>
<el-table-column label="资质类型" prop="quali_type">
<template slot-scope="scope">
<span>{{ac_options[scope.row.quali_type]}}</span>
</template>
</el-table-column>
<el-table-column label="发证单位" prop="org"></el-table-column>
<el-table-column label="发证日期" prop="org_date"></el-table-column>
<el-table-column label="截至日期" prop="expiration_date"></el-table-column>
<el-table-column label="资质范围" prop="scope"></el-table-column>
<el-table-column label="参数数量" prop="number"></el-table-column>
<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"
@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"
@click="handleDelete(scope)">删除</el-link>
<el-link type="primary" @click="handleChange(scope)">变更记录</el-link>
</template>
</el-table-column>
</el-table>
<el-dialog title="变更记录" :visible.sync="dialogTableVisible">
<el-table :data="changeTableData" style="width: 100%">
<el-table-column label="公司名称" prop="company_name"></el-table-column>
<el-table-column label="变更时间" prop="change_time" ></el-table-column>
<el-table-column prop="change_reason" label="变更原因"></el-table-column>
<el-table-column label="资质名称" >
<template v-slot="scope">
<span>{{scope.row.val_new.name}}</span>
</template>
</el-table-column>
<el-table-column label="变更详情" width="500">
<template slot-scope="scope">
<el-tag v-for="(diff, index) in scope.row.difference" :key="index">
{{ resultsMap[diff.name] }}: {{ diff.old }} -> {{ diff.new }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-dialog>
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="公司名称" prop="company_name">
<el-input v-model="Content.company_name" placeholder="公司名称"/>
</el-form-item>
<el-form-item label="资质名称" prop="name">
<el-input v-model="Content.name" placeholder="资质名称"/>
</el-form-item>
<el-form-item label="资质类型">
<el-select
v-model="Content.quali_type"
placeholder="请选择"
>
<el-option
v-for="item in activateOptions"
:key="item.value"
:label="item.key"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="发证单位" prop="org">
<el-input v-model="Content.org" ></el-input>
</el-form-item>
<el-form-item label="发证日期" prop="org_date">
<el-date-picker
v-model="Content.org_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
>
</el-date-picker>
</el-form-item>
<el-form-item label="截至日期" prop="expiration_date">
<el-date-picker
v-model="Content.expiration_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
>
</el-date-picker>
</el-form-item>
<el-form-item label="资质范围" prop="scope">
<el-input v-model="Content.scope" ></el-input>
</el-form-item>
<el-form-item label="参数数量" prop="number" >
<el-input-number
v-model="Content.number"
:min="1"
controls-position="right"
:step="1"
/>
</el-form-item>
<el-form-item label="证书上传" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传证书</el-button>
</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>
<script>
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import { getAllQi, getQi, updateQi, createQi, delQi} from "@/api/qualificationInfo";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
company_name: "",
name: "",
quali_type: "",
org: "",
org_date: "",
expiration_date: "",
scope: "",
number: "",
cie_path: "",
change_date:""
};
export default {
components: { Pagination, Treeselect },
data(){
return {
form: {
company_name: "",
name: "",
quali_type: "",
org: "",
org_date: "",
expiration_date: "",
scope: "",
number: "",
cie_path: "",
change_date:""
},
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
Content: defaultContent,
typeOptions: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
dialogTableVisible:false,
exportForm: {
startDate: '', // 开始日期
endDate: '', // 结束日期
},
listQuery: {
page: 1,
page_size: 20,
},
tableData: {
count: 0
},
changeTableData:[],
dialogType: "new",
rule: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
},
filterOrgText: "",
// tableHeight: '300px',
activateOptions:[
{key:'国家级',value:'国家级'},
{key:'省级',value:'省级'}
],
ac_options:{'国家级':'国家级', '省级':'省级'},
resultsMap:{
"company_name":"公司名称",
"name":"资质名称",
"quali_type":"资质类型",
"org":"发证单位",
"org_date":"发证日期",
"expiration_date":"截至日期",
"scope":"资质范围",
"number":"参数数量",
"cie_path":"证书路径",
}
}
},
mounted() {
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
that.getList();
},
methods: {
//提交表单
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.dialogType == "new") {
this.addData();
} else {
this.editData();
}
} else {
console.log("error submit!!");
return false;
}
});
},
getTableList(){
this.getList()
},
handleExport() {
// if (!this.exportForm.startDate || !this.exportForm.endDate) {
// this.$message.error('请选择完整的起止日期!');
// return;}
getAllQi().then((response) => {
if (response.data) {
let filename = '资质情况统计表.xlsx';
let tableData = response.data;
const ws = XLSX.utils.json_to_sheet(tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
}else {
this.$message.error(data.message || '导出失败');
}
})
// this.showExportDialog = false;
.catch(error => {
this.$message.error('请求失败,请稍后再试');
console.error(error);
})
.finally(() => {
this.handleDialogClose();
});
},
handleDialogClose(){
this.showExportDialog = false;
this.startDate = '';
this.endDate = '';
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
} else {
window.open(file.response.data.path);
}
},
handleUpSuccess(res, file, filelist) {
this.Content.cie_path = res.data.path;
this.Content.file = res.data.id;
},
handleRemove(file, filelist) {
this.Content.file = null;
},
checkPermission,
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getAllQi(this.listQuery).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
}
);
},
handleAddFile() {
this.Content = Object.assign({}, defaultContent);
this.dialogType = "new";
this.dialogVisible = true;
this.fileList = []
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.Content = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit";
this.dialogVisible = true;
if (this.Content.file) {
this.fileList = [
{
name: this.Content.file_.name,
url: this.Content.file,
},
];
}
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await delQi(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
handleChange(scope) {
this.listLoading = true;
this.Content = Object.assign({}, scope.row); // copy obj
this.changeTableData =[];
this.dialogTableVisible=true;
getQi(scope.row.id).then((response) => {
if (response.data.count>0) {
this.changeTableData = response.data.results;
this.listLoading = false;
}else{
alert('无变更记录')
this.dialogTableVisible=false;
this.listLoading = false;
return
}
});
},
async confirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
updateQi(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createQi(this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
}
});
},
},
};
</script>
<style></style>

View File

@ -2,62 +2,174 @@
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" @click="handleImport">导入</el-button>
<el-button type="primary" @click="showExportDialog = true">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading" :data="tableData.results" :hieght="tableHeight" border fit stripe
<el-table v-loading="listLoading"
:data="tableData.results"
border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="文件名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
<el-table-column label="活动名称" prop="name"></el-table-column>
<el-table-column label="活动角色" prop="roles">
<template slot-scope="scope">
<span>{{ac_options[scope.row.roles]}}</span>
</template>
</el-table-column>
<el-table-column label="文件描述">
<template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column>
<el-table-column label="文件格式">
<template slot-scope="scope">{{ scope.row.file_.mime }}</template>
</el-table-column>
<el-table-column align="center" label="文件下载">
<el-table-column label="合作方" prop="collaborators"></el-table-column>
<el-table-column label="组织单位" prop="orgunits"></el-table-column>
<el-table-column label="活动地点" prop="place"></el-table-column>
<el-table-column label="活动参与单位数量" prop="participations"></el-table-column>
<el-table-column label="活动中发挥的作用" prop="function"></el-table-column>
<el-table-column label="活动收益(元)" prop="earnings" ></el-table-column>
<!-- <el-table-column align="center" label="文件下载">
<template slot-scope="scope" v-if="scope.row.file_.file">
<el-link :href="scope.row.file_.file" type="primary">下载</el-link>
</template>
</el-table-column>
</el-table-column> -->
<el-table-column align="center" label="操作" width="120px" fixed="right">
<template slot-scope="scope">
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="primary" size="small"
<el-link :disabled="!checkPermission(['infoCollect_QA'])" type="primary" size="small"
@click="handleEdit(scope)">编辑</el-link>
<el-divider direction="vertical"
v-if="checkPermission(['infoCollect_RS']) && checkPermission(['infoCollect_RS'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="danger" size="small"
v-if="checkPermission(['infoCollect_QA']) && checkPermission(['infoCollect_QA'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_QA'])" type="danger" size="small"
@click="handleDelete(scope)">删除</el-link>
</template>
</el-table-column>
<el-col :span="12" style="margin-top: 5px;">
<div class="cardHead">
<span class="cardTitle">质量活动</span>
</div>
</el-col>
</el-table>
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑评审' : '新增评审'">
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="评审名称" prop="name">
<el-input v-model="Content.name" placeholder="评审名称" />
<el-form-item label="名称" prop="name">
<el-input v-model="Content.name" placeholder="名称"/>
</el-form-item>
<el-form-item label="" prop="description">
<el-input type="textarea" :rows="2" v-model="Content.description" placeholder="评审描述" />
<el-form-item label="活动角色">
<el-select
v-model="Content.roles"
placeholder="请选择"
>
<el-option
v-for="item in activateOptions"
:key="item.value"
:label="item.key"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="评审文件" prop="file" v-if="dialogVisible">
<el-upload ref="upload" :action="upUrl" :on-preview="handlePreview" :on-success="handleUpSuccess"
:on-remove="handleRemove" :headers="upHeaders" :file-list="fileList" :limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<el-form-item label="合作方" prop="collaborators">
<el-input v-model="Content.collaborators" ></el-input>
</el-form-item>
<el-form-item label="组织单位" prop="orgunits">
<el-input v-model="Content.orgunits" ></el-input>
</el-form-item>
<el-form-item label="活动地点" prop="place">
<el-input v-model="Content.place"></el-input>
</el-form-item>
<el-form-item label="活动参与单位数量" prop="participations" >
<el-input-number
v-model="Content.participations"
:min="1"
controls-position="right"
:step="1"
step-strictly="true"
/>
<!-- <el-input v-model="Content.participations"></el-input> -->
</el-form-item>
<el-form-item label="活动中发挥的作用" prop="function" >
<el-input v-model="Content.function"></el-input>
</el-form-item>
<el-form-item label="活动收益(元)" prop="earnings" >
<el-input v-model="Content.earnings"></el-input>
</el-form-item>
<el-form-item label="活动时间" prop="activate_time">
<el-date-picker
v-model="Content.activate_time"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
>
</el-date-picker>
</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>
<el-dialog
:visible.sync="impDialogVisible"
title="导入"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="80px"
label-position="right"
:rules="rule"
>
<el-form-item label="下载模板" prop="path">
<a :href="downloadUrl">点击下载模板</a>
</el-form-item>
<el-form-item label="导入文件" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>``
</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>
</div>
</el-dialog>
<el-dialog
title="选择时间范围"
:visible.sync="showExportDialog"
width="50%"
@close="showExportDialog = false"
>
<el-form :model="exportForm" ref="exportForm" label-width="120px">
<el-form-item label="开始日期">
<el-date-picker
v-model="exportForm.startDate"
type="date"
placeholder="选择开始日期"
></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker
v-model="exportForm.endDate"
type="date"
placeholder="选择结束日期"
></el-date-picker>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showExportDialog = false">取消</el-button>
<el-button type="primary" @click="handleExport">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
@ -66,16 +178,39 @@ import Pagination from "@/components/Pagination"; // secondary package based on
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import { getRcs, createRc, del_activate, updateRc, getgetMyRcs, exportData, impData } from "@/api/qa";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
name: "",
description: "",
file: null
};
name: "",
roles: "",
collaborators: "",
orgunits: "",
place: "",
activate_time: "",
participations: "",
function: "",
earnings: ""
};
export default {
components: { Pagination, Treeselect },
data() {
data(){
return {
form: {
name: "",
roles: "",
collaborators: "",
orgunits: "",
place: "",
activate_time: "",
participations: "",
function: "",
earnings: "",
},
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
@ -83,6 +218,17 @@ export default {
typeOptions: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
saveLoading:false,
impDialogVisible: false,
downloadUrl: process.env.VUE_APP_BASE_API,
impForm:{
file:''
},
exportForm: {
startDate: '', // 开始日期
endDate: '', // 结束日期
},
listQuery: {
page: 1,
page_size: 20,
@ -95,30 +241,106 @@ export default {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
},
filterOrgText: "",
tableHeight: null,
};
},
computed: {},
watch: {
filterOrgText(val) {
this.$refs.tree.filter(val);
},
},
created() {
// tableHeight: '300px',
activateOptions:[
{key:'组织方',value:0},
{key:'参与方',value:1}
],
ac_options:{0:'组织方', 1:'参与方'}
}
},
mounted() {
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
console.log(that.tableHeight)
that.getList();
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
console.log(that.tableHeight)
that.getList();
},
},
methods: {
getTableList() {
this.getList();
//提交表单
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.dialogType == "new") {
this.addData();
} else {
this.editData();
}
} else {
console.log("error submit!!");
return false;
}
});
},
handleImport(){
this.impDialogVisible = true;
let file_url = '';
file_url = "media/default/质量相关活动统计表.xlsx";
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
getTableList(){
this.getList()
},
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
this.impForm.file = '';
},
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
impData(this.impForm).then(res=>{
console.log(this.impForm)
if(res.code>=200){
this.saveLoading = false;
this.$message.success("提交成功");
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
}
})
},
handleExport() {
if (!this.exportForm.startDate || !this.exportForm.endDate) {
this.$message.error('请选择完整的起止日期!');
return;}
console.log(this.exportForm)
exportData(this.exportForm).then((response) => {
if (response.data) {
let filename = '质量活动表.xlsx';
let tableData = response.data;
console.log(tableData)
const ws = XLSX.utils.json_to_sheet(tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
}else {
this.$message.error(data.message || '导出失败');
}
})
// this.showExportDialog = false;
.catch(error => {
this.$message.error('请求失败,请稍后再试');
console.error(error);
})
.finally(() => {
this.handleDialogClose();
});
},
handleDialogClose(){
this.showExportDialog = false;
this.startDate = '';
this.endDate = '';
},
handlePreview(file) {
if ("url" in file) {
@ -129,10 +351,12 @@ export default {
},
handleUpSuccess(res, file, filelist) {
this.Content.file = res.data.id;
this.impForm.file = res.data.path;
},
handleRemove(file, filelist) {
this.Content.file = null;
this.fileList = [];
this.impForm.file = '';
},
checkPermission,
@ -142,8 +366,18 @@ export default {
},
getList() {
},
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getgetMyRcs(this.listQuery).then((response) => {
console.log(response);
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
});
},
handleFilter() {
this.listQuery.page = 1;
@ -181,13 +415,16 @@ export default {
type: "error",
})
.then(async () => {
// await deleteDoc(scope.row.id);
// this.getList();
// this.$message.success("成功");
await del_activate(scope.row.id).then(() => {
this.getList();
this.$message.success("成功");
})
})
.catch((err) => {
console.error(err);
});
},
async confirm(form) {
this.$refs[form].validate((valid) => {
@ -195,9 +432,23 @@ export default {
const isEdit = this.dialogType === "edit";
if (isEdit) {
console.log(this.Content)
updateRc(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createRc(this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
@ -205,6 +456,7 @@ export default {
});
},
},
};
</script>
<style></style>
<style></style>

View File

@ -2,32 +2,41 @@
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" @click="handleImport">导入</el-button>
<el-button type="primary" @click="showExportDialog = true">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading" :data="tableData.results" :hieght="tableHeight" border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="文件名称">
<el-table-column label="项目名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="文件描述">
<template slot-scope="scope">{{ scope.row.description }}</template>
<el-table-column label="表彰名称">
<template slot-scope="scope">{{ scope.row.commendation_name }}</template>
</el-table-column>
<el-table-column label="文件格式">
<template slot-scope="scope">{{ scope.row.file_.mime }}</template>
<el-table-column label="获奖等级">
<template slot-scope="scope">{{ scope.row.Awards_level }}</template>
</el-table-column>
<el-table-column align="center" label="文件下载">
<template slot-scope="scope" v-if="scope.row.file_.file">
<el-link :href="scope.row.file_.file" type="primary">下载</el-link>
</template>
<el-table-column label="获奖单位">
<template slot-scope="scope">{{ scope.row.awardee_company }}</template>
</el-table-column>
<el-table-column label="获奖人">
<template slot-scope="scope">{{ scope.row.awardee_people }}</template>
</el-table-column>
<el-table-column label="颁奖单位">
<template slot-scope="scope">{{ scope.row.awarded_by }}</template>
</el-table-column>
<el-table-column label="获奖日期">
<template slot-scope="scope">{{ scope.row.awarded_date }}</template>
</el-table-column>
<el-table-column align="center" label="操作" width="120px" fixed="right">
<template slot-scope="scope">
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="primary" size="small"
<el-link :disabled="!checkPermission(['infoCollect_QC'])" type="primary" size="small"
@click="handleEdit(scope)">编辑</el-link>
<el-divider direction="vertical"
v-if="checkPermission(['infoCollect_RS']) && checkPermission(['infoCollect_RS'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="danger" size="small"
v-if="checkPermission(['infoCollect_QC']) && checkPermission(['infoCollect_QC'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_QC'])" type="danger" size="small"
@click="handleDelete(scope)">删除</el-link>
</template>
</el-table-column>
@ -35,29 +44,102 @@
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑评审' : '新增评审'">
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="评审名称" prop="name">
<el-input v-model="Content.name" placeholder="评审名称" />
<el-form-item label="项目名称" prop="name">
<el-input v-model="Content.name" placeholder="项目名称" />
</el-form-item>
<el-form-item label="" prop="description">
<el-input type="textarea" :rows="2" v-model="Content.description" placeholder="评审描述" />
<el-form-item label="表彰名称" prop="commendation_name">
<el-input v-model="Content.commendation_name" placeholder="表彰名称" />
</el-form-item>
<el-form-item label="评审文件" prop="file" v-if="dialogVisible">
<el-upload ref="upload" :action="upUrl" :on-preview="handlePreview" :on-success="handleUpSuccess"
:on-remove="handleRemove" :headers="upHeaders" :file-list="fileList" :limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<el-form-item label="获奖单位" prop="awardee_company">
<el-input v-model="Content.awardee_company" placeholder="获奖等级" />
</el-form-item>
<el-form-item label="获奖等级" prop="Awards_level">
<el-input v-model="Content.Awards_level" placeholder="获奖单位" />
</el-form-item>
<el-form-item label="获奖人" prop="awardee_people">
<el-input v-model="Content.awardee_people" placeholder="获奖人" />
</el-form-item>
<el-form-item label="颁奖单位" prop="awarded_by">
<el-input v-model="Content.awarded_by" placeholder="颁奖单位" />
</el-form-item>
<el-form-item label="获奖日期" prop="awarded_date">
<el-date-picker
v-model="Content.awarded_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
></el-date-picker>
</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>
<el-dialog
:visible.sync="impDialogVisible"
title="导入"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="80px"
label-position="right"
:rules="rule"
>
<el-form-item label="下载模板" prop="path">
<a :href="downloadUrl">点击下载模板</a>
</el-form-item>
<el-form-item label="导入文件" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>
</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>
</div>
</el-dialog>
<el-dialog
title="选择时间范围"
:visible.sync="showExportDialog"
width="50%"
@close="showExportDialog = false"
>
<el-form :model="exportForm" ref="exportForm" label-width="120px">
<el-form-item label="开始日期">
<el-date-picker
v-model="exportForm.startDate"
type="date"
placeholder="选择开始日期"
></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker
v-model="exportForm.endDate"
type="date"
placeholder="选择结束日期"
></el-date-picker>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showExportDialog = false">取消</el-button>
<el-button type="primary" @click="handleExport">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
@ -66,16 +148,32 @@ import Pagination from "@/components/Pagination"; // secondary package based on
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import { getRcs, createRc, del_commend, updateRc, getMyRcs, exportData, impData } from "@/api/qc";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
name: "",
description: "",
file: null
commendation_name: "",
Awards_level: "",
awardee_company: "",
awardee_people: "",
awarded_by: "",
awarded_date: ""
};
export default {
components: { Pagination, Treeselect },
data() {
data(){
return {
form: {
name: "",
commendation_name: "",
Awards_level: "",
awardee_company: "",
awardee_people: "",
awarded_by: "",
awarded_date: ""
},
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
@ -83,10 +181,17 @@ export default {
typeOptions: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
saveLoading:false,
impDialogVisible: false,
downloadUrl: process.env.VUE_APP_BASE_API,
listQuery: {
page: 1,
page_size: 20,
},
impForm:{
file:''
},
tableData: {
count: 0
},
@ -96,6 +201,10 @@ export default {
},
filterOrgText: "",
tableHeight: null,
exportForm: {
startDate: '', // 开始日期
endDate: '', // 结束日期
},
};
},
computed: {},
@ -120,6 +229,34 @@ export default {
getTableList() {
this.getList();
},
handleImport(){
this.impDialogVisible = true;
let file_url = '';
file_url = "media/default/质量相关表彰统计表.xlsx";
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
this.impForm.file = '';
},
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
impData(this.impForm).then(res=>{
console.log(this.impForm)
if(res.code>=200){
this.saveLoading = false;
this.$message.success("提交成功");
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
}
})
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
@ -129,10 +266,11 @@ export default {
},
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 = '';
},
checkPermission,
@ -142,9 +280,18 @@ export default {
},
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getMyRcs(this.listQuery).then((response) => {
console.log(response);
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
@ -181,23 +328,73 @@ export default {
type: "error",
})
.then(async () => {
// await deleteDoc(scope.row.id);
// this.getList();
// this.$message.success("成功");
await del_commend(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
handleExport() {
if (!this.exportForm.startDate || !this.exportForm.endDate) {
this.$message.error('请选择完整的起止日期!');
return;}
console.log(this.exportForm)
exportData(this.exportForm).then((response) => {
if (response.data) {
let filename = '质量表彰表.xlsx';
let tableData = response.data;
console.log(tableData)
const ws = XLSX.utils.json_to_sheet(tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
}else {
this.$message.error(data.message || '导出失败');
}
})
// this.showExportDialog = false;
.catch(error => {
this.$message.error('请求失败,请稍后再试');
console.error(error);
})
.finally(() => {
this.handleDialogClose();
});
},
handleDialogClose(){
this.showExportDialog = false;
this.startDate = '';
this.endDate = '';
},
async confirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
console.log(this.Content)
updateRc(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createRc(this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
@ -205,6 +402,7 @@ export default {
});
},
},
};
</script>
<style></style>

View File

@ -1,32 +1,47 @@
<template>
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleAddFile">新增</el-button>
<el-button type="primary" @click="handleImport">导入</el-button>
<el-button type="primary" @click="showExportDialog = true">导出</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading" :data="tableData.results" :hieght="tableHeight" border fit stripe
<el-table v-loading="listLoading" :data="tableData.results" :hieght="tableHeight" border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="文件名称">
<el-table-column label="公司名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="文件描述">
<template slot-scope="scope">{{ scope.row.description }}</template>
<el-table-column label="资质名称">
<template slot-scope="scope">{{ scope.row.quali_name }}</template>
</el-table-column>
<el-table-column label="文件格式">
<template slot-scope="scope">{{ scope.row.file_.mime }}</template>
</el-table-column>
<el-table-column align="center" label="文件下载">
<template slot-scope="scope" v-if="scope.row.file_.file">
<el-link :href="scope.row.file_.file" type="primary">下载</el-link>
<el-table-column label="评审方法">
<template slot-scope="scope">
<span>{{ cateOptions[scope.row.judging_method] }}</span>
</template>
</el-table-column>
<el-table-column label="评审类型">
<template slot-scope="scope">{{ typesOp[scope.row.judging_type] }}</template>
</el-table-column>
<el-table-column label="新增参数(个)">
<template slot-scope="scope">{{ scope.row.add_param }}</template>
</el-table-column>
<el-table-column label="评审日期">
<template slot-scope="scope">{{ scope.row.review_date }}</template>
</el-table-column>
<el-table-column label="现有场所数量">
<template slot-scope="scope">{{ scope.row.now_count }}</template>
</el-table-column>
<el-table-column label="新增场所数量">
<template slot-scope="scope">{{ scope.row.add_count }}</template>
</el-table-column>
<el-table-column align="center" label="操作" width="120px" fixed="right">
<template slot-scope="scope">
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="primary" size="small"
@click="handleEdit(scope)">编辑</el-link>
<el-divider direction="vertical"
v-if="checkPermission(['infoCollect_RS']) && checkPermission(['infoCollect_RS']) "></el-divider>
v-if="checkPermission(['infoCollect_RS']) && checkPermission(['infoCollect_RS'])"></el-divider>
<el-link :disabled="!checkPermission(['infoCollect_RS'])" type="danger" size="small"
@click="handleDelete(scope)">删除</el-link>
</template>
@ -35,29 +50,140 @@
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑评审' : '新增评审'">
<el-dialog :visible.sync="dialogVisible" :title="dialogType === 'edit' ? '编辑' : '新增'">
<el-form ref="Form" :model="Content" label-width="80px" label-position="right" :rules="rule">
<el-form-item label="评审名称" prop="name">
<el-input v-model="Content.name" placeholder="评审名称" />
<el-form-item label="公司名称" prop="name">
<el-input v-model="Content.name" placeholder="公司名称" />
</el-form-item>
<el-form-item label="" prop="description">
<el-input type="textarea" :rows="2" v-model="Content.description" placeholder="评审描述" />
<el-form-item label="资质名称" prop="quali">
<template>
<el-select v-model="Content.quali" placeholder="请选择" @visible-change="handleVisibleChange">
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-form-item>
<el-form-item label="评审文件" prop="file" v-if="dialogVisible">
<el-upload ref="upload" :action="upUrl" :on-preview="handlePreview" :on-success="handleUpSuccess"
:on-remove="handleRemove" :headers="upHeaders" :file-list="fileList" :limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<el-form-item label="评审方法" prop="judging_method">
<el-select v-model="Content.judging_method" placeholder="请选择">
<el-option
v-for="item in method_options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="评审类型" prop="judging_type">
<el-select v-model="Content.judging_type" placeholder="请选择">
<el-option
v-for="item in type_options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="新增参数(个)" prop="add_param">
<el-input v-model="Content.add_param" placeholder="新增参数" />
</el-form-item>
<el-form-item label="现有场所数量" prop="now_count">
<el-input-number
v-model="Content.now_count"
:min="0"
controls-position="right"
:step="1"
step-strictly="true"
/>
</el-form-item>
<el-form-item label="新增场所数量" prop="add_count">
<el-input-number
v-model="Content.add_count"
:min="0"
controls-position="right"
:step="1"
step-strictly="true"
/>
</el-form-item>
<el-form-item label="评审日期" prop="review_date">
<el-date-picker
v-model="Content.review_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width:50%"
></el-date-picker>
</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>
<el-dialog
:visible.sync="impDialogVisible"
title="导入"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="80px"
label-position="right"
:rules="rule"
>
<el-form-item label="下载模板" prop="path">
<a :href="downloadUrl">点击下载模板</a>
</el-form-item>
<el-form-item label="导入文件" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>
</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>
</div>
</el-dialog>
<el-dialog
title="选择时间范围"
:visible.sync="showExportDialog"
width="50%"
@close="showExportDialog = false"
>
<el-form :model="exportForm" ref="exportForm" label-width="120px">
<el-form-item label="开始日期">
<el-date-picker
v-model="exportForm.startDate"
type="date"
placeholder="选择开始日期"
></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker
v-model="exportForm.endDate"
type="date"
placeholder="选择结束日期"
></el-date-picker>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showExportDialog = false">取消</el-button>
<el-button type="primary" @click="handleExport">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
@ -66,16 +192,67 @@ import Pagination from "@/components/Pagination"; // secondary package based on
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { getDictList, getDictTypeList } from "@/api/dict";
import { createRs, delRs, updateRs, getMyRs, getRs, exportData, impData } from "@/api/review";
import { getMyQi} from "@/api/qualificationInfo";
import { upUrl, upHeaders } from "@/api/file";
import { saveAs } from 'file-saver';
import XLSX from 'xlsx';
const defaultContent = {
name: "",
description: "",
file: null
qualification_name: "",
quali:"",
judging_method: "",
judging_type: "",
add_param: "",
review_date: "",
now_count: "",
add_count: ""
};
export default {
components: { Pagination, Treeselect },
data() {
data(){
return {
form: {
name: "",
qualification_name: "",
judging_method: "",
judging_type: "",
add_param: "",
review_date: "",
now_count: "",
add_count: "",
quali:""
},
method_options: [{value: 0,label: '文审'},{value: 10,label: '现场'}],
cateOptions: {
0:'文审',
10:'现场'
},
typesOp: {
0:'初次',
10:'扩项',
20:'变更',
30:'复评',
40:'迁址'
},
selectedValue: '',
options: [],
isoading: false, //是否正在加载数据
type_options: [{
value: 0,
label: '初次'},
{
value: 10,
label: '扩项'},
{
value: 20,
label: '变更'},{
value: 30,
label: '复评'},
{
value: 40,
label: '迁址'}],
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
@ -83,6 +260,17 @@ export default {
typeOptions: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
saveLoading:false,
impDialogVisible: false,
downloadUrl: process.env.VUE_APP_BASE_API,
impForm:{
file:''
},
exportForm: {
startDate: '', // 开始日期
endDate: '', // 结束日期
},
listQuery: {
page: 1,
page_size: 20,
@ -95,7 +283,7 @@ export default {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
},
filterOrgText: "",
tableHeight:null,
tableHeight: null,
};
},
computed: {},
@ -103,23 +291,48 @@ export default {
filterOrgText(val) {
this.$refs.tree.filter(val);
},
},
created() {
},
mounted() {
let that = this;
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
console.log(that.tableHeight)
that.getList();
},
methods: {
getTableList() {
getTableList() {
this.getList();
},
handleImport(){
this.impDialogVisible = true;
let file_url = '';
file_url = "media/default/评审情况统计表.xlsx";
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
this.impForm.file = '';
},
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
impData(this.impForm).then(res=>{
console.log(this.impForm)
if(res.code>=200){
this.saveLoading = false;
this.$message.success("提交成功");
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
}
})
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
@ -129,22 +342,66 @@ export default {
},
handleUpSuccess(res, file, filelist) {
this.Content.file = res.data.id;
this.impForm.file = res.data.path;
},
handleRemove(file, filelist) {
this.Content.file = null;
this.fileList = [];
this.impForm.file = '';
},
checkPermission,
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
getMyRs(this.listQuery).then((response) => {
console.log(response);
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
});
},
handleExport() {
if (!this.exportForm.startDate || !this.exportForm.endDate) {
this.$message.error('请选择完整的起止日期!');
return;}
console.log(this.exportForm)
exportData(this.exportForm).then((response) => {
if (response.data) {
let filename = '评审情况表.xlsx';
let tableData = response.data;
console.log(tableData)
const ws = XLSX.utils.json_to_sheet(tableData.results);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
try {
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename);
} catch (e) { if(typeof console !== 'undefined') console.log(e, wbout); }
return;
}else {
this.$message.error(data.message || '导出失败');
}
})
// this.showExportDialog = false;
.catch(error => {
this.$message.error('请求失败,请稍后再试');
console.error(error);
})
.finally(() => {
this.handleDialogClose();
});
},
handleDialogClose(){
this.showExportDialog = false;
this.startDate = '';
this.endDate = '';
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
@ -181,9 +438,9 @@ export default {
type: "error",
})
.then(async () => {
// await deleteDoc(scope.row.id);
// this.getList();
// this.$message.success("成功");
await delRs(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
@ -194,18 +451,58 @@ export default {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
console.log(this.Content)
console.log(this.Content);
updateRs(this.Content.id, this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
} else {
createRs(this.Content).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
this.dialogVisible = false;
this.getTableList();
});
}
} else {
return false;
}
});
},
async handleVisibleChange(val) {
if (val) {
// 当下拉框变为可见时调用接口获取数据
if (!this.isLoading) {
this.isLoading = true; // 设置加载状态防止重复请求
try {
const response = await this.fetchOptions();
console.log(response,"ceshi");
} catch (error) {
// 处理错误
console.error(error);
} finally {
this.isLoading = false; // 无论成功还是失败都重置加载状态
}
}
}
},
fetchOptions() {
getMyQi().then((response) => {
console.log(response);
if (response.data) {
this.options = response.data.results;
}
});
}
},
};
</script>
<style>
</style>
<style></style>

View File

@ -0,0 +1,49 @@
<template>
<div>
<el-button class="my-button" type="primary" @click="handleSendEmail">发送邮件</el-button>
<div style="margin-top: 20px;">
<el-checkbox-group
v-model="form.names">
<el-checkbox label="材料报送二级单位"></el-checkbox>
<el-checkbox label="材料报送三级单位"></el-checkbox>
</el-checkbox-group>
</div>
</div>
</template>
<script>
import {sendMsg} from "@/api/msgInfo";
import checkPermission from "@/utils/permission";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
data() {
return {
form: {names: []}, // 用于存储复选框的选中状态
};
},
methods: {
checkPermission,
handleSendEmail() {
// 检查是否有复选框被选中
if (this.form.names.length > 0) {
// 这里应该是发送邮件的逻辑
alert('发送邮件,选中的选项是:' + this.form.names.join(', '));
// 你可以在这里调用API发送邮件并传入选中的选项作为参数
sendMsg(this.form).then(response => {
console.log(response);
});
} else {
alert('请至少选择一个选项');
}
}
}
};
</script>
<style scoped>
.my-button {
margin: 20px; /* 设置外边距 */
/* padding: 10px; /* 如果需要设置内边距可以取消注释这行 */
}
</style>

View File

@ -0,0 +1,206 @@
<template>
<div class="app-container">
<el-card class="elHeader">
<el-button type="primary" @click="handleImport">导入文件</el-button>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading"
:data="tableData.results"
border fit stripe
highlight-current-row>
<el-table-column type="index" width="50" />
<el-table-column label="PDF-名称" prop="pdf_name"></el-table-column>
<el-table-column label="EXCEL-名称" prop="excel_name"></el-table-column>
<el-table-column label="年度" prop="annual"></el-table-column>
<el-table-column label="创建时间" prop="create_time"></el-table-column>
<el-table-column label="状态" prop="status"></el-table-column>
<el-table-column align="center" label="操作" width="120px" fixed="right">
<template slot-scope="scope">
<el-link type="primary" @click="handleDownload(scope)">下载</el-link>
</template>
</el-table-column>
</el-table>
<pagination v-show="tableData.count > 0" :total="tableData.count" :page.sync="listQuery.page"
:limit.sync="listQuery.page_size" @pagination="getTableList" />
</el-card>
<el-dialog
:visible.sync="impDialogVisible"
title="导入"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="200px"
label-position="right"
:model = "form"
>
<el-form-item label="检验检测服务业统计-PDF" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>
<el-form-item label="选择年度">
<el-select v-model="form.annual" placeholder="请选择年度">
<el-option
v-for="year in years"
:key="year"
:label="year"
:value="year">
</el-option>
</el-select>
</el-form-item>
</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>
</div>
</el-dialog>
</div>
</template>
<script>
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { parsePDF, pdfAll, getpdf } from "@/api/report";
import { upUrl, upHeaders } from "@/api/file";
export default {
components: { Pagination, Treeselect },
data(){
return {
form:{
pdf_file:'',
annual:'',
},
isoading: false, //是否正在加载数据
upHeaders: upHeaders(),
upUrl: upUrl(),
fileList: [],
listLoading: false,
dialogVisible: false,
showExportDialog: false,
saveLoading:false,
impDialogVisible: false,
downloadUrl: process.env.VUE_APP_BASE_API,
excelUrl:'',
selectedYear:'',
tableData: {
count: 0
},
listQuery: {
page: 1,
page_size: 20,
},
years: Array.from({ length: 10 }, (_, i) => i + new Date().getFullYear() - 5)
};
},
mounted() {
let that = this;
let height1 = document.getElementsByClassName('app-main')[0].clientHeight;
let height2 = document.getElementsByClassName('elHeader')[0].clientHeight;
that.tableHeight = height1 - height2 - 70;
that.getList();
},
methods: {
handleImport(){
this.impDialogVisible = true;
let file_url = '';
file_url = "media/default/检验检测服务业统计数据上报任务.xlsx";
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
this.form.pdf_file = '';
},
getTableList(){
this.getList()
},
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
console.log(this.form)
parsePDF(this.form).then(res=>{
if(res.code>=200){
this.saveLoading = false;
// this.$message.success("解析成功");
this.fileList = [];
this.form.pdf_file = '';
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
this.fileList = [];
this.getList();
}
})
},
getList() {
this.listLoading = true;
this.tableData.results =[];
this.tableData.count =0;
pdfAll(this.listQuery).then((response) => {
if (response.data) {
this.tableData = response.data;
}
this.listLoading = false;
}
);
},
handleDownload(scope) {
this.listLoading = true;
this.dialogTableVisible=true;
getpdf(scope.row.id).then((response) => {
if (response.code>=200) {
this.downloadFile(response.data.parse_excel)
this.listLoading = false;
}else{
alert('服务器内部错误,请稍后再试')
this.dialogTableVisible=false;
this.listLoading = false;
return
}
});
},
downloadFile(url) {
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', '检验检测服务业统计.xlsx'); // 设置下载文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
handlePreview(file) {
if ("url" in file) {
window.open(file.url);
} else {
window.open(file.response.data.path);
}
},
handleUpSuccess(res, file, filelist) {
this.fileList = [file];
this.form.pdf_file = res.data.path;
},
handleRemove(file, filelist) {
this.Content.file = null;
this.fileList = [];
},
checkPermission,
}
};
</script>
<style></style>

View File

@ -73,13 +73,19 @@
<span v-if="scope.row.task2_">{{ cycleList[scope.row.task2_.cycle] }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center">
<el-table-column label="任务状态" align="center">
<template slot-scope="scope" v-if="scope.row.task2_">
<el-tag v-if="scope.row.task2_.state=='10'" type="warning">创建中</el-tag>
<el-tag v-else-if="scope.row.task2_.state=='20'" type="primary">进行中</el-tag>
<el-tag v-else type="success">已关闭</el-tag>
</template>
</el-table-column>
<el-table-column label="执行状态" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.count_up>=scope.row.count_all" type="success"">已完成</el-tag>
<el-tag v-else type="danger">未完成</el-tag>
</template>
</el-table-column>
<el-table-column label="应提交" prop="count_all" align="center"></el-table-column>
<el-table-column label="已提交" prop="count_up" align="center"></el-table-column>
</el-table>
@ -244,6 +250,7 @@
<el-button type="primary" size="mini" @click="handleImport('oinspect')">导入</el-button>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleCreate('oinspect')"></el-button>
<el-button type="primary" size="mini" @click="exportTableExcel('oinspect','外部监督检查信息')">导出</el-button>
<el-button type="danger" size="mini" :disabled="this.sels.length==0" @click="batchDelete('oinspect')">批量删除</el-button>
</div>
</div>
<el-table
@ -252,7 +259,9 @@
stripe
highlight-current-row
max-height="300"
>
row-key="id"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="检查类型">
<template slot-scope="scope">
@ -287,34 +296,14 @@
>删除</el-link>
</template>
</el-table-column>
</el-table>
<el-table
:data="oinspectList"
fit
stripe
highlight-current-row
id="oinspect"
style="display: none;"
>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="检查类型">
<template slot-scope="scope">
<span>{{ cateOptions[scope.row.cate] }}</span>
</template>
</el-table-column>
<el-table-column label="检查机构" prop="checker"></el-table-column>
<el-table-column label="检查日期" prop="date_inspect"></el-table-column>
<el-table-column label="检查结果" prop="result"></el-table-column>
<el-table-column label="结果日期" prop="result_date"></el-table-column>
<el-table-column label="检查通知单" prop="notification_form"></el-table-column>
<el-table-column label="结果告知单" prop="result_form"></el-table-column>
</el-table>
</el-table>
</el-card>
<saveoin-dialog
v-if="dialogOin"
ref="saveOinDialog"
@success="oinSaveSuccess"
@closed="dialogOin = false"
@close_dialog="close_dialog"
>
</saveoin-dialog>
</el-col>
@ -328,6 +317,7 @@
<el-button type="primary" size="mini" @click="handleImport('rc')">导入</el-button>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleCreate('rc')"></el-button>
<el-button type="primary" size="mini" @click="exportTableExcel('rcList','不准确报告/证书')">导出</el-button>
<el-button type="danger" size="mini" :disabled="this.sels.length==0" @click="batchDelete('rc')">批量删除</el-button>
</div>
</div>
<el-table
@ -336,7 +326,10 @@
stripe
highlight-current-row
max-height="300"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="报告/证书编号" prop="number"></el-table-column>
<el-table-column label="更改后报告/证书编号" prop="number_correct"></el-table-column>
@ -388,6 +381,7 @@
<el-button type="primary" size="mini" @click="handleImport('rc2')">导入</el-button>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleCreate('rc2')"></el-button>
<el-button type="primary" size="mini" @click="exportTableExcel('rc2','超期报告/证书')">导出</el-button>
<el-button type="danger" size="mini" :disabled="this.sels.length==0" @click="batchDelete('rc2')">批量删除</el-button>
</div>
</div>
<el-table
@ -396,7 +390,10 @@
stripe
highlight-current-row
max-height="300"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="超期报告/证书编号" prop="number"></el-table-column>
<el-table-column label="业务受理日期" prop="date_accept"></el-table-column>
@ -438,6 +435,7 @@
id="rc2"
style="display: none;"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="超期报告/证书编号" prop="number"></el-table-column>
<el-table-column label="业务受理日期" prop="date_accept"></el-table-column>
@ -463,6 +461,7 @@
<el-button type="primary" size="mini" @click="handleImport('pt')">导入</el-button>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleCreate('pt')"></el-button>
<el-button type="primary" size="mini" @click="exportTableExcel('ptList','能力验证结果')">导出</el-button>
<el-button type="danger" size="mini" :disabled="this.sels.length==0" @click="batchDelete('pt')">批量删除</el-button>
</div>
</div>
<el-table
@ -471,7 +470,10 @@
stripe
highlight-current-row
max-height="300"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="能力验证名称" prop="name"></el-table-column>
<el-table-column label="项目编号" prop="number"></el-table-column>
@ -575,6 +577,7 @@
<el-button type="primary" size="mini" @click="handleImport('complaint')">导入</el-button>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleCreate('complaint')"></el-button>
<el-button type="primary" size="mini" @click="exportTableExcel('complaint','客户投诉信息')">导出</el-button>
<el-button type="danger" size="mini" :disabled="this.sels.length==0" @click="batchDelete('complaint')">批量删除</el-button>
</div>
</div>
<el-table
@ -583,7 +586,9 @@
stripe
highlight-current-row
max-height="300"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="投诉人/机构" prop="complainant"></el-table-column>
<el-table-column label="投诉内容" prop="content"></el-table-column>
@ -623,6 +628,7 @@
id="complaint"
style="display: none;"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="投诉人/机构" prop="complainant"></el-table-column>
<el-table-column label="投诉内容" prop="content"></el-table-column>
@ -654,6 +660,7 @@
<el-button type="primary" size="mini" @click="handleImport('risk')">导入</el-button>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleCreate('risk')"></el-button>
<el-button type="primary" size="mini" @click="exportTableExcel('risk','风险识别信息')">导出</el-button>
<el-button type="danger" size="mini" :disabled="this.sels.length==0" @click="batchDelete('risk')">批量删除</el-button>
</div>
</div>
<el-table
@ -662,7 +669,9 @@
stripe
highlight-current-row
max-height="300"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="40"></el-table-column>
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="风险事实描述" prop="content"></el-table-column>
<el-table-column label="风险类别">
@ -774,7 +783,7 @@
import { getMyTask2Do,getPgoalDeptList,createPgoalDept,updatePgoalDept,noNum,
deletePgoalDept,saveNum,getPtList,deletePt,getRcList,deleteRc,getRiskList,
deleteRisk,getComplaintList,deleteComplaint,getOinspectList,deleteOinspect,
impPt,impRc,impOverdue,impRisk,impComplaint,impOinspect,getPgoalList} from "@/api/task";
impPt,impRc,impOverdue,impRisk,impComplaint,impOinspect,getPgoalList, delPts,delOinspects, delComplaints, delRcs, delRicks} from "@/api/task";
import { getOrgList, getSubOrgList } from "@/api/org";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination";
@ -798,7 +807,8 @@
data(){
return{
pageSizes:[10,20,50,100,1000,2000,5000],
downloadUrl: process.env.VUE_APP_BASE_API,
downloadUrl:'',
downloadSrc: process.env.VUE_APP_BASE_API,
leftHeight:null,
tableHeight:null,
upHeaders: upHeaders(),
@ -807,6 +817,7 @@
orgData:[],
subData:[],
fileList:[],
sels:[],
task2do:'',
taskYear:'',
deptName:'',
@ -926,6 +937,53 @@
return wbout
},
checkPermission,
handleSelectionChange(sels) {
this.sels = sels;
console.log("选中的值",sels.map((item) => item.id));
},
batchDelete(type) {
if (this.sels.length === 0) {
this.$message({
message: '请选择要删除的项',
type: 'warning'
});
return;
}else{
// 删除前的提示
this.$confirm("确认删除记录吗?", "提示", {
type: "warning",
}).then(() => {
let ids = this.sels.map((item) => item.id);
console.log("ids",ids);
this.deleteItems(type, ids);
this.getList()});
}
},
deleteItems(type, ids){
let item = {ids:ids}
if(type=='rc'){
delRcs(item).then(res=>{
this.getRcList();
})
}else if(type=='pt'){
console.log("删除的值",item);
delPts(item).then(res=>{
this.getPtList();
})
}else if(type=='risk'){
delRicks(item).then(res=>{
this.getRiskList();
})
}else if(type=='complaint'){
delComplaints(item).then(res=>{
this.getComplaintList();
})
}else if(type=='oinspect'){
delOinspects(item).then(res=>{
this.getOinspectList();
})
}
},
getOrgList() {
let that = this;
if (that.checkPermission(["record_confirm"])) {
@ -979,6 +1037,9 @@
})
that.getList();
},
close_dialog(){
this.dialogOin = false
},
resetFilter() {
this.listQuery = {
page: 1,
@ -1289,6 +1350,7 @@
},
handleImport(type){
this.impDialogVisible = true;
this.downloadUrl = '';
this.impType = type;
let file_url = '';
if(type=='rc'){
@ -1304,7 +1366,8 @@
}else if(type=='oinspect'){
file_url = "media/default/模板-外部监督检查信息登记表.xlsx";
}
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
let base_url = this.downloadSrc.substr(0, this.downloadSrc.length - 3);
console.log(base_url);
this.downloadUrl = base_url+file_url;
},
imporCconfirm(){

View File

@ -65,10 +65,6 @@
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<div style="text-align: right">
<el-button type="danger" @click="impDialogVisible = false">取消</el-button>
<el-button type="primary" @click="onSubmit()">确认</el-button>
</div>
</el-form-item>
<el-form-item label="结果告知单" prop="file">
<el-upload
@ -84,10 +80,11 @@
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
<div style="text-align: right">
<el-button type="danger" @click="impDialogVisible = false">取消</el-button>
<el-button type="danger" @click = "close_dialog()">取消</el-button>
<el-button type="primary" @click="onSubmit_result()">确认</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
@ -145,12 +142,19 @@
this.dialogVisible = true;
return this;
},
close_dialog(){
this.impDialogVisible = false;
this.$emit('close_dialog');
},
//表单注入数据
setData(data) {
console.log(data);
Object.assign(this.form, data);
if(data.notification_form){
this.fileList = [{ name: data.notification_form.split('/')[5], value: data.notification_form }];
}else if(data.result_form){
console.log(data.result_form.split('/'))
this.fileList_res = [{ name: data.result_form.split('/')[5], value: data.result_form }];
}
},

View File

@ -122,6 +122,7 @@ import { options } from "runjs";
edit: "能力验证结果编辑",
},
options:[
{key:'未出',value:0},
{key:'满意',value:10},
{key:'有问题',value:20},
{key:'不满意',value:30}

View File

@ -0,0 +1,521 @@
<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-upload"
@click="handleImport"
:loading="listLoading"
>批量导入</el-button>
<el-card style="margin-top: 10px">
<el-dialog
:visible.sync="impDialogVisible"
title="批量新增用户"
:close-on-click-modal="false"
>
<el-form
ref="Form"
label-width="80px"
label-position="right"
>
<el-form-item label="下载模板" prop="path">
<a :href="downloadUrl">点击下载模板</a>
</el-form-item>
<el-form-item label="导入文件" prop="file">
<el-upload
ref="upload"
:action="upUrl"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</el-form-item>
</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>
</div>
</el-dialog>
</el-card>
</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>
</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 {impData} from "@/api/userexam";
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: [],
saveLoading:false,
fileList: [],
impDialogVisible: false,
listLoading: true,
downloadUrl: process.env.VUE_APP_BASE_API,
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,
submitUpload() {
this.$refs.upload.submit();
this.impDialogVisible = false;
this.saveLoading = true;
impData(this.impForm).then(res=>{
console.log(this.impForm)
if(res.code>=200){
this.saveLoading = false;
this.$message.success("提交成功");
this.getList();
}else{
this.$message.error(res.message);
this.saveLoading = false;
}
})
},
handleImport(){
this.impDialogVisible = true;
let file_url = '';
file_url = "media/default/用户表.xlsx";
let base_url = this.downloadUrl.substr(0, this.downloadUrl.length - 3);
this.downloadUrl = base_url+file_url;
},
handleAvatarSuccess(res, file) {
if (res.code >= 200) {
this.user.avatar = res.data.path;
} else {
this.$message.error("头像上传失败!");
}
},
close_dialog(){
this.impDialogVisible = false;
this.fileList = [];
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) {
if ("url" in file) {
window.open(file.url);
} else {
window.open(file.response.data.path);
}
},
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>

View File

@ -49,10 +49,10 @@
</el-table-column>
<el-table-column label="资料名称" min-width="110">
<template slot-scope="scope" v-if="scope.row.file_.file">
<a v-if="scope.row.file_.file.indexOf('.pdf')>-1 " :href="`/static/build/generic/web/viewer.html?file=${scope.row.file_.file}`" target="view_window" class="flv">
<!-- <a v-if="scope.row.file_.file.indexOf('.pdf')>-1 " :href="`/static/build/generic/web/viewer.html?file=${scope.row.file_.file}`" target="view_window" class="flv">
<el-link type="primary">{{ scope.row.name }}</el-link>
</a>
<el-link v-else :href="scope.row.file_.file" type="primary">{{ scope.row.name }}</el-link>
</a> -->
<el-link :href="scope.row.file_.file" type="primary">{{ scope.row.name }}</el-link>
</template>
<template slot-scope="scope" v-else>{{ scope.row.name }}</template>
</el-table-column>

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-05-30 05:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('exam', '0001_initial'),
('edu', '0005_certificate_培训结束日期'),
]
operations = [
migrations.AddField(
model_name='certificate',
name='examrecord',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='exam.examrecord'),
),
]

View File

@ -1,5 +1,6 @@
from django.db import models
from apps.system.models import CommonADModel, CommonAModel
# Create your models here.
@ -28,3 +29,4 @@ class Certificate(CommonADModel):
培训结束日期 = 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)

View File

@ -1,4 +1,4 @@
# Generated by Django 3.0.5 on 2022-10-31 02:26
# Generated by Django 3.2.12 on 2024-05-27 08:35
from django.conf import settings
import django.contrib.postgres.fields.jsonb
@ -12,10 +12,77 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('edu', '0005_certificate_培训结束日期'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0023_alter_user_first_name'),
]
operations = [
migrations.CreateModel(
name='AnswerDetail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('total_score', models.FloatField(default=0, verbose_name='该题满分')),
('user_answer', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)),
('score', models.FloatField(default=0, verbose_name='本题得分')),
('is_right', models.BooleanField(default=False, verbose_name='是否正确')),
],
options={
'verbose_name': '答题记录',
'verbose_name_plural': '答题记录',
},
),
migrations.CreateModel(
name='Exam',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('code', models.CharField(blank=True, max_length=100, null=True, unique=True, verbose_name='考试编号')),
('name', models.CharField(max_length=100, verbose_name='名称')),
('place', models.CharField(blank=True, max_length=100, null=True, verbose_name='考试地点')),
('open_time', models.DateTimeField(blank=True, null=True, verbose_name='开启时间')),
('close_time', models.DateTimeField(blank=True, null=True, verbose_name='关闭时间')),
('proctor_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='监考人姓名')),
('proctor_phone', models.CharField(blank=True, max_length=100, null=True, verbose_name='监考人联系方式')),
('chance', models.IntegerField(default=3, verbose_name='考试机会')),
('certificate', models.BooleanField(default=False, verbose_name='是否生成证书')),
('is_open', models.BooleanField(default=True, verbose_name='是否公开')),
('course_name', models.ManyToManyField(blank=True, to='edu.Course')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='exam_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Paper',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=200, verbose_name='名称')),
('limit', models.IntegerField(default=0, verbose_name='限时(分钟)')),
('total_score', models.FloatField(default=0, verbose_name='满分')),
('pass_score', models.FloatField(default=0, verbose_name='通过分数')),
('danxuan_count', models.IntegerField(default=0, verbose_name='单选数量')),
('danxuan_score', models.FloatField(default=2, verbose_name='单选分数')),
('duoxuan_count', models.IntegerField(default=0, verbose_name='多选数量')),
('duoxuan_score', models.FloatField(default=4, verbose_name='多选分数')),
('panduan_count', models.IntegerField(default=0, verbose_name='判断数量')),
('panduan_score', models.FloatField(default=2, verbose_name='判断分数')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paper_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '押题卷',
'verbose_name_plural': '押题卷',
},
),
migrations.CreateModel(
name='Questioncat',
fields=[
@ -25,7 +92,7 @@ class Migration(migrations.Migration):
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=200, verbose_name='名称')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='questioncat_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='questioncat_parent', to='exam.Questioncat', verbose_name='')),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='questioncat_parent', to='exam.questioncat', verbose_name='')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='questioncat_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
@ -50,7 +117,7 @@ class Migration(migrations.Migration):
('enabled', models.BooleanField(default=False, verbose_name='是否启用')),
('year', models.IntegerField(blank=True, null=True, verbose_name='真题年份')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='question_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('questioncat', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='questioncat', to='exam.Questioncat', verbose_name='所属题库')),
('questioncat', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='questioncat', to='exam.questioncat', verbose_name='所属题库')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='question_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
@ -58,4 +125,88 @@ class Migration(migrations.Migration):
'verbose_name_plural': '题目',
},
),
migrations.CreateModel(
name='PaperQuestion',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('total_score', models.FloatField(default=0, verbose_name='单题满分')),
('paper', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.paper', verbose_name='试卷')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.question', verbose_name='试题')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='paper',
name='questions',
field=models.ManyToManyField(through='exam.PaperQuestion', to='exam.Question'),
),
migrations.AddField(
model_name='paper',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paper_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.CreateModel(
name='ExamRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=200, verbose_name='名称')),
('type', models.CharField(choices=[('自助模考', '自助模考'), ('押卷模考', '押卷模考'), ('正式考试', '正式考试')], default='自助模考', max_length=50, verbose_name='考试类型')),
('limit', models.IntegerField(default=0, verbose_name='限时(分钟)')),
('total_score', models.FloatField(default=0, verbose_name='总分')),
('score', models.FloatField(default=0, verbose_name='得分')),
('took', models.IntegerField(default=0, verbose_name='耗时(秒)')),
('start_time', models.DateTimeField(verbose_name='开始答题时间')),
('end_time', models.DateTimeField(blank=True, null=True, verbose_name='结束答题时间')),
('is_pass', models.BooleanField(default=True, verbose_name='是否通过')),
('is_submited', models.BooleanField(default=False)),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='examrecord_belong_dept', to='system.organization', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='examrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('detail', models.ManyToManyField(through='exam.AnswerDetail', to='exam.Question', verbose_name='答题记录')),
('exam', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='exam.exam', verbose_name='关联的正式考试')),
('paper', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='exam.paper', verbose_name='所用试卷')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='examrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '考试记录',
'verbose_name_plural': '考试记录',
},
),
migrations.AddField(
model_name='exam',
name='paper',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='exam.paper', verbose_name='使用的试卷'),
),
migrations.AddField(
model_name='exam',
name='participant_dep',
field=models.ManyToManyField(blank=True, null=True, related_name='exam_dep', to='system.Organization', verbose_name='考试公司'),
),
migrations.AddField(
model_name='exam',
name='participant_user',
field=models.ManyToManyField(blank=True, null=True, related_name='exam_user', to=settings.AUTH_USER_MODEL, verbose_name='考试人员'),
),
migrations.AddField(
model_name='exam',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='exam_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='answerdetail',
name='examrecord',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.examrecord'),
),
migrations.AddField(
model_name='answerdetail',
name='question',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.question'),
),
]

View File

@ -1,153 +0,0 @@
# Generated by Django 3.0.5 on 2022-11-07 05:56
from django.conf import settings
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('exam', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AnswerDetail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('user_answer', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)),
('score', models.FloatField(default=0, verbose_name='本题得分')),
('is_right', models.BooleanField(default=False, verbose_name='是否正确')),
],
options={
'verbose_name': '答题记录',
'verbose_name_plural': '答题记录',
},
),
migrations.CreateModel(
name='Exam',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('code', models.CharField(blank=True, max_length=100, null=True, unique=True, verbose_name='考试编号')),
('name', models.CharField(max_length=100, verbose_name='名称')),
('place', models.CharField(blank=True, max_length=100, null=True, verbose_name='考试地点')),
('open_time', models.DateTimeField(blank=True, null=True, verbose_name='开启时间')),
('close_time', models.DateTimeField(blank=True, null=True, verbose_name='关闭时间')),
('proctor_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='监考人姓名')),
('proctor_phone', models.CharField(blank=True, max_length=100, null=True, verbose_name='监考人联系方式')),
('chance', models.IntegerField(default=3, verbose_name='考试机会')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='exam_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Paper',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=200, verbose_name='名称')),
('limit', models.IntegerField(default=0, verbose_name='限时(分钟)')),
('total_score', models.FloatField(default=0, verbose_name='满分')),
('pass_score', models.FloatField(default=0, verbose_name='通过分数')),
('danxuan_count', models.IntegerField(default=0, verbose_name='单选数量')),
('danxuan_score', models.FloatField(default=2, verbose_name='单选分数')),
('duoxuan_count', models.IntegerField(default=0, verbose_name='多选数量')),
('duoxuan_score', models.FloatField(default=4, verbose_name='多选分数')),
('panduan_count', models.IntegerField(default=0, verbose_name='判断数量')),
('panduan_score', models.FloatField(default=2, verbose_name='判断分数')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paper_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '押题卷',
'verbose_name_plural': '押题卷',
},
),
migrations.CreateModel(
name='PaperQuestion',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('total_score', models.FloatField(default=0, verbose_name='单题满分')),
('sort', models.PositiveSmallIntegerField(default=1)),
('paper', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.Paper', verbose_name='试卷')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.Question', verbose_name='试题')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='paper',
name='questions',
field=models.ManyToManyField(through='exam.PaperQuestion', to='exam.Question'),
),
migrations.AddField(
model_name='paper',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paper_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.CreateModel(
name='ExamRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=200, verbose_name='名称')),
('type', models.CharField(choices=[('自助模考', '自助模考'), ('押卷模考', '押卷模考'), ('正式考试', '正式考试')], default='自助模考', max_length=50, verbose_name='考试类型')),
('limit', models.IntegerField(default=0, verbose_name='限时(分钟)')),
('total_score', models.FloatField(default=0, verbose_name='总分')),
('score', models.FloatField(default=0, verbose_name='得分')),
('took', models.IntegerField(default=0, verbose_name='耗时(秒)')),
('start_time', models.DateTimeField(verbose_name='开始答题时间')),
('end_time', models.DateTimeField(verbose_name='结束答题时间')),
('is_pass', models.BooleanField(default=True, verbose_name='是否通过')),
('questions', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, verbose_name='下发的题目列表')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='examrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('detail', models.ManyToManyField(through='exam.AnswerDetail', to='exam.Question', verbose_name='答题记录')),
('exam', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='exam.Exam', verbose_name='关联的正式考试')),
('paper', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='exam.Paper', verbose_name='所用试卷')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='examrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '考试记录',
'verbose_name_plural': '考试记录',
},
),
migrations.AddField(
model_name='exam',
name='paper',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='exam.Paper', verbose_name='使用的试卷'),
),
migrations.AddField(
model_name='exam',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='exam_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='answerdetail',
name='examrecord',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.ExamRecord'),
),
migrations.AddField(
model_name='answerdetail',
name='question',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.Question'),
),
]

View File

@ -1,31 +0,0 @@
# Generated by Django 3.0.5 on 2022-11-08 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('exam', '0002_auto_20221107_1356'),
]
operations = [
migrations.RemoveField(
model_name='examrecord',
name='questions',
),
migrations.RemoveField(
model_name='paperquestion',
name='sort',
),
migrations.AddField(
model_name='answerdetail',
name='total_score',
field=models.FloatField(default=0, verbose_name='该题满分'),
),
migrations.AddField(
model_name='examrecord',
name='is_submited',
field=models.BooleanField(default=False),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.0.5 on 2022-11-14 03:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('exam', '0003_auto_20221108_0901'),
]
operations = [
migrations.AlterField(
model_name='examrecord',
name='end_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='结束答题时间'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.0.5 on 2022-11-22 02:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('exam', '0004_auto_20221114_1108'),
]
operations = [
migrations.AddField(
model_name='exam',
name='is_open',
field=models.BooleanField(default=True, verbose_name='是否公开'),
),
]

View File

@ -1,8 +1,9 @@
from django.db import models
from apps.system.models import CommonAModel
from apps.system.models import CommonAModel, CommonBModel
from django.contrib.postgres.fields import JSONField
from utils.model import BaseModel
from apps.edu.models import Course
from apps.system.models import User, Organization
# Create your models here.
class Questioncat(CommonAModel):
name = models.CharField(max_length=200, verbose_name='名称')
@ -40,6 +41,7 @@ class Question(CommonAModel):
resolution = models.TextField(verbose_name='解析', blank=True)
enabled = models.BooleanField('是否启用', default=False)
year = models.IntegerField('真题年份', null=True, blank=True)
class Meta:
verbose_name = '题目'
verbose_name_plural = verbose_name
@ -87,12 +89,17 @@ class Exam(CommonAModel):
proctor_phone = models.CharField('监考人联系方式', max_length=100, null=True, blank=True)
chance = models.IntegerField('考试机会', default=3)
paper = models.ForeignKey(Paper, verbose_name='使用的试卷', on_delete=models.CASCADE, null=True, blank=True)
certificate = models.BooleanField('是否生成证书', default=False)
course_name = models.ManyToManyField(Course, blank=True)
participant_dep = models.ManyToManyField(Organization, verbose_name='考试公司', related_name='exam_dep',null=True, blank=True)
participant_user = models.ManyToManyField(User, verbose_name='考试人员', related_name='exam_user', null=True, blank=True)
is_open = models.BooleanField('是否公开', default=True)
def __str__(self):
return self.name
class ExamRecord(CommonAModel):
class ExamRecord(CommonBModel):
'''
考试记录表
'''

View File

@ -0,0 +1,99 @@
from docx import Document
from openpyxl import load_workbook
import re
QUES_CLASS = '安全领域'
OPTION_LIST=[
[r"A\s*(\S+)", "D"],
[r"B\s*(\S+)", "E"],
[r"C\s*(\S+)", "F"],
[r"D\s*(\S+)", "G"],
[r"E\s*(\S+)", "H"],
[r"F\s*(\S+)", "I"],
]
def fill_excel(matches, excel_path, local):
wb = load_workbook(excel_path)
ws = wb.active
if matches:
ws[local] = matches
wb.save(excel_path)
def match_text(text, pattern):
matches = re.search(pattern, text)
if matches:
results = matches.group(1)
return results
return ''
# 解析word文档
def interpret_text(start:int, excel_path:str, doc_path:str, field=None):
wordfile = Document(doc_path)
correct_dict = {}
option_dict = {}
question_type = {}
ques_text = {}
for index, p in enumerate(wordfile.paragraphs):
correct_answer = match_text(p.text, r"正确答案:\s*(\S+)") # 匹配正确答案
if correct_answer:
correct_dict.setdefault("correct_answer", []).append(correct_answer)
for e in OPTION_LIST: # 匹配选项
result = match_text(p.text, e[0])
if result:
option_dict.setdefault(e[1], []).append(result)
# 题目类型
# fill_excel(QUES_CLASS, excel_path, 'B'+str(index+start))
if p.text[:1]=='' and p.text[4:5]=='':
q_type = p.text[1:3] # 题目类型
question_type.setdefault("question_type", []).append(q_type)
if p.text[-2]=='': #(3分)
question_text = p.text[5:-4].strip()
result = bool(re.match(r'\d+、', question_text)) # 处理题目前的序号
if result:
question = re.sub(r'\d+、', '',question_text)
ques_text.setdefault("question_text", []).append(question)
dict_list = [correct_dict,ques_text,option_dict,question_type]
for d in dict_list:
for key,value in d.items():
if key == "correct_answer":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'J'+str(start+v))
elif key == "question_type":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'A'+str(start+v))
if field:
fill_excel(field, excel_path, 'B'+str(start+v))
else:
fill_excel(QUES_CLASS, excel_path, 'B'+str(start+v))
elif key == "question_text":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'C'+str(start+v))
elif key == "D":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'D'+str(start+v))
elif key == "E":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'E'+str(start+v))
elif key == "F":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'F'+str(start+v))
elif key == "G":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'G'+str(start+v))
elif key == "H":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'H'+str(start+v))
elif key == "I":
for v in range(len(value)):
fill_excel(value[v], excel_path, 'I'+str(start+v))
return 'OK'
if __name__ == '__main__':
doc_path = "C:\code\data\\test.docx"
excel_path = "C:\code\data\question.xlsx"
interpret_text(3, excel_path, doc_path)

View File

@ -2,6 +2,9 @@ from rest_framework.serializers import ModelSerializer, CharField, Serializer, S
from rest_framework import serializers
from apps.exam.models import Question, Questioncat, Paper, Exam, PaperQuestion, ExamRecord, AnswerDetail
from apps.edu.serializers import CourseSerializer
from apps.system.serializers import OrganizationSerializer, UserListSerializer
from apps.system.models import Organization, User
class QuestioncatSerializer(ModelSerializer):
@ -72,20 +75,37 @@ class PaperDetailSerializer(ModelSerializer):
class ExamCreateUpdateSerializer(ModelSerializer):
class Meta:
model = Exam
fields = ['name', 'place', 'open_time',
'close_time', 'proctor_name', 'proctor_phone', 'chance', 'paper']
fields = ['name', 'place', 'open_time','course_name', 'certificate',
'close_time', 'proctor_name', 'proctor_phone', 'chance', 'paper', 'participant_dep', 'participant_user']
class ExamListSerializer(ModelSerializer):
create_by_name = CharField(source='create_by.name', read_only=True)
paper_ = PaperSerializer(source='paper', read_only=True)
course_ = CourseSerializer(source='course_name', read_only=True)
user_count = serializers.SerializerMethodField()
submit_count = serializers.SerializerMethodField()
class Meta:
model = Exam
fields = '__all__'
#参与考试的 数量
def get_user_count(self, obj):
total_user_count = 0
for p in obj.participant_dep.all():
user_count = User.objects.filter(dept_id = p.id).count()
total_user_count += user_count
return total_user_count+obj.participant_user.count()
# 已经提交的数量
def get_submit_count(self, obj):
exam = ExamRecord.objects.select_related('exam').filter(exam=obj, is_submited=True)
submit_user = len(set([i.create_by for i in exam]))
return submit_user
class ExamDetailSerializer(ModelSerializer):
create_by_name = CharField(source='create_by.name', read_only=True)
@ -96,6 +116,7 @@ class ExamDetailSerializer(ModelSerializer):
fields = '__all__'
class ExamAttendSerializer(Serializer):
code = CharField(label="考试编号")
@ -107,7 +128,10 @@ class ExamRecordListSerializer(serializers.ModelSerializer):
took_format = serializers.SerializerMethodField()
create_by_name = serializers.CharField(
source='create_by.name', read_only=True)
exam_name = serializers.CharField(
source='exam.name', read_only=True)
blong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
class Meta:
model = ExamRecord
exclude = ('detail',)

View File

@ -19,9 +19,15 @@ from datetime import datetime
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 apps.exam.parse_word import interpret_text
import os
import shutil
# Create your views here.
EXCEL_PATH = os.path.join(settings.BASE_DIR, "media/default/question.xlsx")
def enctry(s):
k = 'ez9z3a4m*$%srn9ve_t71yd!v+&xn9@0k(e(+l6#g1h=e5i4da'
@ -59,7 +65,8 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
serializer_class = QuestionSerializer
filterset_fields = ['level', 'type', 'year']
search_fields = ['name', 'options', 'resolution']
@action(methods=['get'], detail=False,
url_path='export', url_name='export_question', perms_map=[{'get': '*'}], serializer_class=Serializer)
def export_question(self, request):
@ -194,7 +201,7 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
right = 'A'
else:
right = 'B'
if Question.objects.filter(type='判断', name=name, is_delete=0, options={'A': '', 'B': ''}, questioncat=cateobj).exists():
if Question.objects.filter(type='判断', name=name, is_deleted=0, options={'A': '', 'B': ''}, questioncat=cateobj).exists():
notinlist.append(i)
else:
obj = Question()
@ -263,6 +270,146 @@ class PaperViewSet(ModelViewSet):
PaperQuestion.objects.bulk_create(q_list)
return Response()
@action(methods=['post'], detail=False, perms_map={'post': 'question'}, serializer_class=Serializer)
def upload_paper(self, request):
doc_path = request.data.get('doc_path')
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)
if not os.path.exists(question_excel):
os.makedirs(question_excel)
shutil.copy(excel_path, question_excel)
excel_save_path = os.path.join(question_excel, os.path.basename(EXCEL_PATH))
# 解析word文档到excel
res = interpret_text(3, excel_save_path, doc_path, question_type)
ids = []
if res:
# 将excel 入库并返回 queston_id
wb = load_workbook(excel_save_path)
sheet = wb.worksheets[0]
leveldict = {'': '', '': '', '': ''}
# 验证文件内容
if sheet['a2'].value != '题目类型':
return Response({"error": "类型列错误!"})
if sheet['b2'].value != '分类':
return Response({"error": "分类列错误!"})
if sheet['c2'].value != '题目':
return Response({"error": "题目列错误!"})
questioncatdict = {}
questioncats = Questioncat.objects.all()
for i in questioncats:
questioncatdict[i.name] = i.id
i = 3
while sheet['c'+str(i)].value:
type = sheet['a'+str(i)].value.replace(' ', '')
questioncat = sheet['b'+str(i)].value
if questioncat:
questioncat = questioncat.replace(' ', '')
else:
return Response(str(i)+'行没有分类', status=400)
name = sheet['c'+str(i)].value
answer = {}
if sheet['d'+str(i)].value:
answer['A'] = sheet['d'+str(i)].value
if sheet['e'+str(i)].value:
answer['B'] = sheet['e'+str(i)].value
if sheet['f'+str(i)].value:
answer['C'] = sheet['f'+str(i)].value
if sheet['g'+str(i)].value:
answer['D'] = sheet['g'+str(i)].value
if sheet['h'+str(i)].value:
answer['E'] = sheet['h'+str(i)].value
if sheet['i'+str(i)].value:
answer['F'] = sheet['i'+str(i)].value
right = sheet['j'+str(i)].value
if right:
right = right.replace(' ', '')
else:
return Response(str(i)+'行没有答案', status=400)
resolution = sheet['k'+str(i)].value
level = sheet['l'+str(i)].value
year = sheet['m' + str(i)].value
if level:
level = level.replace(' ', '')
cateobj = None
if questioncat not in questioncatdict:
return Response(str(i)+"行不存在分类("+questioncat+")!请先新建", status=400)
else:
cateobj = Questioncat.objects.get(
id=questioncatdict[questioncat])
if Question.objects.filter(name=name, is_deleted=0, questioncat=cateobj).exists():
ids.append(Question.objects.get(name=name, is_deleted=0, questioncat=cateobj).id)
i = i + 1
continue
if type == '单选':
if right in ['A', 'B', 'C', 'D', 'E', 'F']:
obj = Question()
obj.type = '单选'
if cateobj:
obj.questioncat = cateobj
obj.name = name
obj.options = answer
obj.right = right
obj.create_by = request.user
obj.resolution = resolution if resolution else ''
obj.year = year if year else None
if level in leveldict:
obj.level = leveldict[level]
else:
obj.level = ''
obj.save()
ids.append(obj.id)
elif type == '多选':
obj = Question()
rights = list(right.strip())
right = [x for x in rights if x != ',']
obj.type = '多选'
obj.questioncat = cateobj
obj.name = name
obj.options = answer
obj.right = right
obj.create_by = request.user
obj.resolution = resolution if resolution else ''
obj.year = year if year else None
if level in leveldict:
obj.level = leveldict[level]
else:
obj.level = ''
obj.save()
ids.append(obj.id)
elif type == '判断':
if right == 'A' or right == '' or right == '正确':
right = 'A'
else:
right = 'B'
obj = Question()
obj.type = '判断'
obj.questioncat = cateobj
obj.name = name
obj.options = {'A': '', 'B': ''}
obj.right = right
obj.create_by = request.user
obj.resolution = resolution if resolution else ''
obj.year = year if year else None
if level in leveldict:
obj.level = leveldict[level]
else:
obj.level = ''
obj.save()
ids.append(obj.id)
i = i + 1
else:
raise ParseError('excel解析失败')
if ids:
questions = Question.objects.filter(pk__in=ids)
Serializer = QuestionSerializer(questions, many=True)
Serializer.data
return Response(Serializer.data, status=200)
class ExamViewSet(CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post':'exam', 'put':'exam', 'delete':'exam'}
@ -295,6 +442,15 @@ class ExamViewSet(CreateUpdateCustomMixin, ModelViewSet):
开始考试具体题目信息
"""
exam = self.get_object()
# 查询本次考试对应哪些人
participants = exam.participant_user.all()
participants_ids = [i.id for i in participants]
dep = exam.participant_dep.all()
dep_ids = [i.id for i in dep]
if request.user.id in participants_ids or request.user.dept.id in dep_ids:
pass
else:
raise ParseError('不在考试人员范围内')
now = timezone.now()
if now < exam.open_time or now > exam.close_time:
raise ParseError('不在考试时间范围')
@ -335,11 +491,25 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
perms_map = {'get': '*', 'post': '*', 'delete':'examrecord'}
queryset = ExamRecord.objects.select_related('create_by')
serializer_class = ExamRecordListSerializer
ordering_fields = ['create_time', 'score', 'took', 'update_time']
ordering_fields = ['create_time', 'score', 'took', 'update_time', 'belong_dept']
ordering = ['-update_time']
search_fields = ('create_by__name', 'create_by__username')
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('ctc_manager', self.request.user):
return qs
# 如果是部门管理员,只能看到自己部门下的考试记录
# 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
@ -380,11 +550,15 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
提交答卷
'''
er = self.get_object()
try:
er = ExamRecord.objects.get(id=pk)
except Exception as e:
raise ParseError(e)
now = timezone.now()
if er.create_by != request.user:
raise ParseError('提交人有误')
exam = er.exam
paper = er.paper
if not exam:
raise ParseError('暂不支持')
if now > exam.close_time + timedelta(minutes=30):
@ -412,10 +586,31 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
except Exception as e:
raise ParseError('判卷失败, 请检查试卷:' + str(e))
er.score = total_score
if er.score > 0.6*er.total_score:
# if er.score > 0.6*er.total_score:
if er.score >= paper.pass_score:
er.is_pass = True
# 如果是自动发证
if exam.certificate:
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')
data_dict = {
'姓名': request.user.name,
'证书编号': 'CTCZL'+ current_date,
'单位名称': request.user.dept.name,
'所属单位': '国检测试控股集团'+request.user.dept.name,
'发证日期': current_date,
'课程列表': courese_ids,
'examrecord': er.id,
}
serializer = CertificateSerializer(data=data_dict)
serializer.is_valid(raise_exception=True)
serializer.save()
er.took = (now - er.create_time).total_seconds()
er.end_time = now
er.belong_dept=request.user.dept.id
er.is_submited = True
er.save()
return Response(ExamRecordListSerializer(instance=er).data)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-03-29 08:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('information', '0010_rename_time_qualityactivities_activate_time'),
]
operations = [
migrations.RenameField(
model_name='qualityactivities',
old_name='organizational_units',
new_name='orgunits',
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.12 on 2024-04-02 01:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
('information', '0011_rename_organizational_units_qualityactivities_orgunits'),
]
operations = [
migrations.AlterField(
model_name='qualityactivities',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='组织单位'),
),
migrations.AlterField(
model_name='qualitycommendation',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='所属单位'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-04-02 02:23
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
('information', '0012_auto_20240402_0939'),
]
operations = [
migrations.AddField(
model_name='abilityreview',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='所属单位'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-04-02 06:55
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
('information', '0013_abilityreview_department'),
]
operations = [
migrations.AddField(
model_name='contact',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='组织单位'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.12 on 2024-04-02 06:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('information', '0014_contact_department'),
]
operations = [
migrations.RemoveField(
model_name='contact',
name='department',
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.12 on 2024-04-02 07:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0015_remove_contact_department'),
]
operations = [
migrations.AlterField(
model_name='contact',
name='email',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='负责人邮箱'),
),
migrations.AlterField(
model_name='contact',
name='email_quality',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='质量负责人邮箱'),
),
migrations.AlterField(
model_name='contact',
name='email_technology',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='技术负责人邮箱'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-04-03 01:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
('information', '0016_auto_20240402_1558'),
]
operations = [
migrations.AddField(
model_name='externalauditors',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='组织单位'),
),
]

View File

@ -0,0 +1,75 @@
# Generated by Django 3.2.12 on 2024-04-07 08:22
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
('information', '0017_externalauditors_department'),
]
operations = [
migrations.AddField(
model_name='abilityreview',
name='create_date',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='abilityreview',
name='update_date',
field=models.DateTimeField(auto_now=True, null=True),
),
migrations.AddField(
model_name='contact',
name='create_date',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='contact',
name='update_date',
field=models.DateTimeField(auto_now=True, null=True),
),
migrations.AddField(
model_name='externalauditors',
name='create_date',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='externalauditors',
name='update_date',
field=models.DateTimeField(auto_now=True, null=True),
),
migrations.AddField(
model_name='qualityactivities',
name='create_date',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='qualityactivities',
name='update_date',
field=models.DateTimeField(auto_now=True, null=True),
),
migrations.AddField(
model_name='qualitycommendation',
name='create_date',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='qualitycommendation',
name='update_date',
field=models.DateTimeField(auto_now=True, null=True),
),
migrations.AlterField(
model_name='externalauditors',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='单位名称'),
),
migrations.AlterField(
model_name='externalauditors',
name='remark',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='备注'),
),
]

View File

@ -0,0 +1,57 @@
# Generated by Django 3.2.12 on 2024-04-09 06:02
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('information', '0018_auto_20240407_1622'),
]
operations = [
migrations.CreateModel(
name='Qualification',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('company_name', models.CharField(blank=True, max_length=20, null=True, verbose_name='公司名称')),
('name', models.CharField(blank=True, max_length=20, null=True, verbose_name='资质名称')),
('quali_type', models.CharField(choices=[('国家级', '国家级'), ('省级', '省级')], max_length=20, verbose_name='资质类型')),
('org', models.CharField(blank=True, max_length=20, null=True, verbose_name='发证单位')),
('org_date', models.DateField(blank=True, null=True, verbose_name='发证日期')),
('expiration_date', models.DateField(blank=True, null=True, verbose_name='截至日期')),
('scope', models.TextField(blank=True, null=True, verbose_name='资质范围')),
('number', models.IntegerField(blank=True, null=True, verbose_name='参数数量')),
('cie_path', models.CharField(blank=True, max_length=100, null=True, verbose_name='证书路径')),
('create_date', models.DateTimeField(auto_now_add=True, null=True)),
('update_date', models.DateTimeField(auto_now=True, null=True)),
('department', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='system.organization', verbose_name='所属单位')),
],
options={
'verbose_name': '资质情况',
'db_table': 'qfn_info',
},
),
migrations.CreateModel(
name='AuditLog',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('action', models.CharField(max_length=20, verbose_name='动作')),
('instance_id', models.CharField(editable=False, max_length=20, verbose_name='记录ID')),
('change_reason', models.CharField(default='', max_length=50, verbose_name='变更原因')),
('change_time', models.DateTimeField(verbose_name='变更时间')),
('val_new', models.JSONField(default=dict, verbose_name='变更后完整数据')),
('difference', models.JSONField(default=list, verbose_name='变更情况')),
('change_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='操作人')),
],
options={
'verbose_name': '资质变更情况',
'db_table': 'qfn_change_info',
},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.12 on 2024-04-10 06:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0019_auditlog_qualification'),
]
operations = [
migrations.AlterField(
model_name='auditlog',
name='change_time',
field=models.DateField(verbose_name='变更时间'),
),
migrations.AlterField(
model_name='qualification',
name='create_date',
field=models.DateField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='qualification',
name='update_date',
field=models.DateField(auto_now=True, null=True),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.12 on 2024-04-10 08:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('information', '0020_auto_20240410_1434'),
]
operations = [
migrations.RemoveField(
model_name='abilityreview',
name='qualification_name',
),
migrations.AddField(
model_name='abilityreview',
name='qualification',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='information.qualification', verbose_name='资质情况'),
),
]

View File

@ -0,0 +1,65 @@
# Generated by Django 3.2.12 on 2024-04-10 08:55
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('information', '0021_auto_20240410_1604'),
]
operations = [
migrations.RemoveField(
model_name='abilityreview',
name='create_date',
),
migrations.RemoveField(
model_name='abilityreview',
name='department',
),
migrations.RemoveField(
model_name='abilityreview',
name='update_date',
),
migrations.AddField(
model_name='abilityreview',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='abilityreview_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AddField(
model_name='abilityreview',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='abilityreview_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='abilityreview',
name='create_time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'),
),
migrations.AddField(
model_name='abilityreview',
name='is_deleted',
field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'),
),
migrations.AddField(
model_name='abilityreview',
name='qualification_name',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='资质名称'),
),
migrations.AddField(
model_name='abilityreview',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='abilityreview_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='abilityreview',
name='update_time',
field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.12 on 2024-04-10 09:13
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('information', '0022_auto_20240410_1655'),
]
operations = [
migrations.AlterField(
model_name='abilityreview',
name='qualification',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='information.qualification', verbose_name='资质情况'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 3.2.12 on 2024-04-11 01:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('information', '0023_alter_abilityreview_qualification'),
]
operations = [
migrations.RenameField(
model_name='abilityreview',
old_name='qualification',
new_name='quali',
),
migrations.RemoveField(
model_name='abilityreview',
name='qualification_name',
),
]

View File

@ -0,0 +1,224 @@
# Generated by Django 3.2.12 on 2024-04-11 06:17
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0023_alter_user_first_name'),
('information', '0024_auto_20240411_0940'),
]
operations = [
migrations.RemoveField(
model_name='contact',
name='create_date',
),
migrations.RemoveField(
model_name='contact',
name='update_date',
),
migrations.RemoveField(
model_name='externalauditors',
name='create_date',
),
migrations.RemoveField(
model_name='externalauditors',
name='department',
),
migrations.RemoveField(
model_name='externalauditors',
name='update_date',
),
migrations.RemoveField(
model_name='qualification',
name='create_date',
),
migrations.RemoveField(
model_name='qualification',
name='department',
),
migrations.RemoveField(
model_name='qualification',
name='update_date',
),
migrations.RemoveField(
model_name='qualityactivities',
name='create_date',
),
migrations.RemoveField(
model_name='qualityactivities',
name='department',
),
migrations.RemoveField(
model_name='qualityactivities',
name='update_date',
),
migrations.RemoveField(
model_name='qualitycommendation',
name='create_date',
),
migrations.RemoveField(
model_name='qualitycommendation',
name='department',
),
migrations.RemoveField(
model_name='qualitycommendation',
name='update_date',
),
migrations.AddField(
model_name='contact',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AddField(
model_name='contact',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='contact',
name='create_time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'),
),
migrations.AddField(
model_name='contact',
name='is_deleted',
field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'),
),
migrations.AddField(
model_name='contact',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='contact',
name='update_time',
field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'),
),
migrations.AddField(
model_name='externalauditors',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='externalauditors_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AddField(
model_name='externalauditors',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='externalauditors_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='externalauditors',
name='create_time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'),
),
migrations.AddField(
model_name='externalauditors',
name='is_deleted',
field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'),
),
migrations.AddField(
model_name='externalauditors',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='externalauditors_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='externalauditors',
name='update_time',
field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'),
),
migrations.AddField(
model_name='qualification',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualification_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AddField(
model_name='qualification',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualification_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='qualification',
name='create_time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'),
),
migrations.AddField(
model_name='qualification',
name='is_deleted',
field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'),
),
migrations.AddField(
model_name='qualification',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualification_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='qualification',
name='update_time',
field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'),
),
migrations.AddField(
model_name='qualityactivities',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualityactivities_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AddField(
model_name='qualityactivities',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualityactivities_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='qualityactivities',
name='create_time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'),
),
migrations.AddField(
model_name='qualityactivities',
name='is_deleted',
field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'),
),
migrations.AddField(
model_name='qualityactivities',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualityactivities_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='qualityactivities',
name='update_time',
field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'),
),
migrations.AddField(
model_name='qualitycommendation',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualitycommendation_belong_dept', to='system.organization', verbose_name='所属部门'),
),
migrations.AddField(
model_name='qualitycommendation',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualitycommendation_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='qualitycommendation',
name='create_time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'),
),
migrations.AddField(
model_name='qualitycommendation',
name='is_deleted',
field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'),
),
migrations.AddField(
model_name='qualitycommendation',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualitycommendation_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='qualitycommendation',
name='update_time',
field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.12 on 2024-04-12 08:08
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('information', '0025_auto_20240411_1417'),
]
operations = [
migrations.AlterField(
model_name='auditlog',
name='instance_id',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='information.qualification', verbose_name='实例ID'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-12 08:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('information', '0026_alter_auditlog_instance_id'),
]
operations = [
migrations.RenameField(
model_name='auditlog',
old_name='instance_id',
new_name='instance',
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.12 on 2024-04-22 08:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0027_rename_instance_id_auditlog_instance'),
]
operations = [
migrations.AlterField(
model_name='qualitycommendation',
name='awardee_company',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='获奖单位'),
),
migrations.AlterField(
model_name='qualitycommendation',
name='awardee_people',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='获奖人'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-23 03:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0028_auto_20240422_1623'),
]
operations = [
migrations.AlterField(
model_name='qualityactivities',
name='roles',
field=models.PositiveSmallIntegerField(choices=[(0, '组织方'), (1, '参与方')], max_length=20, verbose_name='参与角色'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-23 05:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0029_alter_qualityactivities_roles'),
]
operations = [
migrations.AlterField(
model_name='externalauditors',
name='review_types',
field=models.CharField(max_length=20, verbose_name='评审类型'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-24 04:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0030_alter_externalauditors_review_types'),
]
operations = [
migrations.AddField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, null=True, verbose_name='变更日期'),
),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.12 on 2024-04-25 09:36
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('system', '0023_alter_user_first_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('information', '0031_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='contact',
name='name',
field=models.CharField(max_length=20, unique=True, verbose_name='公司名称'),
),
migrations.CreateModel(
name='Parsepdf',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('pdf_path', models.CharField(max_length=100, verbose_name='pdf路径')),
('excel_path', models.CharField(max_length=100, verbose_name='excel路径')),
('parse_excel', models.CharField(max_length=100, verbose_name='解析后excel路径')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parsepdf_belong_dept', to='system.organization', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parsepdf_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parsepdf_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.12 on 2024-04-25 10:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('information', '0032_auto_20240425_1736'),
]
operations = [
migrations.AlterModelOptions(
name='parsepdf',
options={'verbose_name': '解析pdf'},
),
migrations.AlterModelTable(
name='parsepdf',
table='parsepdf',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-26 00:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0033_auto_20240425_1805'),
]
operations = [
migrations.AddField(
model_name='parsepdf',
name='year',
field=models.IntegerField(default=2024, verbose_name='年份'),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.12 on 2024-04-26 01:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0034_parsepdf_year'),
]
operations = [
migrations.RemoveField(
model_name='parsepdf',
name='year',
),
migrations.AddField(
model_name='parsepdf',
name='excel_name',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='excel名称'),
),
migrations.AddField(
model_name='parsepdf',
name='pdf_name',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='pdf名称'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-26 02:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0035_auto_20240426_0920'),
]
operations = [
migrations.AddField(
model_name='parsepdf',
name='annual',
field=models.IntegerField(default=2024, verbose_name='年份'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-04-28 09:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0036_parsepdf_annual'),
]
operations = [
migrations.AlterField(
model_name='parsepdf',
name='annual',
field=models.IntegerField(blank=True, null=True, verbose_name='年份'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-05-08 09:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0037_alter_parsepdf_annual'),
]
operations = [
migrations.AddField(
model_name='parsepdf',
name='status',
field=models.CharField(choices=[('执行中', '执行中'), ('完成', '完成'), ('未完成', '未完成')], default='完成', max_length=100, verbose_name='状态'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-05-16 07:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('information', '0038_parsepdf_status'),
]
operations = [
migrations.AlterField(
model_name='auditlog',
name='change_reason',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='变更原因'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-05-16 10:58
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0039_alter_auditlog_change_reason'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 5, 16, 10, 58, 55, 398970, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

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

View File

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

View File

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

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-05-27 08:04
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('information', '0043_alter_qualification_change_date'),
]
operations = [
migrations.AlterField(
model_name='qualification',
name='change_date',
field=models.DateField(blank=True, default=datetime.datetime(2024, 5, 27, 8, 4, 40, 332743, tzinfo=utc), null=True, verbose_name='变更日期'),
),
]

View File

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

View File

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

View File

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

View File

@ -1,6 +1,43 @@
from django.db import models
from apps.system.models import Organization
class AbilityReview(models.Model):
from django.utils import timezone
import uuid
from apps.system.models import CommonBDModel
class Qualification(CommonBDModel):
quali_options = (('国家级','国家级'),('省级','省级'))
company_name = models.CharField(max_length=20, verbose_name='公司名称', null=True, blank=True)
name = models.CharField(max_length=20, verbose_name='资质名称',null=True, blank=True)
quali_type = models.CharField(max_length=20, choices=quali_options, verbose_name='资质类型')
org = models.CharField(max_length=20, verbose_name='发证单位', null=True, blank=True)
org_date = models.DateField(verbose_name='发证日期', null=True, blank=True)
expiration_date = models.DateField(verbose_name='截至日期', null=True, blank=True)
scope = models.TextField(verbose_name='资质范围', null=True, blank=True)
number = models.IntegerField(verbose_name='参数数量', null=True, blank=True)
cie_path = models.CharField(max_length=100, verbose_name='证书路径', null=True, blank=True)
change_date = models.DateField(verbose_name='变更日期',default=timezone.now() ,null=True, blank=True)
class Meta:
verbose_name = '资质情况'
db_table = 'qfn_info'
class AuditLog(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
action = models.CharField('动作', max_length=20)
instance = models.ForeignKey(Qualification, on_delete=models.CASCADE, verbose_name='实例ID')
change_reason = models.CharField('变更原因', null=True ,blank=True ,max_length=50)
change_user = models.ForeignKey('system.user', on_delete=models.SET_NULL, verbose_name='操作人', null=True, blank=True)
change_time = models.DateField('变更时间')
val_new = models.JSONField('变更后完整数据', default=dict)
difference = models.JSONField('变更情况', default=list)
class Meta:
verbose_name = '资质变更情况'
db_table = 'qfn_change_info'
class AbilityReview(CommonBDModel):
type_method = (
(0, '文审'),
(10, '现场'))
@ -11,80 +48,93 @@ class AbilityReview(models.Model):
(30, '复评'),
(40, '迁址'))
name = models.CharField(max_length=20, unique=True, verbose_name='公司名称')
qualification_name = models.CharField(max_length=20, verbose_name='资质名称')
# qualification_name = models.CharField(max_length=20, verbose_name='资质名称', null=True, blank=True)
judging_method = models.PositiveSmallIntegerField(max_length=20, choices=type_method, verbose_name='评审方法')
judging_type = models.PositiveSmallIntegerField(max_length=20, choices=juge_type, verbose_name='评审类型')
add_param = models.IntegerField(default=0, verbose_name='新增参数')
review_date = models.DateField(verbose_name='评审日期', null=True)
now_count = models.IntegerField(default=0, verbose_name='现有场所数量')
add_count = models.IntegerField(default=0, verbose_name='新增场所数量')
quali = models.ForeignKey(Qualification, on_delete=models.SET_NULL, null=True, verbose_name='资质情况')
class Meta:
verbose_name = '评审情况'
db_table = 'ability_review'
class QualityCommendation(models.Model):
class QualityCommendation(CommonBDModel):
name = models.CharField(max_length=20, unique=True, verbose_name='项目名称')
commendation_name = models.CharField(max_length=20, verbose_name='表彰名称')
Awards_level = models.CharField(max_length=20, verbose_name='获奖等级')
awardee_company = models.CharField(max_length=20, verbose_name='获奖单位', null=True)
awardee_people = models.CharField(max_length=20, verbose_name='获奖人', null=True)
awardee_company = models.CharField(max_length=20, verbose_name='获奖单位', null=True, blank=True)
awardee_people = models.CharField(max_length=20, verbose_name='获奖人', null=True, blank=True)
awarded_by = models.CharField(max_length=20, verbose_name='颁奖单位')
awarded_date = models.DateField(verbose_name='获奖日期', null=True)
department = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True, verbose_name='所属单位')
class Meta:
verbose_name = '质量表彰'
db_table = 'quality_commendation'
class QualityActivities(models.Model):
role = ((0,'组织方'),(1,'活动'))
class QualityActivities(CommonBDModel):
role = ((0,'组织方'),(1,'参与'))
name = models.CharField(max_length=20, unique=True, verbose_name='活动名称')
roles = models.PositiveSmallIntegerField(max_length=20, choices=role, verbose_name='参与角色')
collaborators = models.CharField(max_length=20, verbose_name='合作方')
organizational_units = models.CharField(max_length=20, verbose_name='组织单位')
orgunits = models.CharField(max_length=20, verbose_name='组织单位')
place = models.CharField(max_length=50, verbose_name='活动地点')
activate_time = models.DateField(verbose_name='活动时间', null=True)
participations = models.IntegerField(verbose_name='活动参与单位数量', null=True, blank=True)
function = models.CharField(max_length=100, verbose_name='活动中发挥的作用', null=True)
earnings = models.FloatField(verbose_name='活动收益', null=True)
department = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True, verbose_name='组织单位')
class Meta:
verbose_name = '质量活动'
db_table = 'quality_activities'
class Contact(models.Model):
name = models.CharField(max_length=20, unique=True, verbose_name='姓名')
class Contact(CommonBDModel):
name = models.CharField(max_length=20, unique=True, verbose_name='公司名称')
address = models.CharField(max_length=100, verbose_name='地址')
header = models.CharField(max_length=20, verbose_name='负责人')
tel = models.CharField(max_length=20, verbose_name='负责人电话')
email = models.CharField(max_length=50, verbose_name='负责人邮箱', null=True)
email = models.CharField(max_length=50, verbose_name='负责人邮箱', null=True, blank=True)
head_technology = models.CharField(max_length=50, verbose_name='技术负责人')
tel_technology = models.CharField(max_length=20, verbose_name='技术负责人电话')
email_technology = models.CharField(max_length=50, verbose_name='技术负责人邮箱', null=True)
email_technology = models.CharField(max_length=50, verbose_name='技术负责人邮箱', null=True, blank=True)
head_quality = models.CharField(max_length=50, verbose_name='质量负责人')
tel_quality = models.CharField(max_length=20, verbose_name='质量负责人电话')
email_quality = models.CharField(max_length=50, verbose_name='质量负责人邮箱', null=True)
tel_quality = models.CharField(max_length=20, verbose_name='质量负责人电话')
email_quality = models.CharField(max_length=50, verbose_name='质量负责人邮箱', null=True, blank=True)
class Meta:
verbose_name = '实验室联系方式'
db_table = 'contact'
class ExternalAuditors(models.Model):
review_type = ( (0, 'CNAS'),
(1, 'CMA'),
(2, 'DICA'))
class ExternalAuditors(CommonBDModel):
name_company = models.CharField(max_length=20, unique=True, verbose_name='公司名称')
name = models.CharField(max_length=20, verbose_name='姓名')
review_types = models.PositiveSmallIntegerField(max_length=20, choices=review_type,verbose_name='评审类型', default=0)
review_types = models.CharField(max_length=20, verbose_name='评审类型')
certificate_expiration = models.DateField(verbose_name='证书有效期')
contact = models.CharField(max_length=20, verbose_name='联系方式')
judging_areas = models.CharField(max_length=20, verbose_name='评审领域')
remark = models.CharField(max_length=20, verbose_name='备注', null=True)
remark = models.CharField(max_length=20, verbose_name='备注', null=True, blank=True)
class Meta:
verbose_name = '外审员情况'
db_table = 'externalauditors'
class Parsepdf(CommonBDModel):
choice_status= (
('执行中', '执行中'),
('完成', '完成'),
('未完成', '未完成'))
pdf_path = models.CharField(max_length=100, verbose_name='pdf路径')
excel_path = models.CharField(max_length=100, verbose_name='excel路径')
parse_excel = models.CharField(max_length=100, verbose_name='解析后excel路径')
annual = models.IntegerField(verbose_name='年份', blank=True, null=True)
excel_name = models.CharField(max_length=100, verbose_name='excel名称', null=True, blank=True)
pdf_name = models.CharField(max_length=100, verbose_name='pdf名称', null=True, blank=True)
status = models.CharField(max_length=100, choices=choice_status, verbose_name='状态', default='完成')
class Meta:
verbose_name = '解析pdf'
db_table = 'parsepdf'

View File

@ -1,37 +1,70 @@
from rest_framework import serializers
from apps.system.serializers import OrganizationSimpleSerializer
from .models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors
from apps.system.models import Organization
from .models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors, AuditLog, Qualification, Parsepdf
from django.utils import timezone
class AbilityReviewSerializer(serializers.ModelSerializer):
class Meta:
model = AbilityReview
fields = '__all__'
class BaseMeta:
fields = '__all__'
read_only_fields = ['create_time', 'update_time', 'create_by', 'update_by', 'is_deleted', 'belong_dept']
class QualityCommendationSerializer(serializers.ModelSerializer):
class Meta:
class Meta(BaseMeta):
model = QualityCommendation
fields = '__all__'
read_only_fields = ['id']
class QualityActivitiesSerializer(serializers.ModelSerializer):
class Meta:
class Meta(BaseMeta):
model = QualityActivities
fields = '__all__'
read_only_fields = ['id']
class ContactSerializer(serializers.ModelSerializer):
class Meta:
class Meta(BaseMeta):
model = Contact
fields = '__all__'
class ExternalAuditorsSerializer(serializers.ModelSerializer):
class Meta:
class Meta(BaseMeta):
model = ExternalAuditors
fields = '__all__'
class Qualification2Serializer(serializers.ModelSerializer):
class Meta(BaseMeta):
model = Qualification
fields = ["id","name","company_name","quali_type","org","org_date","expiration_date","number","scope"]
def create(self, validated_data):
validated_data["belong_dept_id"] = Organization.objects.filter(name = validated_data.get("company_name")).first().id
return super().create(validated_data)
def update(self, instance, validated_data):
validated_data["change_date"] = timezone.now().date()
return super().update(instance, validated_data)
class AbilityReviewSerializer(serializers.ModelSerializer):
quali_name = serializers.CharField(source='quali.name', read_only=True)
class Meta(BaseMeta):
model = AbilityReview
fields = '__all__'
class AuditLogSerializer(serializers.ModelSerializer):
company_name = serializers.CharField(source='instance.company_name', read_only=True)
class Meta(BaseMeta):
model = AuditLog
fields = '__all__'
class ParsepdfSerializer(serializers.ModelSerializer):
class Meta(BaseMeta):
model = Parsepdf
fields = '__all__'

View File

@ -1,13 +1,19 @@
from django.urls import path, include
from rest_framework import routers
from .views import AbilityReviewViewSet, QualityCommendationViewSet, QualityActivitiesViewSet, ContactViewSet, ExternalAuditorsViewSet
from .views import AbilityReviewViewSet,sendMsgInfo, AuditLogViewSet, QualityCommendationViewSet, QualityActivitiesViewSet, ParsePdfViewSet, QualiChangeViewSet, ContactViewSet, ExternalAuditorsViewSet, QualificationViewSet
router = routers.DefaultRouter()
router.register('abilityreviews', AbilityReviewViewSet, basename='abilityreviews')
router.register('qualitycommendation', QualityCommendationViewSet, basename='qualitycommendation')
router.register('qualityactivities', QualityActivitiesViewSet, basename='qualityactivities')
router.register('ar', AbilityReviewViewSet, basename='abilityreviews')
router.register('qc', QualityCommendationViewSet, basename='qualitycommendation')
router.register('qa', QualityActivitiesViewSet, basename='qualityactivities')
router.register('contact', ContactViewSet, basename='contact')
router.register('externalauditors', ExternalAuditorsViewSet, basename='externalauditors')
router.register('ea', ExternalAuditorsViewSet, basename='externalauditors')
router.register('faq', QualificationViewSet, basename='faq')
router.register('faqch', QualiChangeViewSet, basename='faqch')
router.register('ad', AuditLogViewSet, basename='ad')
router.register('pdf', ParsePdfViewSet, basename='pdf')
urlpatterns = [
path('', include(router.urls))
path('', include(router.urls)),
path('sendmsg_info/', sendMsgInfo.as_view())
]

View File

@ -1,14 +1,8 @@
from rest_framework import viewsets, mixins
from rest_framework.viewsets import ViewSet
from rest_framework import status
from django.conf import settings
from server import settings
from rest_framework.decorators import action
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
# from .models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors
# from .serializers import AbilityReviewSerializer, QualityCommendationSerializer, QualityActivitiesSerializer,ContactSerializer, ExternalAuditorsSerializer
from apps.system.permission_data import RbacFilterSet
from utils.queryset import get_child_queryset2
from rest_framework.response import Response
from apps.system.permission import has_permission
@ -18,21 +12,18 @@ from rest_framework.exceptions import ParseError
from apps.system.models import Organization
from .models import *
from .serializers import *
from utils.pdf2txt import run
from utils.sedmeg import sendmessage
from datetime import datetime
from rest_framework.views import APIView
from apps.system.models import Role
import os
import traceback
import shutil
class AbilityReviewViewSet( mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = AbilityReview.objects.all()
serializer_class = AbilityReviewSerializer
EXCEL_PATH = os.path.join(settings.BASE_DIR, "media/default/检验检测服务业统计数据上报任务.xlsx")
#自定义查询
def get_queryset(self):
pass
class ImpMixin:
def get_queryset(self):
@ -68,57 +59,133 @@ class ImpMixin:
def F(self, data, sheet, i, etype):
raise NotImplementedError()
def gen_imp_view(self, request, start: int, mySerializer, types = None):
def gen_imp_view(self, request, start: int, mySerializer):
if 'file' not in request.data:
raise ParseError('请提供文件')
path = request.data['file']
path = request.data['file']
if not str(path).endswith('.xlsx'):
raise ParseError('请提供xlsx格式文件')
fullpath = os.path.join(settings.BASE_DIR, str(path))
fullpath = settings.BASE_DIR + str(path)
wb = load_workbook(fullpath,data_only=True)
sheet = wb.active
# 遍历Excel文件中的数据
if types.lower() == "qt":
data_list = self.build_qt_data(sheet)
else:
pass
data_list = self.build_data(sheet, start)
serializer = mySerializer(data=data_list, many=True, context={'request': request})
if serializer.is_valid():
serializer.save()
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 QualityCommendationViewSet(ImpMixin, ModelViewSet):
queryset = QualityCommendation.objects.all()
serializer_class = QualityCommendationSerializer
perms_map = {"get": "*", "post": "*", "put": "*", "delete": "*"}
def create(self):
if Organization.objects.filter(name=self.request.data['awardee_company']).exists():
department_id = Organization.objects.filter(name=self.request.data['awardee_company']).first().id
self.request.data['department'] = department_id
serializer = self.get_serializer(data=self.request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status = status.HTTP_201_CREATED)
# 人名存在重复
class CreateUpdateCustomMixin:
"""
整合
"""
def perform_create(self, serializer):
if hasattr(self.queryset.model, 'belong_dept'):
belong_dept = serializer.validated_data.get('belong_dept', self.request.user.dept)
serializer.save(create_by = self.request.user, belong_dept=belong_dept)
else:
raise ParseError("部门不存在")
serializer.save(create_by = self.request.user)
def perform_update(self, serializer):
serializer.save(update_by = self.request.user)
# 导入表格
@action(methods=['post'], detail=False)
class AbilityReviewViewSet(ImpMixin, RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post': 'abilityreview',
'put': 'abilityreview', 'delete': 'abilityreview'}
queryset = AbilityReview.objects.select_related('quali').all()
serializer_class = AbilityReviewSerializer
filterset_fields = ['belong_dept', 'judging_method', 'judging_type']
search_fields = ['name']
# 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))
#根据日期过滤数据
@action(detail=False, methods=['post'])
@transaction.atomic
def filter_by_date(self, request, *args, **kwargs):
father_dept = request.user.dept
child_dept = get_child_queryset2(father_dept)
start_date = request.data['startDate']
end_date = request.data['endDate']
query = AbilityReview.objects.filter(create_time__range=[start_date, end_date], belong_dept_id__in=child_dept)
serializer = AbilityReviewSerializer(query, many=True)
data_list = serializer.data
# 构造结构化数据
map_key_dict = {"name": "公司名称",
"qualification_name": "资质名称",
"qualification_level": "资质等级",
"judging_method": "评审方法",
"judging_type": "评审类型",
"add_param":"新增参数",
"review_date": "评审日期",
"now_count":"现有场所数量",
"add_count":"新增场所数量",
}
review_method = {0:"文审", 10:"现场"}
juge_type = {0:"初次", 10:"扩项", 20:"变更", 30:"复评",40:"迁址"}
# 遍历字典将旧key映射到新key
new_data_list = []
for i in range(len(data_list)):
new_data_dict = {map_key_dict[old_key]: old_value for old_key, old_value in data_list[i].items() if old_key in map_key_dict.keys()}
new_data_dict['评审方法'] = review_method[data_list[i]['judging_method']]
new_data_dict['评审类型'] = juge_type[data_list[i]['judging_type']]
new_data_list.append(new_data_dict)
data = {'count':len(serializer.data), 'results':new_data_list}
return Response(data, status = status.HTTP_200_OK)
def build_data(self, sheet, start):
data_list = []
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[0] is not None:
review_date = row[6].strftime("%Y-%m-%d")
quil_id = Qualification.objects.filter(name=row[2]).first().id
judging_method_dict = {"文审":0, "现场":10}
judging_type_dict = {"初次":0, "扩项":10, "变更":20, "复评":30, "迁址":40}
serializer_data = {
'name': row[1], # 第一列是名字
'quali':quil_id,
'judging_method':judging_method_dict.get(row[3]),
'judging_type':judging_type_dict.get(row[4]),
'add_param':row[5],
'review_date':review_date,
'now_count':row[7],
'add_count':row[8]
}
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, 5, QualityCommendationSerializer, 'qt')
#构造导入的数据格式
def build_qt_data(self, sheet):
"""
导入数据
"""
return self.gen_imp_view(request, 2, AbilityReviewSerializer)
class QualityCommendationViewSet(ImpMixin, RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
queryset = QualityCommendation.objects.all()
serializer_class = QualityCommendationSerializer
def build_data(self, sheet, start):
data_list = []
for row in sheet.iter_rows(min_row=2, values_only=True): # 假设第一行是表头,从第二行开始读取数据
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[0] is not None:
awarded_date = row[6].strftime("%Y-%m-%d")
# 判断获奖的是人还是公司
@ -137,63 +204,328 @@ class QualityCommendationViewSet(ImpMixin, ModelViewSet):
'awardee_people':awardee_people,
'awarded_by':row[5],
'awarded_date':awarded_date,
'department':department_id,
'belong_dept':department_id,
}
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, QualityCommendationSerializer)
# 查询子以及已经本公司的质量表彰
@action(detail=False, methods=['get'])
def commentdation_info(self, *args, **kwargs):
father_dept = self.request.user.dept
#根据日期过滤数据
@action(detail=False, methods=['post'])
@transaction.atomic
def filter_by_date(self, request, *args, **kwargs):
father_dept = request.user.dept
child_dept = get_child_queryset2(father_dept)
query = QualityCommendation.objects.filter(department__in=child_dept)
start_date = request.data['startDate']
end_date = request.data['endDate']
query = QualityCommendation.objects.filter(create_time__range=[start_date, end_date], belong_dept_id__in=child_dept)
serializer = QualityCommendationSerializer(query, many=True)
return Response(serializer.data)
data_list = serializer.data
map_key_dict = {"name": "项目名称",
"commendation_name": "表彰名称",
"Awards_level": "获奖等级",
"awardee_company": "获奖单位",
"awardee_people": "获奖人",
"awarded_by":"颁奖单位",
"review_date": "获奖日期"
}
new_data_list = []
for i in range(len(data_list)):
new_data_list.append({map_key_dict[key] : value for key, value in data_list[i].items() if key in map_key_dict.keys()})
data = {'count':len(serializer.data), 'results':new_data_list}
return Response(data, status = status.HTTP_200_OK)
# 质量活动
class QualityActivitiesViewSet(ModelViewSet):
class QualityActivitiesViewSet(ImpMixin, RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
queryset = QualityActivities.objects.all()
serializer_class = QualityActivitiesSerializer
def create(self, request):
if Organization.objects.filter(name=self.request.data['organizational_units']).exists():
department_id = Organization.objects.filter(name=self.request.data['organizational_units']).first().id
self.request.data['department'] = department_id
print("-----------,",self.request.data)
serializer = self.get_serializer(data=self.request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status = status.HTTP_201_CREATED)
else:
raise ParseError("部门不存在")
# 查询子以及已经本公司的质量活动
@action(detail=False, methods=['get'])
def activate_info(self, *args, **kwargs):
father_dept = self.request.user.dept
#根据日期过滤数据
@action(detail=False, methods=['post'])
@transaction.atomic
def filter_by_date(self, request, *args, **kwargs):
father_dept = request.user.dept
child_dept = get_child_queryset2(father_dept)
query = QualityActivities.objects.filter(department__in=child_dept)
start_date = request.data['startDate']
end_date = request.data['endDate']
query = QualityActivities.objects.filter(create_time__range=[start_date, end_date], belong_dept_id__in=child_dept)
serializer = QualityActivitiesSerializer(query, many=True)
return Response(serializer.data)
data_list = serializer.data
map_key_dict = {"name": "活动名称",
"roles": "参与角色",
"collaborators": "合作方",
"orgunits": "组织单位",
"place": "活动地点",
"activate_time":"活动时间",
"participations": "活动参与单位数量",
"function": "活动中发挥的作用",
"earnings":"活动收益(元)"
}
role_map ={0:"组织方", 1:"参与方"}
new_data_list = []
for i in range(len(data_list)):
new_dict = {map_key_dict[key] : value for key, value in data_list[i].items() if key in map_key_dict.keys()}
new_dict['参与角色'] = role_map[data_list[i]['roles']]
new_data_list.append(new_dict)
data = {'count':len(serializer.data), 'results':new_data_list}
return Response(data, status = status.HTTP_200_OK)
def build_data(self, sheet, start):
data_list = []
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[4] !=None:
department_id = Organization.objects.filter(name=row[4]).first().id
activate_time = row[6].strftime("%Y-%m-%d")
role_dict = {"组织方":0, "参与方":1}
serializer_data = {
'name': row[1], # 第一列是名字
'roles':role_dict.get(row[2]),
'collaborators':row[3],
'orgunits':row[4],
'place':row[5],
'activate_time':activate_time,
'participations':row[7],
'function':row[8],
'earnings':row[9],
'belong_dept':department_id,
}
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, QualityActivitiesSerializer)
class ContactViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
class ContactViewSet(ImpMixin, CreateUpdateCustomMixin, ModelViewSet):
queryset = Contact.objects.all()
serializer_class = ContactSerializer
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))
class ExternalAuditorsViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
def build_data(self, sheet, start):
data_list = []
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[0] is not None:
serializer_data = {
'name': row[1], # 第一列是名字
'address':row[2],
'header':row[3],
'tel':row[4],
'email':row[5],
'head_technology':row[6],
'tel_technology':row[7],
'email_technology':row[8],
'head_quality':row[9],
'tel_quality':row[10],
'email_quality':row[11],
}
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, ContactSerializer)
def dont_wait(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
return Response({"message": "操作已在后台开始,结果将不会立即返回。"})
return wrapper
import threading
class ParsePdfViewSet(RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
queryset = Parsepdf.objects.all().order_by('id')
serializer_class = ParsepdfSerializer
filterset_fields = ['belong_dept', 'annual']
#解析pdf到excel
@action(detail=False, methods=['post'])
@transaction.atomic
def parse_pdf(self, request, *args, **kwargs):
"""
解析pdf到excel
"""
pdf_file = request.data['pdf_file']
annual = request.data['annual']
# 读数据路径copy 在media 下新建excel,解析完成后存入数据库。
media_excel = os.path.join(os.path.dirname(EXCEL_PATH),"media_excel")
if not os.path.exists(media_excel):
os.makedirs(media_excel)
shutil.copy(EXCEL_PATH, media_excel)
save_path = os.path.join(media_excel, os.path.basename(EXCEL_PATH))
server_pdf_path = settings.BASE_DIR + pdf_file
download_excel_path = save_path.split("server")[-1]
ps = Parsepdf.objects.create(
pdf_path=pdf_file,
excel_path= EXCEL_PATH,
parse_excel = download_excel_path,
belong_dept = request.user.dept,
create_by = request.user,
create_time = datetime.now(),
annual = annual,
excel_name = os.path.basename(EXCEL_PATH),
pdf_name = os.path.basename(pdf_file),
status = "执行中"
)
t = threading.Thread(target=run, args=(server_pdf_path, save_path, ps.id))
t.start()
return Response({"message":"解析中", "url":save_path}, status = status.HTTP_200_OK)
# msg = run(server_pdf_path, save_path)
# if msg == "success":
# Parsepdf.objects.update(id=ps.id, status="成功")
# return Response({"message":"解析成功", "url":save_path}, status = status.HTTP_200_OK)
class ExternalAuditorsViewSet(ImpMixin, RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
queryset = ExternalAuditors.objects.all()
serializer_class = ExternalAuditorsSerializer
#根据日期过滤数据
@action(detail=False, methods=['post'])
@transaction.atomic
def filter_by_date(self, request, *args, **kwargs):
father_dept = request.user.dept
child_dept = get_child_queryset2(father_dept)
start_date = request.data['startDate']
end_date = request.data['endDate']
query = ExternalAuditors.objects.filter(create_time__range=[start_date, end_date], belong_dept_id__in=child_dept)
serializer = ExternalAuditorsSerializer(query, many=True)
data_list = serializer.data
map_key_dict = {"name_company": "公司名称",
"name": "姓名",
"certificate_expiration": "证书有效期",
"contact": "联系方式",
"judging_areas": "评审领域",
"remark":"备注",
"review_types": "评审类型"
}
new_data_list = []
for i in range(len(data_list)):
new_dict = {map_key_dict[key] : value for key, value in data_list[i].items() if key in map_key_dict.keys()}
new_data_list.append(new_dict)
data = {'count':len(serializer.data), 'results':new_data_list}
return Response(data, status = status.HTTP_200_OK)
def build_data(self, sheet, start):
data_list = []
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[0] is not None:
department_id = Organization.objects.filter(name=row[1]).first().id
activate_time = row[4].strftime("%Y-%m-%d")
serializer_data = {
'name_company': row[1], # 第一列是名字
'name':row[2],
'review_types':row[3],
'certificate_expiration':activate_time,
'contact':row[5],
'judging_areas':row[6],
'remark':row[7],
'belong_dept':department_id,
}
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, ExternalAuditorsSerializer)
class QualificationViewSet(ModelViewSet):
queryset = Qualification.objects.all()
serializer_class = Qualification2Serializer
perms_map = {'get': '*', 'post': 'qa_create',
'put': 'qa_update', 'delete': 'qa_delete'}
class AuditLogViewSet(RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
queryset = AuditLog.objects.select_related('instance').all()
serializer_class = AuditLogSerializer
filterset_fields = ['instance_id']
class QualiChangeViewSet(RbacFilterSet, CreateUpdateCustomMixin, ModelViewSet):
queryset = Qualification.objects.all()
serializer_class = Qualification2Serializer
perms_map = {'get': '*', 'post': 'qchange_create',
'put': 'qchange_update', 'patch': 'qchange_update', 'delete': 'qchange_delete'}
# 重写更新的方法
def update(self, request, pk=None):
#获取需要更新的实列
instance = self.get_object()
# 数据比较
print("--------",request.data)
ignore_fields = ['create_by', 'create_time', 'update_date', 'id', 'change_reason']
origin_dict = QualificationSerializer(instance=instance).data
diff = []
for k, v in request.data.items():
if k not in ignore_fields:
origin_value = origin_dict.get(k)
if origin_value != v:
diff.append({'old':origin_value, 'new':v, 'name':k})
serializers = self.get_serializer(instance, data=request.data, partial=True)
serializers.is_valid(raise_exception=True)
self.perform_update(serializers)
if diff:
AuditLog.objects.create(
action='update',
instance=instance,
change_time = datetime.now(),
change_reason = request.data.get('change_reason'),
change_user=request.user,
val_new=serializers.data,
difference=diff
)
return Response(serializers.data, status = status.HTTP_204_NO_CONTENT)
class sendMsgInfo(APIView):
def post(self, request):
# 获取角色名称
names = request.data['names']
# 根据角色查询绑定的user
role_ids = [role.id for role in Role.objects.filter(name__in=names)]
reci_users = []
for i in role_ids:
role = Role.objects.get(id=i)
users = role.user_set.all()
username = users.values_list('username', flat=True)
reci_users.extend(username)
subject = "检测检验月度邮件通知"
message = "各个单位提交报表"
for user in reci_users:
sendmessage(subject,message, user)
return Response({"message":"发送成功"}, status = status.HTTP_200_OK)

View File

@ -0,0 +1,30 @@
# Generated by Django 3.2.12 on 2024-04-01 08:19
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('supervision', '0064_auto_20240326_1004'),
]
operations = [
migrations.AlterField(
model_name='oinspect',
name='result',
field=models.TextField(blank=True, default='', null=True, verbose_name='检查结果'),
),
migrations.AlterField(
model_name='oinspect',
name='result_date',
field=models.DateField(default=datetime.datetime(2024, 4, 1, 8, 19, 8, 965907, tzinfo=utc), verbose_name='检查结果日期'),
),
migrations.AlterField(
model_name='pt',
name='result',
field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '满意'), (20, '有问题'), (30, '不满意')], default=None, help_text="((10, '满意'), (20, '有问题'), (30, '不满意'))", null=True, verbose_name='结果'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.12 on 2024-04-01 08:41
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('supervision', '0065_auto_20240401_1619'),
]
operations = [
migrations.AlterField(
model_name='oinspect',
name='result_date',
field=models.DateField(default=datetime.datetime(2024, 4, 1, 8, 41, 22, 34090, tzinfo=utc), verbose_name='检查结果日期'),
),
migrations.AlterField(
model_name='pt',
name='result',
field=models.PositiveSmallIntegerField(choices=[(0, '/'), (10, '满意'), (20, '有问题'), (30, '不满意')], default=0, help_text="((0, '/'), (10, '满意'), (20, '有问题'), (30, '不满意'))", verbose_name='结果'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.12 on 2024-05-07 09:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supervision', '0066_auto_20240401_1641'),
]
operations = [
migrations.AlterField(
model_name='oinspect',
name='result_date',
field=models.DateField(blank=True, default='', null=True, verbose_name='检查结果日期'),
),
migrations.AlterField(
model_name='pt',
name='certificate_date',
field=models.DateField(blank=True, null=True, verbose_name='证书日期'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-05-08 06:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supervision', '0067_auto_20240507_1751'),
]
operations = [
migrations.AlterField(
model_name='pt',
name='certificate_date',
field=models.DateField(blank=True, default='', null=True, verbose_name='证书日期'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-05-08 06:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supervision', '0068_alter_pt_certificate_date'),
]
operations = [
migrations.AlterField(
model_name='pt',
name='result',
field=models.PositiveSmallIntegerField(choices=[(0, '未出'), (10, '满意'), (20, '有问题'), (30, '不满意')], default=0, help_text="((0, '未出'), (10, '满意'), (20, '有问题'), (30, '不满意'))", verbose_name='结果'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-05-08 07:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supervision', '0069_alter_pt_result'),
]
operations = [
migrations.AlterField(
model_name='pt',
name='certificate_date',
field=models.DateField(blank=True, null=True, verbose_name='证书日期'),
),
]

View File

@ -235,6 +235,7 @@ class Pt(CommonBDModel):
"""能力验证
"""
pt_result_choices = (
(0, '未出'),
(10, '满意'),
(20, '有问题'),
(30, '不满意')
@ -253,9 +254,9 @@ class Pt(CommonBDModel):
number = models.CharField('编号', max_length=100)
organizer = models.CharField('组织方', max_length=100)
params = models.TextField('参加参数名称')
result = models.PositiveSmallIntegerField('结果', choices=pt_result_choices, default=10, help_text=str(pt_result_choices))
result = models.PositiveSmallIntegerField('结果', choices=pt_result_choices, default=0, help_text=str(pt_result_choices))
handle_result = models.PositiveSmallIntegerField('处理结果', choices = pt_hresult_choices, default=0, help_text=str(pt_hresult_choices))
certificate_date = models.DateField('证书日期')
certificate_date = models.DateField('证书日期', null=True, blank=True)
field = models.PositiveSmallIntegerField('领域', choices=field_choices, help_text=str(field_choices),default=0)
a_class = models.BooleanField('A类', default=True)
task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True)
@ -292,8 +293,8 @@ class Oinspect(CommonBDModel):
cate = models.PositiveSmallIntegerField('检查分类', choices=oinspect_cate_choices, default=30, help_text=str(oinspect_cate_choices))
checker = models.CharField('检查方', max_length=100)
date_inspect = models.DateField('检查日期')
result = models.TextField('检查结果')
result_date = models.DateField('检查结果日期', default=timezone.now())
result = models.TextField('检查结果', null=True, blank=True, default='')
result_date = models.DateField('检查结果日期', null=True, blank=True, default='')
notification_form = models.CharField('检查通知单', max_length=100, null=True)
result_form = models.CharField('检查结果告知单', max_length=100, null=True)
task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True)

View File

@ -570,6 +570,36 @@ class Task2DoViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
serializer = Task2DoSerializer(queryset, many=True)
return Response(serializer.data)
class BulkDeleteModelViewSet(ModelViewSet):
"""
A ModelViewSet with a bulk delete action.
"""
def get_queryset(self):
"""
Override this method to define your queryset if necessary.
"""
return super().get_queryset()
@action(methods=['delete'], detail=False)
def bulk_delete(self, request, *args, **kwargs):
# 获取要删除的对象的ID列表
print("--", request.data)
ids = request.data.get('ids', [])
if not ids:
return Response({"error": "No IDs provided for deletion"}, status=status.HTTP_400_BAD_REQUEST)
# 构建查询条件
query = Q()
for id in ids:
query |= Q(id=id)
# 执行批量删除
deleted_count, _ = self.get_queryset().filter(query).delete()
# 返回删除的数量
return Response({"deleted_count": deleted_count}, status=status.HTTP_200_OK)
class Domixin:
perms_map = {'get': '*', 'put': 'task2do', 'post': 'task2do', 'delete': 'task2do'}
@ -699,7 +729,7 @@ class ImpMixin:
self.countup_task2do(task2do) # 统计执行情况
return Response()
class RcViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
class RcViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin,BulkDeleteModelViewSet, ModelViewSet):
queryset = Rc.objects.all()
serializer_class = RcSerializer
count_dict = {
@ -745,7 +775,7 @@ class RcViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
class ComplaintViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
class ComplaintViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, BulkDeleteModelViewSet, ModelViewSet):
queryset = Complaint.objects.all()
serializer_class = ComplaintSerializer
count_dict = {
@ -772,7 +802,7 @@ class ComplaintViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet)
return self.gen_imp_view(request, 5, ComplaintSerializer)
class PtViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
class PtViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, BulkDeleteModelViewSet, ModelViewSet):
queryset = Pt.objects.all()
serializer_class = PtSerializer
count_dict = {
@ -801,7 +831,7 @@ class PtViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
return self.gen_imp_view(request, 5, PtSerializer, 0, Pt, repalce=True, types='pt')
class RiskViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
class RiskViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, BulkDeleteModelViewSet, ModelViewSet):
queryset = Risk.objects.all()
serializer_class = RiskSerializer
count_dict = {
@ -826,7 +856,7 @@ class RiskViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
return self.gen_imp_view(request, 5, RiskSerializer)
class OinspectViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
class OinspectViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, BulkDeleteModelViewSet, ModelViewSet):
queryset = Oinspect.objects.all()
serializer_class = OinspectSerializer
count_dict = {
@ -850,4 +880,5 @@ class OinspectViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet):
导入表格
"""
return self.gen_imp_view(request, 5, OinspectSerializer, 0, Oinspect, repalce=True, types="oinspect")
return self.gen_imp_view(request, 5, OinspectSerializer, 0, Oinspect, repalce=True, types="oinspect")

View File

@ -95,6 +95,7 @@ class UserListSerializer(serializers.ModelSerializer):
queryset = queryset.prefetch_related('roles',)
return queryset
class UserModifySerializer(serializers.ModelSerializer):
"""
用户编辑序列化

View File

@ -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")

View File

@ -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()

File diff suppressed because one or more lines are too long

BIN
server/dist/baiwen.pdf vendored Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More