From 8e5874a30ae9b194968393b2399bbba193eaa313 Mon Sep 17 00:00:00 2001 From: zhouwx <1175765986@qq.com> Date: 星期三, 20 十一月 2024 17:14:00 +0800 Subject: [PATCH] 项目管理 --- src/views/safetyReview/projectManage/components/basicInfo.vue | 113 +++++- src/views/safetyReview/projectManage/components/chooseExpert.vue | 185 ++++++++++- src/store/modules/user.js | 1 src/api/projectManage.js | 71 ++++ src/views/safetyReview/projectManage/components/exportWord.js | 55 +++ src/views/safetyReview/projectManage/components/projectArchive.vue | 23 + src/views/safetyReview/projectManage/process.vue | 107 ++++-- src/views/safetyReview/projectManage/components/expertsList.vue | 56 ++- src/views/safetyReview/projectManage/index.vue | 135 +++----- src/views/safetyReview/projectManage/components/projectApproval.vue | 175 +++++----- 10 files changed, 638 insertions(+), 283 deletions(-) diff --git a/src/api/projectManage.js b/src/api/projectManage.js new file mode 100644 index 0000000..8162ce8 --- /dev/null +++ b/src/api/projectManage.js @@ -0,0 +1,71 @@ +import request from "@/utils/request"; + +// 项目管理列表(分页) +export function getProjectList(params) { + return request({ + url: '/system/management/list', + method: 'get', + params: params + }) +} + +// 新增 +export function addProject(data) { + return request({ + url: '/system/management/saveProject', + method: 'post', + data: data + }) +} + +// 项目详细信息 +export function getProjectInfo(id) { + return request({ + url: '/system/management/' + id, + method: 'get', + }) +} + +//数量 +export function getProjectNum() { + return request({ + url: '/system/management/getProjectNum', + method: 'get', + }) +} + +// 删除项目 +export function delProject(id) { + return request({ + url: '/system/management/'+id, + method: 'delete' + }) +} + +// 专家选用 +export function choose(data) { + return request({ + url: '/system/management/projectExpert', + method: 'post', + data: data + }) +} + + +// 随机获取专家 +export function expertRound(data) { + return request({ + url: '/system/expert_info/getExpertRound', + method: 'post', + data: data + }) +} + + +export function getCheckInfo(params) { + return request({ + url: '/system/management/projectExpertCheckInfo', + method: 'get', + params: params + }) +} diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 065f3a6..9900234 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -47,6 +47,7 @@ this.id = user.userId this.name = user.userName this.avatar = avatar + Cookies.set('userInfo',JSON.stringify(user)) resolve(res) }).catch(error => { reject(error) diff --git a/src/views/safetyReview/projectManage/components/basicInfo.vue b/src/views/safetyReview/projectManage/components/basicInfo.vue index 5f36145..03649ea 100644 --- a/src/views/safetyReview/projectManage/components/basicInfo.vue +++ b/src/views/safetyReview/projectManage/components/basicInfo.vue @@ -22,7 +22,7 @@ </el-form-item> </el-col> <el-col :span="6"> - <el-form-item prop="projectName" label="项目预计时间区间"> + <el-form-item prop="projectDateStart" label="项目预计时间区间"> <el-date-picker v-model="searchTime" type="daterange" @@ -36,9 +36,9 @@ </el-form-item> </el-col> <el-col :span="6"> - <el-form-item prop="unit" label="被检查/对接单位"> + <el-form-item prop="companyName" label="被检查/对接单位"> <el-input - v-model.trim="state.formData.unit" + v-model.trim="state.formData.companyName" size="large" placeholder="请输入被检查/对接单位" > @@ -46,9 +46,9 @@ </el-form-item> </el-col> <el-col :span="6"> - <el-form-item prop="place" label="项目地点"> + <el-form-item prop="projectAddress" label="项目地点"> <el-input - v-model.trim="state.formData.place" + v-model.trim="state.formData.projectAddress" size="large" placeholder="请输入项目地点" > @@ -58,9 +58,9 @@ </el-row> <el-row :gutter="24"> <el-col :span="6"> - <el-form-item prop="people" label="处室带队人员"> + <el-form-item prop="deptUserName" label="处室带队人员"> <el-input - v-model.trim="state.formData.projectName" + v-model.trim="state.formData.deptUserName" size="large" placeholder="请输入处室带队人员" > @@ -70,11 +70,11 @@ </el-row> <el-row :gutter="24"> <el-col :span="24"> - <el-form-item prop="description" label="概况描述"> + <el-form-item prop="remark" label="概况描述"> <el-input type="textarea" :rows="6" - v-model.trim="state.formData.description" + v-model.trim="state.formData.remark" size="large" placeholder="请输入概况描述" > @@ -87,49 +87,108 @@ </template> <script setup> -import {reactive, ref} from "vue"; +import {onMounted, reactive, ref} from "vue"; +import Cookies from "js-cookie"; +import {addProject, getProjectInfo, getProjectList} from "@/api/projectManage"; +import {ElMessage} from "element-plus"; +const searchTime = ref([]); const emit = defineEmits(["getNextStatus"]); const state = reactive({ formData: { + id:null, + deptId: null, deptName:'危险化学品监督管理处', projectName: '', - startTime: '', - endTime: '', - unit: '', - place: '', - people: '', - description: '' + projectDateStart: '', + projectDateEnd: '', + companyName: '', + projectAddress: '', + deptUserName: '', + remark: '' }, rules: { - + projectName: [{ required: true, message: "项目名称不能为空", trigger: "blur" }], + projectDateStart: [{ required: true, message: "项目预计时间不能为空", trigger: "change" }], + projectAddress: [{ required: true, message: "项目地点不能为空", trigger: "blur" }], + deptUserName: [{ required: true, message: "处室带队人员不能为空", trigger: "blur" }], + remark: [{ required: true, message: "概况描述不能为空", trigger: "blur" }], } }) -const searchTime = ref([]); + +const userInfo = ref() +onMounted(() => { + userInfo.value = JSON.parse(Cookies.get('userInfo')) + state.formData.deptName = userInfo.value.dept.deptName + state.formData.deptId = userInfo.value.deptId +}) + + const riskOpen = async (type,val) => { console.log("type",type,val) if(type === 'add'){ - state.formData.startTime = searchTime.value[0] - state.formData.endTime = searchTime.value[1] //保存按钮 - //成功后自动到下一步 项目id - emit('getNextStatus', 1); - reset() - + const {id,...data} = state.formData + const res = await addProject(data); + if(res.code == 200){ + ElMessage.success('新增成功') + emit('getNextStatus', res.data); + reset() + }else{ + ElMessage.warning(res.message) + } }else if(type === 'clickEdit'){ //变更按钮 + const {...data} = state.formData + const res = await addProject(data); + if(res.code == 200){ + ElMessage.success('修改成功') + }else{ + ElMessage.warning(res.message) + } }else if(type === 'detail'){ - console.log("view111111111111111",type,val) + const res = await getProjectInfo(val) + if(res.code == 200){ + if(res.data) { + const val = res.data + for(let i in state.formData) { + if (validKey(i, val)) { + state.formData[i] = val[i] + } + } + searchTime.value = [res.data.projectDateStart,res.data.projectDateEnd] + } + + }else{ + ElMessage.warning(res.message) + } } } +const validKey=(key,obj)=>{ + return key in obj +} const reset = () => { + state.formData = { + deptId: null, + deptName:'危险化学品监督管理处', + projectName: '', + projectDateStart: '', + projectDateEnd: '', + companyName: '', + projectAddress: '', + deptUserName: '', + remark: '' + } searchTime.value = []; } const changeTime=(value)=>{ if(!value){ - state.formData.startTime = "" - state.formData.endTime = "" + state.formData.projectDateStart = "" + state.formData.projectDateEnd = "" + }else { + state.formData.projectDateStart = searchTime.value[0] + state.formData.projectDateEnd = searchTime.value[1] } } defineExpose({ diff --git a/src/views/safetyReview/projectManage/components/chooseExpert.vue b/src/views/safetyReview/projectManage/components/chooseExpert.vue index 59ffcd2..579d5c1 100644 --- a/src/views/safetyReview/projectManage/components/chooseExpert.vue +++ b/src/views/safetyReview/projectManage/components/chooseExpert.vue @@ -13,7 +13,11 @@ </template> </el-table-column> <el-table-column label="身份证号" prop="idCard" align="center" /> - <el-table-column label="等级" prop="level" align="center" /> + <el-table-column label="等级" prop="ratingLevel" align="center" > + <template #default="scope"> + <span>{{scope.row.ratingLevel == 1 ?'一级':scope.row.ratingLevel == 1?'二级':'三级'}}</span> + </template> + </el-table-column> <el-table-column label="专业领域" prop="domain" align="center" /> <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="180"> <template #default="scope"> @@ -43,10 +47,8 @@ </el-col> <el-col :span="8"> <el-form-item label="允许级别:"> - <el-checkbox-group v-model="checkList"> - <el-checkbox label="一级" value="一级" /> - <el-checkbox label="二级" value="二级" /> - <el-checkbox label="三级" value="三级" /> + <el-checkbox-group v-model="queryParams.ratingLevel"> + <el-checkbox v-for="item in state.levelList" :label="item.id" :key="item.id">{{ item.name }}</el-checkbox> </el-checkbox-group> </el-form-item> </el-col> @@ -60,6 +62,7 @@ <el-col :span="24"> <el-form-item label="拟选用人数:"> <el-input + clearable v-model.trim="queryParams.num" placeholder="请输入拟选用人数" @input="queryParams.num = queryParams.num.replace(/[^0-9]/g,' ')" @@ -77,11 +80,15 @@ </template> </el-table-column> <el-table-column label="身份证号" prop="idCard" align="center" /> - <el-table-column label="等级" prop="level" align="center" /> + <el-table-column label="等级" prop="ratingLevel" align="center" > + <template #default="scope"> + <span>{{scope.row.ratingLevel == 1 ?'一级':scope.row.ratingLevel == 1?'二级':'三级'}}</span> + </template> + </el-table-column> <el-table-column label="专业领域" prop="domain" align="center" /> <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="180"> <template #default="scope"> - <el-button link type="primary" @click="delF(scope.row)" >删除</el-button> + <el-button link type="primary" @click="delR(scope.row)" >删除</el-button> </template> </el-table-column> </el-table> @@ -91,10 +98,11 @@ </template> <script setup> -import {onMounted, ref} from "vue"; +import {onMounted, reactive, ref} from "vue"; import expertsList from './expertsList.vue' import {getExpertTypes} from "@/api/form"; import {ElMessage} from "element-plus"; +import {addProject, choose, expertRound, getCheckInfo} from "@/api/projectManage"; const emit = defineEmits(["getNextStatus"]); const fTableRef = ref(null); @@ -110,6 +118,29 @@ const expertType = ref([]) const checkList = ref([]) const domainList = ref([]) +const state = reactive({ + formData: { + id:null, + saveData: [], + delData: [] + }, + levelList: [ + { + id: 1, + name: '一级' + }, + { + id: 2, + name: '二级' + }, + { + id: 3, + name: '三级' + }, + ] +}) + + onMounted(() =>{ getDomain() @@ -122,44 +153,154 @@ return; } } - //保存按钮 if(type === 'add'){ + let fData = [] + let rData = [] //成功后自动到下一步 项目id - emit('getNextStatus', 2); + const {...data} = state.formData + data.id = val + if(fixedDataList.value && fixedDataList.value.length > 0){ + fData = fixedDataList.value.map(item => { + return { + expertId: item.id, + expertName:item.name, + selectionMode: 1, + teamLeader: null + } + }) + } + if(randomDataList.value && randomDataList.value.length > 0){ + rData = randomDataList.value.map(item => { + return { + expertId: item.id, + expertName:item.name, + selectionMode: 2, + teamLeader: null + } + }) + } + data.saveData = fData.concat(rData) + const res = await choose(data); + if(res.code == 200){ + ElMessage.success('新增成功') + emit('getNextStatus', val); + reset() + }else{ + ElMessage.warning(res.message) + } }else if(type === 'clickEdit'){ - //变更按钮 + let fData = [] + let rData = [] + const {...data} = state.formData + data.id = val + if(fixedDataList.value && fixedDataList.value.length > 0){ + fData = fixedDataList.value.map(item => { + return { + id: item.expertId ? item.id : '', + expertId: item.expertId ? item.expertId : item.id, + expertName:item.name, + selectionMode: 1, + teamLeader: null + } + }) + } + if(randomDataList.value && randomDataList.value.length > 0){ + rData = randomDataList.value.map(item => { + return { + id: item.expertId ? item.id : '', + expertId: item.expertId ? item.expertId : item.id, + expertName:item.name, + selectionMode: 2, + teamLeader: null + } + }) + } + data.saveData = fData.concat(rData) + const res = await choose(data); + if(res.code == 200){ + ElMessage.success('修改成功') + }else{ + ElMessage.warning(res.message) + } }else if(type === 'detail'){ - console.log("view222222",type,val) + const param = { + id: val + } + const res = await getCheckInfo(param); + if(res.code == 200){ + fixedDataList.value = res.data.projectExpertCheckResp.filter(item => item.selectionMode === 1) + randomDataList.value = res.data.projectExpertCheckResp.filter(item => item.selectionMode === 2) + console.log('res',fixedDataList.value) + }else{ + ElMessage.warning(res.message) + } } } const chooseExpert = (type) => { - expertsListRef.value.openDialog(type,fixedDataList.value) + expertsListRef.value.openDialog(type,fixedDataList.value,domainList.value) } const getSelected = (type,data)=>{ - fixedDataList.value = JSON.parse(JSON.stringify(data)) + const selectData = JSON.parse(JSON.stringify(data)) + if(fixedDataList.value && fixedDataList.value.length > 0){ + fixedDataList.value.forEach(item => { + if(item.expertId){ + selectData.forEach((i,index) => { + if(i.id == item.expertId){ + selectData[index] = item + } + }) + } + }) + } + fixedDataList.value = selectData } const delF = (val) => { + if(val.expertId){ + state.formData.delData.push(val.id) + } fixedDataList.value = fixedDataList.value.filter(item => item.id != val.id) } +const delR = (val) => { + if(val.expertId){ + state.formData.delData.push(val.id) + } + randomDataList.value = randomDataList.value.filter(item => item.id != val.id) +} const professionChange=(value)=> { - console.log(value,'val') + queryParams.value.domain = value.map(item => item[1]) } const getDomain = async () => { - // const res = await getExpertTypes() - // if(res.code == 200){ - // domainList.value = res.data - // }else{ - // ElMessage.warning(res.message) - // } + const res = await getExpertTypes() + if(res.code == 200){ + domainList.value = res.data + }else{ + ElMessage.warning(res.message) + } } -const random = () => { +const random = async () => { + console.log('11',randomDataList.value) + randomDataList.value.forEach(item => { + if(item.expertId){ + state.formData.delData.push(item.id) + } + }) + if(queryParams.value.num =='' || queryParams.value.num == undefined){ + ElMessage.warning('拟选用人数不能为空') + return + } rLoading.value = true + const res = await expertRound(queryParams.value) + if(res.code == 200){ + randomDataList.value = res.data + }else{ + ElMessage.warning(res.message) + } rLoading.value = false } diff --git a/src/views/safetyReview/projectManage/components/expertsList.vue b/src/views/safetyReview/projectManage/components/expertsList.vue index fedffc6..2595871 100644 --- a/src/views/safetyReview/projectManage/components/expertsList.vue +++ b/src/views/safetyReview/projectManage/components/expertsList.vue @@ -21,7 +21,7 @@ /> </el-form-item> <el-form-item label="等级"> - <el-select v-model="queryParams.level" style="width: 250px" placeholder="等级" > + <el-select v-model="queryParams.ratingLevel" style="width: 250px" placeholder="等级" > <el-option v-for="item in levelList" :key="item.id" @@ -104,9 +104,9 @@ pageSize: 10, name: '', phone: '', - level: '', - domain: null, - post: '' + ratingLevel: '', + smallClassify: null, + bigClassify: null, }, total: 0, choosed: [], @@ -134,16 +134,24 @@ const tableRef = ref() const { queryParams, total, expertList, domainList, levelList , dialogVisible,title,selected, expertType } = toRefs(data); -const openDialog = (type,choosedData) => { - getDomain() +const openDialog = (type,choosedData,domainList) => { research() - data.choosed = choosedData - data.choosed.forEach(item => { - tableRef.value.toggleRowSelection(item, true); + data.dialogVisible = true + data.choosed = choosedData.map(item => { + return{ + ...item, + id: item.expertId ? item.expertId : item.id + } }) + nextTick(() => { + data.choosed.forEach(item => { + tableRef.value.toggleRowSelection(item, true); + }) + }) + data.domainList = domainList data.selected = [] data.title = type - data.dialogVisible = true + } const onSubmit = async () => { @@ -162,7 +170,7 @@ loading.value = true; const res = await getExpertsList(data.queryParams) if(res.code == 200){ - data.expertList = res.data.list.map(item => { + data.expertList = res.rows.map(item => { return{ ...item, birthdayName: item.birthday ? item.birthday.slice(0,10) : '', @@ -190,7 +198,7 @@ } }) }) - data.total = res.data.total + data.total = res.total }else{ ElMessage.warning(res.msg) } @@ -201,14 +209,14 @@ projectRef.value.openDialog(val); } -const getDomain = async () => { - const res = await getExpertTypes() - if(res.code == 200){ - data.domainList = res.data - }else{ - ElMessage.warning(res.message) - } -} +// const getDomain = async () => { +// const res = await getExpertTypes() +// if(res.code == 200){ +// data.domainList = res.data +// }else{ +// ElMessage.warning(res.message) +// } +// } const select = ((selection, row) => { // data.expertList.forEach(item => { @@ -241,15 +249,17 @@ pageSize: 10, name: '', phone: '', - level: '', - domain: null, - post: '' + ratingLevel: '', + smallClassify: null, + bigClassify: null, } getList() } const professionChange=(value)=> { console.log(value,'val') + data.queryParams.bigClassify = value[0] + data.queryParams.smallClassify = value[1] } defineExpose({ diff --git a/src/views/safetyReview/projectManage/components/exportWord.js b/src/views/safetyReview/projectManage/components/exportWord.js new file mode 100644 index 0000000..32816b3 --- /dev/null +++ b/src/views/safetyReview/projectManage/components/exportWord.js @@ -0,0 +1,55 @@ +//引入工具 +import PizZip from 'pizzip'; +import Docxtemplater from 'docxtemplater'; +import JSZipUtils from 'jszip-utils'; +import { saveAs } from 'file-saver'; + +// 加载 .docx 模板文件 +function loadFile(url, callback) { + JSZipUtils.getBinaryContent(url, callback); +} + +// 下载生成的文档 +export function download(file, name) { + +} + +// 生成并下载 Word 文档(templatePath是word文档模版地址,data是对应的数据) +export function generateWordDocument(templatePath, data) { + loadFile(templatePath, function (error, content) { + if (error) { + throw error + return; + } + + try { + // 加载模板文件内容到 PizZip + const zip = new PizZip(content); + const doc = new Docxtemplater(zip, { + paragraphLoop: true, + linebreaks: true, + }); + + // 设置模板中的占位符数据 + doc.setData(data); + + // 渲染文档 + doc.render(); + + // 生成最终的文档 Blob + const fileWord = doc.getZip().generate({ + type: 'blob', + mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + }); + + saveAs(fileWord, '专家选用审批单.docx'); + + // // 返回生成的文档 Blob + // resolve(fileWord); + } catch (error) { + console.error('Error rendering document:', error); + throw error + } + }); + +} diff --git a/src/views/safetyReview/projectManage/components/projectApproval.vue b/src/views/safetyReview/projectManage/components/projectApproval.vue index 6296004..8a854d5 100644 --- a/src/views/safetyReview/projectManage/components/projectApproval.vue +++ b/src/views/safetyReview/projectManage/components/projectApproval.vue @@ -4,36 +4,36 @@ <el-row :gutter="24"> <el-col :span="12"> <el-form-item label="项目名称:"> - <span>{{state.dataForm.name}}</span> + <span>{{state.dataForm.projectName}}</span> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="被检查/对接单位:"> - <span>{{state.dataForm.unit}}</span> + <span>{{state.dataForm.companyName ? state.dataForm.companyName: '--'}}</span> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="12"> <el-form-item label="计划实行时间:"> - <span>{{state.dataForm.time}}</span> + <span>{{state.dataForm.projectDateStart}}—{{state.dataForm.projectDateEnd}}</span> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="项目地点:"> - <span>{{state.dataForm.address}}</span> + <span>{{state.dataForm.projectAddress}}</span> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="项目概况:"> - <span>{{state.dataForm.description}}</span> + <span>{{state.dataForm.remark}}</span> </el-form-item> </el-col> </el-row> </el-form> - <el-table v-loading="state.loading" :data="state.dataList" :border="true" ref="tableRef" style="width: 100%;"> + <el-table v-loading="state.loading" row-key="id" :data="state.dataList" :border="true" ref="tableRef" style="width: 100%;"> <el-table-column label="姓名" prop="name" align="center" /> <el-table-column label="性别" prop="sex" align="center" > <template #default="scope"> @@ -41,37 +41,30 @@ </template> </el-table-column> <el-table-column label="身份证号" prop="idCard" align="center" /> - <el-table-column label="等级" prop="level" align="center" /> - <el-table-column label="专业领域" prop="domain" align="center" /> - <el-table-column label="选用方式" prop="type" align="center" > + <el-table-column label="等级" prop="ratingLevel" align="center" > <template #default="scope"> - {{scope.row.type === 0 ? '固定' : '随机' }} + <span>{{scope.row.ratingLevel == 1 ?'一级':scope.row.ratingLevel == 1?'二级':'三级'}}</span> </template> </el-table-column> - <el-table-column label="是否为组长" prop="isLeader" align="center" > + <el-table-column label="专业领域" prop="domain" align="center" /> + <el-table-column label="选用方式" prop="selectionMode" align="center" > <template #default="scope"> - <span v-if="scope.row.isLeader === 0" style="color: red">是</span> + {{scope.row.selectionMode === 1 ? '固定' : '随机' }} + </template> + </el-table-column> + <el-table-column label="是否为组长" prop="teamLeader" align="center" > + <template #default="scope"> + <span v-if="scope.row.teamLeader === 1" style="color: red">是</span> <span v-else>否</span> </template> </el-table-column> <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="180"> <template #default="scope"> - <el-button link type="primary" v-if="scope.row.isLeader === 0" @click="handleLeader(scope.row)">取消组长</el-button> - <el-button link type="primary" v-else @click="handleLeader(scope.row)">设为组长</el-button> + <el-button link type="primary" v-if="scope.row.teamLeader === 1" @click="scope.row.teamLeader = 0">取消组长</el-button> + <el-button link type="primary" v-else @click="scope.row.teamLeader = 1">设为组长</el-button> </template> </el-table-column> </el-table> -<!-- <div class="pag-container">--> -<!-- <el-pagination--> -<!-- v-model:current-page="state.queryParams.pageNum"--> -<!-- v-model:page-size="state.queryParams.pageSize"--> -<!-- :page-sizes="[10,15,20,25]"--> -<!-- layout="total, sizes, prev, pager, next, jumper"--> -<!-- :total="state.total"--> -<!-- @size-change="handleSizeChange"--> -<!-- @current-change="handleCurrentChange"--> -<!-- />--> -<!-- </div>--> <el-dialog v-model="dialogVisible" width="600px" @@ -81,14 +74,14 @@ <span style="font-size: 18px;font-weight: 600">审批填报:</span> <div style="display: flex;align-items: flex-end;margin-top: 15px"> <img :src="wordSvg" style="width: 30px;height: 35px" /> - <el-button style="width: 110px;margin-left: 10px" type="primary" >下载审批单word</el-button> + <el-button style="width: 110px;margin-left: 10px" type="primary" @click="startGeneration">下载审批单word</el-button> </div> </div> </div> <div style="margin-top: 30px"> <div style="display: flex"> <span style="font-size: 18px;font-weight: 600">审批记录附件:</span> - <el-upload :disabled="projectType==='view'" accept=".pdf" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :on-preview="handlePictureCardPreview" :limit='1' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles,5)"> + <el-upload :disabled="projectType==='view'" accept=".pdf" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :on-preview="handlePictureCardPreview" :limit='1' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)"> <el-button type="primary">上传附件PDF</el-button> <template #tip> <div class="el-upload__tip">上传文件尺寸小于5M,最多可上传1份</div> @@ -112,17 +105,22 @@ import {ElMessage, ElMessageBox} from "element-plus"; import {getToken} from "@/utils/auth"; import axios from "axios"; -const emit = defineEmits(["getNextStatus"]); +import {generateWordDocument} from "@/views/safetyReview/projectManage/components/exportWord"; +import {getCheckInfo, getProjectList} from "@/api/projectManage"; +const emit = defineEmits(["getNextStatus","backStatus"]); +const prop = defineProps(['projectId']) const tableRef = ref() const dialogVisible = ref(false); const state = reactive({ dataForm: { - name: 'xxx', - unit: 'xxx', - time: '2024年11月1日-2024年11月25日', - address: 'xxx', - description: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' - + deptName: '', + projectName: '', + companyName: '', + projectDateStart: '', + projectDateEnd: '', + projectAddress: '', + remark: '', + projectCheckTime: '' }, queryParams: { pageNum: 1, @@ -131,16 +129,20 @@ loading: false, dataList: [], total: 0, - uploadUrl: import.meta.env.VITE_APP_BASE_API + '', + uploadUrl: import.meta.env.VITE_APP_BASE_API + '/common/upload', header: { Authorization: getToken() }, - fileList: [] + fileList: [], + projectId: null }); const projectType = ref('') - onMounted(async () => { - await getList() + if(prop.projectId){ + state.projectId = prop.projectId + await getList() + } + }) const riskOpen = async (type,val) => { @@ -164,62 +166,48 @@ } const nextStatus = () =>{ + if(state.fileList && state.fileList.length ===0){ + state.fileList = [] + ElMessage({ + type: 'warning', + message: '请上传审批记录附件' + }) + return; + } dialogVisible.value = false emit('getNextStatus', 3); } const back = () => { + // emit('backStatus', 1);//回到状态1 dialogVisible.value = false } const getList = async () => { state.loading = true; - state.dataList = [ - { - id:1, - name: 'xxx', - idCard: 'xxx', - isLeader: 0, - level: '二级', - domain: 'xxx', - type: 0 - }, - { - id:2, - name: 'xxx', - idCard: 'xxx', - isLeader: 1, - level: '二级', - domain: 'xxx', - type: 1 - } - ] - // state.total = 1 - state.loading = false; -} - -// const handleSizeChange = (val) => { -// state.queryParams.pageNum = 1; -// state.queryParams.pageSize = val -// getList() -// } -// const handleCurrentChange = (val) => { -// state.queryParams.pageNum = val -// getList() -// } - -const handleLeader = (val) => { - state.dataList.forEach(item => { - if(val.id === item.id){ - if(val.isLeader === 0){ - item.isLeader = 1 - }else { - item.isLeader = 0 + const param = { + id:state.projectId + } + const res = await getCheckInfo(param); + if(res.code == 200){ + state.dataList = res.data.projectExpertCheckResp + if(res.data) { + const val = res.data + for(let i in state.dataForm) { + if (validKey(i, val)) { + state.dataForm[i] = val[i] + } } + state.dataForm.projectCheckTime = res.data.projectCheckTime ?res.data.projectCheckTime : '' } - }) - + console.log('11',state.dataForm) + state.loading = false; + }else{ + ElMessage.warning(res.message) + } } - +const validKey=(key,obj)=>{ + return key in obj +} const handleAvatarSuccess = (res, uploadFile) => { if(res.code == 200){ state.fileList = res.data.map(i=>{ @@ -303,6 +291,29 @@ } } +const templatePath = '/example.docx'; +const startGeneration = async () => { + const data = state.dataForm + data.companyName = data.companyName != '' ? data.companyName: '-' + data.projectCheckTime = data.projectCheckTime != '' ? data.projectCheckTime: '-' + data.tableData = state.dataList.map(item => { + return { + ...item, + ratingLevel: item.ratingLevel == 1 ? '一级' : item.ratingLevel == 2 ? '二级':item.ratingLevel == 3 ? '三级' : '', + sex: item.sex == 0 ? '男' : '女', + selectionMode: item.selectionMode == 1 ? '固定' : '随机', + teamLeader: item.teamLeader == 1 ? '是' : '否', + } + }) + try { + generateWordDocument(templatePath, data); + } catch (error){ + ElMessage({ + type: 'warning', + message: '失败' + }); + } +}; defineExpose({ riskOpen diff --git a/src/views/safetyReview/projectManage/components/projectArchive.vue b/src/views/safetyReview/projectManage/components/projectArchive.vue index 04c1d1f..6e365ec 100644 --- a/src/views/safetyReview/projectManage/components/projectArchive.vue +++ b/src/views/safetyReview/projectManage/components/projectArchive.vue @@ -1,14 +1,14 @@ <template> <div class="archive"> <el-form style="display: flex;flex-direction: column" :model="state.dataForm" ref="dataForm" :inline="true" :rules="state.rules" label-width="130px" > - <el-form-item prop="deptName" label="结束日期:"> + <el-form-item prop="endTime" label="结束日期:"> <el-date-picker v-model="state.dataForm.endTime" type="date" placeholder="请选择结束日期" /> </el-form-item> - <el-form-item prop="deptName" label="总结描述:"> + <el-form-item prop="description" label="总结描述:"> <el-input type="textarea" :rows="8" @@ -19,7 +19,19 @@ </el-input> </el-form-item> <el-form-item label="附件上传:"> - <el-upload :disabled="projectType==='view'" accept=".pdf" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :on-preview="handlePictureCardPreview" :limit='1' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles,5)"> + <el-upload + :disabled="projectType==='view'" + accept=".pdf" + :action="state.uploadUrl" + :headers="state.header" + method="post" + :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" + :on-exceed="showTip" + :on-preview="handlePictureCardPreview" + :limit='1' + v-model:file-list="state.fileList" + :before-upload="picSize" + :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)"> <el-button type="primary">上传附件PDF</el-button> <template #tip> <div class="el-upload__tip">上传文件尺寸小于5M,最多可上传1份</div> @@ -43,7 +55,10 @@ endTime: '', summarize:'' }, - rules: {}, + rules: { + description: [{ required: true, message: "总结描述不能为空", trigger: "blur" }], + endTime:[{ required: true, message: "结束日期不能为空", trigger: "blur" }] + }, uploadUrl: import.meta.env.VITE_APP_BASE_API + '', header: { Authorization: getToken() diff --git a/src/views/safetyReview/projectManage/index.vue b/src/views/safetyReview/projectManage/index.vue index e5e37f2..f77bed7 100644 --- a/src/views/safetyReview/projectManage/index.vue +++ b/src/views/safetyReview/projectManage/index.vue @@ -19,7 +19,7 @@ <div>项目</div> <div>总数</div> </div> - <div class="font-right">{{search.num.projectTotal}}</div> + <div class="font-right">{{search.num.total}}</div> </div> <div class="box-right"> <div class="inbox" @click="choose(1)" style="box-shadow: rgba(132, 122, 253, 0.2) 0 3px 5px 0;" :class="{btn1: chooseType === 1}"> @@ -27,7 +27,7 @@ <span class="top-right-font">基本信息</span> </div> <div class="bottom"> - <span class="top-right-font-bottom">{{search.num.riskTotal}}</span> + <span class="top-right-font-bottom">{{search.num.staging}}</span> </div> </div> <div class="inbox" @click="choose(2)" style="box-shadow: rgba(255, 142, 139, 0.15) 0 3px 5px 0;" :class="{btn2: chooseType === 2}"> @@ -35,7 +35,7 @@ <span class="top-right-font">专家选用</span> </div> <div class="bottom"> - <span class="top-right-font-bottom">{{search.num.investigationTotal}}</span> + <span class="top-right-font-bottom">{{search.num.expert}}</span> </div> </div> <div class="inbox" @click="choose(3)" style="box-shadow: rgba(222, 106, 169, 0.15) 0 3px 5px 0;" :class="{btn3: chooseType === 3}"> @@ -43,7 +43,7 @@ <span class="top-right-font">项目审批</span> </div> <div class="bottom"> - <span class="top-right-font-bottom">{{search.num.reviewTotal}}</span> + <span class="top-right-font-bottom">{{search.num.approval}}</span> </div> </div> <div class="inbox" @click="choose(4)" style="box-shadow: rgba(109, 177, 254, 0.2) 0 3px 5px 0;" :class="{btn4: chooseType === 4}"> @@ -51,7 +51,7 @@ <span class="top-right-font">项目归档</span> </div> <div class="bottom"> - <span class="top-right-font-bottom">{{search.num.reportTotal}}</span> + <span class="top-right-font-bottom">{{search.num.end}}</span> </div> </div> </div> @@ -61,11 +61,14 @@ <el-table v-loading="loading" :data="dataList" :border="true" ref="tableRef" :height="tableHeight" style="width: 100%;"> <el-table-column label="序号" width="80" align="center" type="index" ></el-table-column> <el-table-column label="申请时间" prop="createTime" align="center" :show-overflow-tooltip="true" width="180" /> - <el-table-column label="项目名称" prop="name" align="center" :show-overflow-tooltip="true" width="180" /> - <el-table-column label="部门" prop="dept" align="center" /> - <el-table-column label="专家组组长" prop="leader" align="center" width="150"/> - <el-table-column label="专家组人数" prop="num" align="center" width="150"/> + <el-table-column label="项目名称" prop="projectName" align="center" :show-overflow-tooltip="true" width="180" /> + <el-table-column label="部门" prop="deptName" align="center" /> + <el-table-column label="专家组组长" prop="deptUserName" align="center" width="150"/> + <el-table-column label="专家组人数" prop="expertNum" align="center" width="150"/> <el-table-column label="当前状态" prop="state" align="center" width="200"> + <template #default="scope"> + <span>{{scope.row.state === 0 ? '暂存' : scope.row.state === 1 ? '生成审批单' : scope.row.state === 2 ? '审批通过' :scope.row.state === 3 ?'审批驳回' : '完结' }}</span> + </template> </el-table-column> <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="180"> <template #default="scope"> @@ -103,17 +106,10 @@ /> </el-form-item> <el-form-item label="项目名称" > - <el-input v-model.trim="search.queryParams.name" placeholder="请输入项目名称"></el-input> + <el-input v-model.trim="search.queryParams.projectName" placeholder="请输入项目名称"></el-input> </el-form-item> - <el-form-item label="当前状态" > - <el-select v-model="search.queryParams.state" class="m-2" size="large" placeholder="请选择" style="width: 100%" > - <el-option - v-for="item in stateList" - :key="item.id" - :label="item.label" - :value="item.id" - /> - </el-select> + <el-form-item label="部门名称" > + <el-input v-model.trim="search.queryParams.deptName" placeholder="请输入部门名称"></el-input> </el-form-item> </el-form> </div> @@ -140,6 +136,7 @@ // import {getDict} from "@/api/login"; // import {getDictList} from "@/api/backManage/evaluate"; import Cookies from "js-cookie" +import {delProject, getProjectList, getProjectNum} from "@/api/projectManage"; const router = useRouter(); const loading = ref(false); @@ -155,20 +152,18 @@ queryParams: { pageNum: 1, pageSize: 20, - name: '', - phase: '', - state: null, - params:{ + projectName: '', + projectDateStart: '', + projectDateEnd: '', + step: null - } }, num: { - projectTotal: 0, - riskTotal: 0, - investigationTotal: 0, - reviewTotal: 0, - reportTotal: 0, - archiveTotal: 0 + approval: 0, + end: 0, + expert: 0, + staging: 0, + total: 0 } }); const searchTime = ref([]); @@ -217,55 +212,27 @@ const chooseType = ref(''); const choose = (val) => { chooseType.value = val; - search.queryParams.phase = val; + search.queryParams.step = val; getList(); } const getList = async () => { loading.value = true; - dataList.value = [ - { - id:1, - createTime: 'xxx', - name: 'xxx', - dept: 'xxx', - leader: '张三', - num: 10, - state: 1, - reportProgress: 1 - - } - ] - // const res = await getProjectList(search.queryParams); - // if(res.code == 200){ - // dataList.value = res.data.list.map(item => { - // return { - // ...item, - // process: item.reportProgress <= 4 ? 1 : item.reportProgress > 4 && item.reportProgress <= 6 ? 2 : item.reportProgress > 6 && item.reportProgress <= 9 ? 3 : item.reportProgress > 9 && item.reportProgress <= 11 ? 4 : 5, - // leaderName: item.leader ? item.leader.name : '', - // area: item.district ? item.province + '/' + item.city + '/' + item.district : item.city != item.province ? item.province + '/' + item.city: item.province , - // filingDate: item.filingDate ? conversionDays(item.filingDate,item.createTime) : conversionDays('',item.createTime), - // contractMoney: item.contract ? item.contract.contractMoney : '', - // actualContractMoney: item.contract ? item.contract.actualContractMoney : '', - // activeConfirm: item.personRecognition ? item.personRecognition.recognitionCnt + '/' + item.personRecognition.personCnt: '', - // missingMaterialCnt: 3-item.materialCnt, - // activeConfirmOut: item.personRecognition ? "\'" + item.personRecognition.recognitionCnt + '/' + item.personRecognition.personCnt: '', - // } - // }) - total.value = 1 - loading.value = false; - // console.log('11',dataList.value) - // }else{ - // ElMessage.warning(res.message) - // } + const res = await getProjectList(search.queryParams); + if(res.code == 200){ + dataList.value = res.rows + total.value = res.total + loading.value = false; + }else{ + ElMessage.warning(res.message) + } } const getStatistics = async () => { - // const {pageNum,pageSize, ...data} = JSON.parse(JSON.stringify(search.queryParams)) - // const res = await getProjectStatistics(data); - // if(res.code == 200){ - // search.num = res.data - // }else{ - // ElMessage.warning(res.message) - // } + const res = await getProjectNum(); + if(res.code == 200){ + search.num = res.data + }else{ + ElMessage.warning(res.message) + } } const handleSizeChange = (val) => { search.queryParams.pageNum = 1; @@ -278,7 +245,7 @@ } const toProcess = (type,value,toPath) => { value.type = type; - router.push({ path: '/process', query: {id: value.id, type: type, toPath: toPath, process: value.reportProgress}}); + router.push({ path: '/process', query: {id: value.id, type: type, toPath: toPath, process: value.step}}); } const del = (val) => { @@ -291,7 +258,7 @@ type: 'warning', }) .then( async() => { - const res = await delProject(val) + const res = await delProject(val.id) if(res.code == 200){ ElMessage.success('数据删除成功') await getList() @@ -308,14 +275,14 @@ } const changeTime=(value)=>{ if(!value){ - search.queryParams.params.endTime = "" - search.queryParams.params.startTime = "" + search.queryParams.projectDateStart = "" + search.queryParams.projectDateEnd = "" } } const searchClick = () => { if(searchTime.value && searchTime.value.length>0){ - search.queryParams.params.startTime = searchTime.value[0] - search.queryParams.params.endTime = searchTime.value[1] + search.queryParams.projectDateStart = searchTime.value[0] + search.queryParams.projectDateEnd = searchTime.value[1] } getList(); getStatistics(); @@ -326,12 +293,10 @@ search.queryParams = { pageNum: 1, pageSize: 20, - name: '', - phase: '', - state: null, - params:{ - - } + projectName: '', + projectDateStart: '', + projectDateEnd: '', + step: null } chooseType.value = ''; getList(); diff --git a/src/views/safetyReview/projectManage/process.vue b/src/views/safetyReview/projectManage/process.vue index cd85d77..c904f21 100644 --- a/src/views/safetyReview/projectManage/process.vue +++ b/src/views/safetyReview/projectManage/process.vue @@ -27,7 +27,7 @@ <div :style="'height:' + middleContentHeight + 'px'" style="overflow-y: scroll;"> <basic-info ref="basicRef" v-if="selectedObj.id === 1" :projectId="projectId" @getNextStatus="getNextStatus"></basic-info> <expert ref="expertRef" v-if="selectedObj.id === 2" :projectId="projectId" @getNextStatus="getNextStatus"></expert> - <approval ref="approvalRef" v-if="selectedObj.id === 3" :projectId="projectId" @getNextStatus="getNextStatus"></approval> + <approval ref="approvalRef" v-if="selectedObj.id === 3" :projectId="projectId" @getNextStatus="getNextStatus" @back-status="backStatus" ></approval> <archive ref="archiveRef" v-if="selectedObj.id === 4" :projectId="projectId" @getNextStatus="getNextStatus"></archive> </div> @@ -58,6 +58,7 @@ import archive from './components/projectArchive.vue' import {ElMessage} from "element-plus"; import {get} from "@vueuse/core"; +import {getProjectInfo} from "@/api/projectManage"; const selectedObj = ref({}) const nextObj = ref({}) @@ -172,66 +173,92 @@ } } -const getStatus = (id) => { +const getStatus = async (id) => { //projectId 项目id,根据id获取当前状态 projectId.value = Number(id) - //调接口 - const res = projectId.value - //当前项目状态,编辑、查看的时候定位到最新状态 - newProgress.value = res - menuList.value.forEach(item => { - if(projectStatus.value === 'edit'){ - if(item.id <= res){ - item.status = 2 + const res = await getProjectInfo(projectId.value) + if(res.code === 200) { + //当前项目状态,编辑、查看的时候定位到最新状态 + newProgress.value = res.data.step + menuList.value.forEach(item => { + if(projectStatus.value === 'edit'){ + if(item.id <= newProgress.value){ + item.status = 2 + } + if(item.id == newProgress.value){ + selectedObj.value =item + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } + if(item.id == newProgress.value +1){ + item.status = 1 + } + }else if(projectStatus.value === 'view'){ + if(item.id <= newProgress.value){ + item.status = 2 + } + if(item.id == newProgress.value){ + selectedObj.value =item + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } } - if(item.id == res){ + if( newProgress.value == 4 && item.id == 4){ + item.status = 2 selectedObj.value =item setTimeout(() => { goRouter(selectedObj.value.id) }, 10) } - if(item.id == res +1){ - item.status = 1 - } - }else if(projectStatus.value === 'view'){ - if(item.id <= res){ - item.status = 2 - } - if(item.id == res){ - selectedObj.value =item - setTimeout(() => { - goRouter(selectedObj.value.id) - }, 10) - } - } - if(res == 4 && item.id == 4){ - item.status = 2 - selectedObj.value =item - setTimeout(() => { - goRouter(selectedObj.value.id) - }, 10) - } - }) + }) + } + else { + ElMessage.warning(res.message) + } console.log('menuList.value',menuList.value) } -const getNextStatus = (id) => { +const getNextStatus = async (id) => { + projectId.value = Number(id) + //调接口 + const res = await getProjectInfo(projectId.value) + if(res.code === 200) { + newProgress.value = res.data.step + //定位到下一步状态 + menuList.value.forEach(item => { + if(item.id <= newProgress.value){ + item.status = 2 + }else if(item.id == newProgress.value + 1){ + item.status = 1 + selectedObj.value =item + } + + if( newProgress.value == 4 && item.id == 4){ + item.status = 2 + selectedObj.value =item + } + }) + }else { + ElMessage.warning(res.message) + } +} + +const backStatus = (id) => { //projectId 项目id,根据id获取当前状态 projectId.value = Number(id) //调接口 const res = projectId.value newProgress.value = res - //定位到下一步状态 + //审核不通过状态回到第二步 menuList.value.forEach(item => { if(item.id <= res){ item.status = 2 }else if(item.id == res + 1){ item.status = 1 selectedObj.value =item - } - - if(res == 4 && item.id == 4){ - item.status = 2 - selectedObj.value =item + }else if(item.id > res + 1){ + item.status = 0 } }) } -- Gitblit v1.9.2