From 52f0aa42410a2102a8fa1badfc8e467cae7d6a7c Mon Sep 17 00:00:00 2001 From: zhouwenxuan <1175765986@qq.com> Date: 星期二, 16 一月 2024 09:19:46 +0800 Subject: [PATCH] 项目管理 --- src/api/projectManage/project.js | 28 src/layout/components/Sidebar/menu.js | 10 src/api/projectManage/riskAnalysis.js | 25 src/views/safetyReview/projectManage/process.vue | 846 +++++++++++++++++++++++++++++ src/router/index.js | 27 src/views/safetyReview/projectManage/index.vue | 318 ++++++++++ src/views/safetyReview/projectManage/components/riskAnalysis.vue | 456 +++++++++++++++ 7 files changed, 1,710 insertions(+), 0 deletions(-) diff --git a/src/api/projectManage/project.js b/src/api/projectManage/project.js new file mode 100644 index 0000000..3f61973 --- /dev/null +++ b/src/api/projectManage/project.js @@ -0,0 +1,28 @@ +import request from '@/utils/request' + +export function getProjectList(params) { + return request({ + url: '/manage/project/list', + method: 'get', + params: params + + }) +} + +export function getProjectStatus(data) { + return request({ + url: '/manage/project/progress/' + data, + method: 'get' + + }) +} + +export function getProjectStatistics() { + return request({ + url: '/manage/project/statistics', + method: 'get' + + }) +} + + diff --git a/src/api/projectManage/riskAnalysis.js b/src/api/projectManage/riskAnalysis.js new file mode 100644 index 0000000..9610ae0 --- /dev/null +++ b/src/api/projectManage/riskAnalysis.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function addRisk(data) { + return request({ + url: '/manage/risk-estimate/add', + method: 'post', + data: data + }) +} + +export function editRisk(params) { + return request({ + url: `/manage/risk-estimate/edit`, + method: 'put', + data: params + }) +} + +export function getRiskDetail(data) { + return request({ + url: '/manage/risk-estimate/getRiskByProjectId', + method: 'get', + params: data + }) +} diff --git a/src/layout/components/Sidebar/menu.js b/src/layout/components/Sidebar/menu.js index 7a0d0c2..56f02e5 100644 --- a/src/layout/components/Sidebar/menu.js +++ b/src/layout/components/Sidebar/menu.js @@ -2,6 +2,11 @@ const menu = { adminMenu: [ { + path: '/project', + name: 'Project', + meta: { title: '项目管理',icon: 'form',affix: true } + }, + { path: '/institution', name: 'Institution', meta: { title: '机构公示',icon: 'form',affix: true } @@ -87,6 +92,11 @@ ], agencyMenu: [ { + path: '/project', + name: 'Project', + meta: { title: '项目管理',icon: 'form',affix: true } + }, + { path: '/userManage', redirect: '/userManage/supervise', meta: { title: '用户管理',icon: 'form'}, diff --git a/src/router/index.js b/src/router/index.js index e9967eb..782e60a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -130,6 +130,33 @@ ] }, { + path: '/project', + component: Layout, + redirect: '/project', + children: [ + { + path: '/project', + component: () => import('@/views/safetyReview/projectManage/index.vue'), + name: 'Project', + meta: { title: '项目管理',icon: 'form', affix: true } + } + ] + }, + { + path: '/process', + component: Layout, + redirect: '/process', + children: [ + { + path: '/process', + component: () => import('@/views/safetyReview/projectManage/process.vue'), + name: 'Process', + meta: { title: '项目信息管理'} + } + ] + }, + + { path: '/userManage', component: Layout, redirect: '/userManage/supervise', diff --git a/src/views/safetyReview/projectManage/components/riskAnalysis.vue b/src/views/safetyReview/projectManage/components/riskAnalysis.vue new file mode 100644 index 0000000..da3b0ae --- /dev/null +++ b/src/views/safetyReview/projectManage/components/riskAnalysis.vue @@ -0,0 +1,456 @@ +<template> + <div class="riskBox"> + <el-form ref="formRef" :model="state.formData" :rules="state.rules" class="register-form" label-position="top"> + <el-row :gutter="30"> + <el-col :span="6"> + <el-form-item prop="project.name" label="项目名称"> + <el-input + v-model="state.formData.project.name" + size="large" + placeholder="请输入项目名称" + > + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="project.client" label="委托单位"> + <el-input + v-model="state.formData.project.client" + size="large" + placeholder="请输入委托单位" + > + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="project.creditCode" label="委托单位统一社会信用代码"> + <el-input + v-model="state.formData.project.creditCode" + size="large" + placeholder="请输入委托单位统一社会信用代码" + > + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="project.address" label="详细地址"> + <el-input + maxlength="100" + show-word-limit + v-model="state.formData.project.address" + size="large" + placeholder="请输入详细地址" + > + </el-input> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="30"> + <el-col :span="6"> + <el-form-item prop="project.estimateType" label="评价类型"> + <el-select v-model="state.formData.project.estimateType" class="m-2" size="large" placeholder="请选择" style="width: 100%" > + <el-option + v-for="item in state.estimateTypeList" + :key="item.id" + :label="item.label" + :value="item.id" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="project.code" label="项目编号"> + <el-input + v-model="state.formData.project.code" + size="large" + placeholder="请输入项目编号" + > + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="project.invest" label="项目投资金额"> + <el-input + v-model="state.formData.project.invest" + size="large" + placeholder="请输入项目投资金额" + > + <template #append>万元</template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="project.area" label="所属区域"> + <el-cascader + v-model="state.formData.project.area" + :options="state.addressList" + :props="props" + @change="handleChange" + style="width: 100%" + size="large" + /> + </el-form-item> + </el-col> + </el-row> + <el-row el-row :gutter="30"> + <el-col :span="6"> + <el-form-item prop="project.business" label="业务范围"> + <el-select v-model="state.formData.project.business" class="m-2" size="large" placeholder="请选择" style="width: 100%" > + <el-option + v-for="item in state.businessList" + :key="item.id" + :label="item.label" + :value="item.id" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + <el-form-item prop="project.introduction" label="基本概括"> + <el-input + v-model="state.formData.project.introduction" + :autosize="{ minRows: 6 }" + maxlength="500" + show-word-limit + type="textarea"> + </el-input> + </el-form-item> + <el-form-item prop="riskCharacter" label="行业风险特性"> + <el-input + v-model="state.formData.riskCharacter" + :autosize="{ minRows: 6 }" + maxlength="500" + show-word-limit + type="textarea"> + </el-input> + </el-form-item> + <el-form-item prop="surroundings" label="周边环境"> + <el-input + v-model="state.formData.surroundings" + :autosize="{ minRows: 6 }" + maxlength="500" + show-word-limit + type="textarea"> + </el-input> + </el-form-item> + <el-form-item prop="equipment" label="主要生产装置"> + <el-input + v-model="state.formData.equipment" + :autosize="{ minRows: 6 }" + maxlength="500" + show-word-limit + type="textarea"> + </el-input> + </el-form-item> + <el-form-item prop="technology" label="生产工艺概况"> + <el-input + v-model="state.formData.technology" + :autosize="{ minRows: 6 }" + maxlength="500" + show-word-limit + type="textarea"> + </el-input> + </el-form-item> + <el-table :data="state.tableData" :border="true" style="margin: 20px 0"> + <el-table-column label="序号" width="60" align="center" type="index"></el-table-column> + <el-table-column label="内容" prop="content" header-align="center" :show-overflow-tooltip="true"/> + <el-table-column label="选择" header-align="center" class-name="small-padding fixed-width" width="175"> + <template #default="scope"> + <el-radio-group v-model="scope.row.status" > + <el-radio :label="1">{{scope.row.id ==='6' ? '可行': '是'}}</el-radio> + <el-radio :label="0">{{scope.row.id ==='6' ? '不可行': '否'}}</el-radio> + </el-radio-group> + </template> + </el-table-column> + </el-table> + <el-row :gutter="30"> + <el-col :span="6"> + <el-form-item prop="estimateDate" label="评估日期"> + <el-date-picker + style="width: 100%" + v-model="state.formData.estimateDate" + type="date" + value-format="YYYY-MM-DD 00:00:00" + placeholder="选择日期" + size="large" + /> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="techOpinion" label="技术负责人意见"> + <el-input + v-model="state.formData.techOpinion" + size="large" + maxlength="30" + show-word-limit + placeholder="技术负责人意见" + > + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="riskOpinion" label="风险评估人意见"> + <el-input + v-model="state.formData.riskOpinion" + size="large" + maxlength="30" + show-word-limit + placeholder="请输入风险评估人意见" + > + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item prop="agencyOpinon" label="机构评价负责人意见"> + <el-input + maxlength="30" + show-word-limit + v-model="state.formData.agencyOpinon" + size="large" + placeholder="请输入机构评价负责人意见" + > + </el-input> + </el-form-item> + </el-col> + </el-row> + + </el-form> + </div> + +</template> +<script setup> + +import {defineEmits, onMounted, reactive, ref} from "vue"; +import {getDictList} from "@/api/backManage/evaluate"; +import {ElMessage} from "element-plus"; +import {getDict} from "@/api/login"; +import {getRegionTree} from "@/api/area"; +import {addRisk, editRisk, getRiskDetail} from "@/api/projectManage/riskAnalysis"; +import Cookies from "js-cookie"; +const emit = defineEmits(["getNextStatus"]); + +const state = reactive({ + formData: { + project: { + id: '', + name: '', + client: '', + creditCode: '', + address:'', + estimateType: null, + code: '', + invest: '', + area: [], + district: '', + city: '', + province: '', + business: '', + introduction: '', + }, + id: '', + riskCharacter: '', + surroundings: '', + equipment: '', + technology: '', + isInBusiness: true, + isSatisfyNeed: true, + isNeedExpert: true, + isFinishReport: true, + isAcceptChargess: true, + isFeasibility: true, + estimateDate: '', + techOpinion: '', + riskOpinion: '', + agencyOpinon: '' + }, + rules: { + "project.name": [{required: true, message: '请填写项目名称', trigger: 'blur'}], + "project.client": [{required: true, message: '请填写委托单位', trigger: 'blur'}], + "project.creditCode": [{required: true, message: '请填写委托单位统一社会信用代码', trigger: 'blur'}], + "project.estimateType": [{required: true, message: '请选择评价类型', trigger: 'blur'}], + "project.address": [{required: true, message: '请填写详细地址', trigger: 'blur'}], + "project.invest": [{required: true, message: '请填写投资金额', trigger: 'blur'}], + "project.area": [{required: true, message: '请选择所属区域', trigger: 'blur'}], + "project.business": [{required: true, message: '请选择业务范围', trigger: 'blur'}], + "project.introduction": [{required: true, message: '请填写基本概况', trigger: 'blur'}], + riskCharacter: [{required: true, message: '请填写行业风险特性', trigger: 'blur'}], + surroundings: [{required: true, message: '请填写周边环境', trigger: 'blur'}], + estimateDate: [{required: true, message: '请选择评估日期', trigger: 'blur'}], + techOpinion: [{required: true, message: '请填写技术负责人意见', trigger: 'blur'}], + riskOpinion: [{required: true, message: '请填写风险评估人意见', trigger: 'blur'}], + agencyOpinon: [{required: true, message: '请填写机构评价负责人意见', trigger: 'blur'}], + }, + estimateTypeList: [], + addressList: [], + businessList: [], + tableData: [ + { + id: '1', + content: '评价项目是否在本机构资质业务范围内', + status: 1 + }, + { + id: '2', + content: '评价人员专业构成是否满足评价项目需要', + status: 1 + }, + { + id: '3', + content: '是否需要聘请相关专业的技术专家', + status: 1 + }, + { + id: '4', + content: '是否能在约定的时间内完成评价报告', + status: 1 + }, + { + id: '5', + content: '评价费用是否在本机构所接受的范围内', + status: 1 + }, + { + id: '6', + content: '项目的可行性(风险分析结论)', + status: 1 + }, + ] +}) +const props = { + expandTrigger: 'hover', + value: 'name', + label: 'name' +} +const isAmin = ref(false) +const formRef = ref(); +onMounted(() => { + const userInfo = JSON.parse(Cookies.get('userInfo')) + if(userInfo.identity === 0){ + isAmin.value = true; + } + getRiskList(); + getBusinessList(); + getArea(); +}); + +const getRiskList = async () => { + const res = await getDictList({dictType: "sys_assess_type"}); + if(res.code === 200){ + state.estimateTypeList = res.data.list + }else{ + ElMessage.warning(res.message) + } +} +const getBusinessList = async () => { + const res = await getDict({dictType: 'sys_business_scope'}) + if(res.code === 200){ + state.businessList = res.data + }else{ + ElMessage.warning(res.message) + } +} +const getArea = async ()=>{ + const type = 1 + const res = await getRegionTree({name: '',parentId: null,regionType: type}) + if(res.code == 200){ + state.addressList = res.data + }else{ + ElMessage.warning(res.message) + } +} +const riskOpen = async (type,val) => { + console.log("type",type,val) + if(type === 'detail' || type === 'edit' ){ + const res = await getRiskDetail({projectId: val}); + if(res.code == 200){ + state.formData = res.data; + state.formData.project.business = parseInt(res.data.project.business); + state.formData.project.area = [res.data.project.province,res.data.project.city]; + state.tableData[0].status = res.data.isInBusiness ? 1 : 0; + state.tableData[1].status = res.data.isSatisfyNeed ? 1 : 0; + state.tableData[2].status = res.data.isNeedExpert ? 1 : 0; + state.tableData[3].status = res.data.isFinishReport ? 1 : 0; + state.tableData[4].status = res.data.isAcceptChargess ? 1 : 0; + state.tableData[5].status = res.data.isFeasibility ? 1 : 0; + }else { + ElMessage.warning(res.message) + } + } + if(type === 'add' || type === 'clickEdit') { + const valid = await formRef.value.validate(); + if(valid){ + if (isAmin.value) { + ElMessage.warning("当前用户暂无权限"); + return; + } + if(type === 'add'){ + const {id, ...data} = JSON.parse(JSON.stringify(state.formData)) + delete data.project.area; + delete data.project.id; + data.isInBusiness = state.tableData[0].status === 1; + data.isSatisfyNeed = state.tableData[1].status === 1; + data.isNeedExpert = state.tableData[2].status === 1; + data.isFinishReport = state.tableData[3].status === 1; + data.isAcceptChargess = state.tableData[4].status === 1; + data.isFeasibility = state.tableData[5].status === 1; + console.log('data', data) + const res = await addRisk(data); + if (res.code == 200) { + ElMessage.success('保存成功') + formRef.value.clearValidate(); + emit('getNextStatus', res.data); + + } else { + ElMessage.warning(res.message) + } + }else if(type === 'clickEdit'){ + const { ...data} = JSON.parse(JSON.stringify(state.formData)) + delete data.project.area; + data.isInBusiness = state.tableData[0].status === 1; + data.isSatisfyNeed = state.tableData[1].status === 1; + data.isNeedExpert = state.tableData[2].status === 1; + data.isFinishReport = state.tableData[3].status === 1; + data.isAcceptChargess = state.tableData[4].status === 1; + data.isFeasibility = state.tableData[5].status === 1; + console.log('data', data) + const res = await editRisk(data); + if (res.code == 200) { + ElMessage.success('变更成功') + formRef.value.clearValidate(); + // emit('getNextStatus', data.project.id); + } else { + ElMessage.warning(res.message) + } + } + } + } +} + +const handleChange = (value) => { + // if(state.registerForm.agency.attribute == 0){ + // state.registerForm.agency.province = '新疆维吾尔自治区' + // state.registerForm.agency.city = value[0]?value[0]:'' + // state.registerForm.agency.district = value[1]?value[1]:'' + // }else{ + state.formData.project.province = value[0]?value[0]:'' + state.formData.project.city = value[1]?value[1]:'' + state.formData.project.district = value[2]?value[2]:'' + // } +} + + + +defineExpose({ + riskOpen +}); + + +</script> +<style scoped lang="scss"> +.riskBox{ + :deep(.el-form .el-form-item__label) { + font-size: 15px; + } +} + +</style> diff --git a/src/views/safetyReview/projectManage/index.vue b/src/views/safetyReview/projectManage/index.vue new file mode 100644 index 0000000..2127984 --- /dev/null +++ b/src/views/safetyReview/projectManage/index.vue @@ -0,0 +1,318 @@ +<template> + <div class="project-container"> + <div class="header"> + <el-button type="success" icon="Plus" @click="toProcess('add',{})">新增</el-button> + <el-button type="warning" >导出</el-button> + <el-button type="primary" icon="Filter">筛选</el-button> + </div> + <div class="middle"> + <div class="card-box"> + <div class="box-left" @click="choose('')"> + <class class="font-left"> + <div>项目</div> + <div>总数</div> + </class> + <class class="font-right">{{search.num.projectTotal}}</class> + </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}"> + <div class="top" style="background: linear-gradient(90deg, rgb(127, 118, 253), rgb(218, 180, 246));"> + <span class="top-right-font">风险分析及计划评价</span> + </div> + <div class="bottom"> + <span class="top-right-font-bottom">{{search.num.riskTotal}}</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}"> + <div class="top" style="background: linear-gradient(90deg, rgb(255, 140, 138), rgb(239, 186, 141));"> + <span class="top-right-font">现场勘验</span> + </div> + <div class="bottom"> + <span class="top-right-font-bottom">{{search.num.investigationTotal}}</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}"> + <div class="top" style="background: linear-gradient(90deg, rgb(229, 119, 180), rgb(249, 159, 192));"> + <span class="top-right-font">项目审核</span> + </div> + <div class="bottom"> + <span class="top-right-font-bottom">{{search.num.reviewTotal}}</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}"> + <div class="top" style="background: linear-gradient(90deg, rgb(54, 115, 255), rgb(124, 196, 242));"> + <span class="top-right-font">出具报告</span> + </div> + <div class="bottom"> + <span class="top-right-font-bottom">{{search.num.reportTotal}}</span> + </div> + </div> + <div class="inbox" @click="choose(5)" style="box-shadow: rgba(88, 211, 137, 0.2) 0 3px 5px 0;" :class="{btn5: chooseType === 5}"> + <div class="top" style="background: linear-gradient(90deg, rgb(0, 195, 151), rgb(114, 232, 200));"> + <span class="top-right-font">项目归档</span> + </div> + <div class="bottom"> + <span class="top-right-font-bottom">{{search.num.archiveTotal}}</span> + </div> + </div> + </div> + </div> + </div> + <!-- 表格数据 --> + <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="name" align="center" :show-overflow-tooltip="true" width="180" /> + <el-table-column label="委托单位" prop="client" align="center" :show-overflow-tooltip="true" width="180"/> + <el-table-column label="所属地市" prop="updateTime" align="center" width="250"> + <template #default="scope"> + {{scope.row.province}}/{{scope.row.city}} + </template> + </el-table-column> + <el-table-column label="评价类型" prop="estimateTypeName" align="center" width="150"/> + <el-table-column label="业务范围" prop="businessName" align="center" :show-overflow-tooltip="true" width="150"/> + <el-table-column label="项目负责人" prop="" align="center" width="120" :show-overflow-tooltip="true"/> + <el-table-column label="项目阶段" prop="" align="center" width="200"> + <template #default="scope"> + + </template> + </el-table-column> + <el-table-column label="项目实施天数" prop="" align="center" width="150"/> + <el-table-column label="项目变更" prop="" align="center" width="120"/> + <el-table-column label="预估金额(万元)" prop="" align="center" width="130"/> + <el-table-column label="归档金额(万元)" prop="" align="center" width="130"/> + <el-table-column label="缺失要件" prop="" align="center" width="150"/> + <el-table-column label="归档确认" prop="" align="center" width="150"/> + <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="toProcess('view',scope.row)">查看</el-button> + <el-button link type="primary" @click="toProcess('edit',scope.row)">编辑</el-button> + <el-button link type="danger">删除</el-button> + </template> + </el-table-column> + </el-table> + + <div class="pag-container"> + <el-pagination + v-model:current-page="search.queryParams.pageNum" + v-model:page-size="search.queryParams.pageSize" + :page-sizes="[10,15,20,25]" + layout="total, sizes, prev, pager, next, jumper" + :total="total" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> + </div> + +</template> + +<script setup> +import {onMounted, reactive, ref} from "vue"; +import {getProjectList, getProjectStatistics} from "@/api/projectManage/project"; +import {ElMessage} from "element-plus"; +const router = useRouter(); + +const loading = ref(false); +const search = reactive({ + queryParams: { + pageNum: 1, + pageSize: 20, + params:{ + projectPhase: '', + } + }, + num: { + projectTotal: 0, + riskTotal: 0, + investigationTotal: 0, + reviewTotal: 0, + reportTotal: 0, + archiveTotal: 0 + } +}); +const tableRef = ref(null); +const tableHeight = ref(0); +const dataList = ref([]); +const total = ref(0); + +onMounted(() => { + getList(); + getStatistics(); + tableHeight.value = window.innerHeight - tableRef.value.$el.offsetTop - 170; + // 监听浏览器高度变化 + window.onresize = () => { + tableHeight.value = window.innerHeight - tableRef.value.$el.offsetTop - 170; + }; + +}); +const chooseType = ref(''); +const choose = (val) => { + chooseType.value = val; + search.queryParams.params.projectPhase = val; + getList(); +} +const getList = async () => { + loading.value = true; + console.log(search.queryParams,'search.queryParams') + const res = await getProjectList(search.queryParams); + if(res.code == 200){ + dataList.value = res.data.list; + total.value = res.data.total + loading.value = false; + }else{ + ElMessage.warning(res.message) + } +} +const getStatistics = async () => { + const res = await getProjectStatistics(); + if(res.code == 200){ + console.log("res",res) + search.num = res.data + + }else{ + ElMessage.warning(res.message) + } +} +const handleSizeChange = (val) => { + search.queryParams.pageSize = val + getList() +} +const handleCurrentChange = (val) => { + search.queryParams.pageNum = val + getList() +} +const toProcess = (type,value) => { + value.type = type; + router.push({ path: '/process', query: {id: value.id, type: type}}); +} +</script> + +<style scoped lang="scss"> +.project-container{ + .header{ + margin: 15px 10px; + display: flex; + align-items: center; + justify-content: flex-end; + } + .middle{ + height: 99px; + margin-bottom: 20px; + } + .card-box{ + margin: 20px; + height: 99px; + background: #fff; + box-shadow: 0 3px 10px 0 rgba(62,62,62,.25); + border-radius: 10px; + display: flex; + .box-left{ + width: 20%; + min-width: 200px; + height: 99px; + background: #fff; + box-shadow: 0 0 5px 0 rgba(62,62,62,.2); + border-radius: 10px; + display: flex; + margin-left: -15px; + align-items: center; + cursor: pointer; + padding-left: 2%; + .font-left{ + width: 40px; + height: 40px; + font-size: 20px; + font-weight: 400; + color: #9198ad; + line-height: 18px; + font-family: fantasy; + } + .font-right{ + position: relative; + left: 7%; + top: -3px; + height: 40px; + font-size: 36px; + font-family: fantasy; + font-weight: 400; + color: #222733; + } + } + .box-left:hover{ + border: 1px solid #4378f1 + } + .box-right{ + width: 80%; + height: 99px; + border-radius: 10px; + margin-left: 15px; + display: flex; + align-items: center; + justify-content: space-between; + .inbox{ + width: 19%; + height: 76px; + background: #fff; + border-radius: 5px; + cursor: pointer; + .top{ + width: 100%; + height: 39px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + .top-right-font{ + font-size: 16px; + color: #fff; + line-height: 39px; + margin-left: 10px; + } + } + .bottom{ + height: 37px; + .top-right-font-bottom{ + font-size: 20px; + color: #333; + line-height: 37px; + margin-left: 20px; + font-family: fantasy; + } + } + } + .inbox:nth-child(1):hover{ + border: 1px solid #dab4f6; + } + .inbox:nth-child(2):hover{ + border: 1px solid #efba8d; + } + .inbox:nth-child(3):hover{ + border: 1px solid #f99fc0; + } + .inbox:nth-child(4):hover{ + border: 1px solid #7cc4f2; + } + .inbox:nth-child(5):hover{ + border: 1px solid #72e8c8; + } + } + } + .btn1{ + border: 1px solid #dab4f6; + } + .btn2{ + border: 1px solid #efba8d; + } + .btn3{ + border: 1px solid #f99fc0; + } + .btn4{ + border: 1px solid #7cc4f2; + } + .btn5{ + border: 1px solid #72e8c8; + } + .pag-container{ + float: right; + margin-top: 20px; + } +} +</style> diff --git a/src/views/safetyReview/projectManage/process.vue b/src/views/safetyReview/projectManage/process.vue new file mode 100644 index 0000000..6c50762 --- /dev/null +++ b/src/views/safetyReview/projectManage/process.vue @@ -0,0 +1,846 @@ +<template> + <div class="process-container"> + <div class="flow"> + <div class="content" :class="{show: isShowMenu}" > + <div class="content-middle" @click="clickMenu(true)"> + <div style="margin-right:20px;height: 85px;display: flex;align-items: center;flex-shrink: 1;"> + <img :src="projectPng"> + </div> + <div v-for="item in menuList" :key="item.id" style="flex: 1" :class="{choose: item.status === 1 || item.status === 2}"> + <div class="header-item"> + <div class="item-content"> + <img v-if="item.status === 0" :src="status0Png"> + <img v-else-if="item.status === 1" :src="status1Png"> + <img v-else :src="status2Png"> + <span class="text-eclipse" style="margin-left: 5px">{{item.name}}</span> + </div> + </div> + <div v-for="child in item.subMenus" :key="child.id" @click="chooseSubMenu(child,true)"> + <div class="down-item" :class="{itemActive: child.status === 1 || child.status === 3, itemPrev: child.status === 2}"> + <div class="item-icon-status0" v-if="child.status === 0">{{child.id}}</div> + <div class="item-icon-status0 item-icon-status1" v-else-if="child.status === 1 || (selectedObj.status === 3 && child.id === selectedObj.id)">{{child.id}}</div> + <div class="item-icon-status0 item-icon-status2" v-else-if="child.status === 2 "><img :src="itemStatus2Png"></div> + <div class="item-icon-status0 item-icon-status4 " v-else-if="nextObj.status === 4 && child.id === nextObj.id">{{child.id}}</div> + <div class="text-eclipse" style="margin-right: 15%">{{child.name}}</div> + <div v-if="child.status === 1 || (selectedObj.status === 1 && child.id ===selectedObj.id)" class="item-icon-status0 item-icon-status2"><img :src="status1Png"></div> + <div v-if="nextObj.status === 4 && child.id === nextObj.id" class="item-icon-status0 item-icon-status4" ><img :src="status1Png"></div> + </div> + <div></div> + </div> + </div> + </div> + <div class="content-bottom" @click="clickMenu(false)"></div> + </div> + </div> + + <div class="middle" :style="'height:' + middleHeight + 'px'"> + <el-card> + <div class="card-header">{{selectedObj.id}}- {{selectedObj.name}}</div> + <div class="card-content"> + <div :style="'height:' + middleContentHeight + 'px'" style="overflow-y: scroll;"> + <rickAnalysis ref="riskRef" v-if="selectedObj.id === 1" @getNextStatus="getNextStatus"></rickAnalysis> + + + </div> + <div style="display: flex;align-items: center;justify-content: center;margin-bottom: -20px"> + <el-button type="primary" v-if="selectedObj.id !== 1" style="width: 80px" @click="back">上一步</el-button> + <el-button type="warning" style="width: 80px" v-if="projectStatus === 'edit' && selectedObj.status !== 1" @click="clickEdit">变更</el-button> +<!-- <el-button type="warning" style="width: 80px" v-if="selectedObj.status === 1" @click="save">暂存</el-button>--> + <el-button type="primary" style="width: 80px" @click="next">下一步</el-button> + </div> + </div> + </el-card> + </div> + <div class="bottom"></div> + </div> +</template> + +<script setup> + +import {onMounted, ref} from "vue"; +import {ElMessage} from "element-plus"; +import rickAnalysis from "./components/riskAnalysis.vue" +import projectPng from "@/assets/images/project.png" +import status0Png from "@/assets/images/status0.png" +import status1Png from "@/assets/images/status1.png" +import status2Png from "@/assets/images/status2.png" +import itemStatus2Png from "@/assets/images/itemStatus2.png" +import { useRoute } from 'vue-router' +import {getProjectStatus} from "@/api/projectManage/project"; + +const route = useRoute() +const menuList = ref([ + { + id: 'a', + name: '风险分析及计划评价', + status: 1 , + subMenus: [ + { + id: 1, + name: '项目风险分析', + status: 1 // 0:未完成,1:选中待完成,2:已完成 ,3:选中已完成,4:未选中待完成 + }, + { + id: 2, + name: '合同管理', + status: 0 + }, + { + id: 3, + name: '评价任务通知', + status: 0 + }, + { + id: 4, + name: '评价项目计划', + status: 0 + }, + ] + }, + { + id: 'b', + name: '现场勘验', + status: 0, + subMenus: [ + { + id: 5, + name: '从业告知', + status: 0 + }, + { + id: 6, + name: '现场勘验', + status: 0 + }, + ] + }, + { + id: 'c', + name: '项目审核', + status: 0, + subMenus: [ + { + id: 7, + name: '内部审核', + status: 0 + }, + { + id: 8, + name: '技术负责人审核', + status: 0 + }, + { + id: 9, + name: '审核记录', + status: 0 + }, + ] + }, + { + id: 'd', + name: '出具报告', + status: 0, + subMenus: [ + { + id: 10, + name: '评价结论', + status: 0 + }, + { + id: 11, + name: '过程控制负责人审核', + status: 0 + }, + + ] + }, + { + id: 'e', + name: '项目归档', + status: 0, + subMenus: [ + { + id: 12, + name: '项目归档', + status: 0 + }, + { + id: 13, + name: '签字确认', + status: 0 + }, + { + id: 14, + name: '确认完结', + status: 0 + }, + ] + }, +]) +const riskRef = ref(); +const isShowMenu = ref(false); +const selectedObj = ref({}) +const middleHeight = ref(0); +const middleContentHeight = ref(0); +const projectId = ref(); +const projectStatus = ref(); +onMounted(() => { + middleHeight.value = window.innerHeight - 250; + middleContentHeight.value = window.innerHeight - 385; + // 监听浏览器高度变化 + window.onresize = () => { + middleHeight.value = window.innerHeight - 250; + middleContentHeight.value = window.innerHeight - 385; + }; + if(route.query.type !== 'add'){ + projectStatus.value = route.query.type; + projectId.value = route.query.id; + getStatus(projectId.value); + }else { + projectStatus.value = route.query.type; + selectedObj.value = { + id: 1, + name: '项目风险分析', + status: 1 + } + } +}); +const nextObj = ref({}) +const setMenuList = (id) => { + menuList.value[id].subMenus.forEach(item => { + item.status = 2; + }) +} +const getStatus = async (projectId) => { + const res = await getProjectStatus(projectId); + if(res.code == 200){ + if(res.data <= 4 ){ + menuList.value[0].subMenus.forEach(item => { + if(item.id <= res.data){ + item.status = 2 + } + if(item.id === res.data + 1){ + item.status = 4; + nextObj.value = item; + } + if(item.id === res.data){ + item.status = 3; + selectedObj.value =item; + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + + } + }) + menuList.value[0].status = 1; + console.log("menu11",menuList.value) + } + if(res.data >=4 && res.data <=6){ + setMenuList(0) + if(res.data === 4){ + menuList.value[0].subMenus[3].status = 3; + } + menuList.value[1].subMenus.forEach(item => { + if(item.id <= res.data) { + item.status = 2 + } + if(item.id === res.data + 1){ + item.status = 4; + nextObj.value = item; + } + if(item.id === res.data){ + item.status = 3; + selectedObj.value =item; + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } + }) + menuList.value[0].status = 2; + menuList.value[1].status = 1; + console.log("menu22",menuList.value) + } + if(res.data >= 6 && res.data <= 9){ + setMenuList(0); + setMenuList(1); + if(res.data === 6){ + menuList.value[1].subMenus[1].status = 3; + } + menuList.value[2].subMenus.forEach(item => { + if(item.id <= res.data) { + item.status = 2 + } + if(item.id === res.data + 1){ + item.status = 4; + nextObj.value = item; + } + if(item.id === res.data){ + item.status = 3; + selectedObj.value =item; + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } + }) + menuList.value[0].status = 2; + menuList.value[1].status = 2; + menuList.value[2].status = 1; + } + if(res.data >= 9 && res.data <=11){ + setMenuList(0); + setMenuList(1); + setMenuList(2); + if(res.data === 9){ + menuList.value[2].subMenus[2].status = 3; + } + menuList.value[3].subMenus.forEach(item => { + if(item.id <= res.data) { + item.status = 2 + } + if(item.id === res.data + 1){ + item.status = 4; + nextObj.value = item; + } + if(item.id === res.data){ + item.status = 3; + selectedObj.value =item; + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } + }) + menuList.value[0].status = 2; + menuList.value[1].status = 2; + menuList.value[2].status = 2; + menuList.value[3].status = 1; + } + if(res.data >= 11){ + setMenuList(0); + setMenuList(1); + setMenuList(2); + setMenuList(3); + if(res.data === 11){ + menuList.value[3].subMenus[1].status = 3; + } + menuList.value[4].subMenus.forEach(item => { + if(item.id <= res.data) { + item.status = 2 + } + if(item.id === res.data + 1){ + item.status = 4; + nextObj.value = item; + } + if(item.id === res.data){ + item.status = 3; + selectedObj.value =item; + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } + menuList.value[0].status = 2; + menuList.value[1].status = 2; + menuList.value[2].status = 2; + menuList.value[3].status = 2; + menuList.value[4].status = 1; + if(res.data === 14 && res.data === item.id){ + item.status = 3; + menuList.value[4].status = 2; + selectedObj.value =item; + } + }) + + } + }else{ + ElMessage.warning(res.message) + } +} + +const clickMenu = (val) => { + isShowMenu.value = val; +} +const chooseSubMenu = (val,flag) => { + if(val.status === 0) { + setTimeout(() => { + isShowMenu.value = false; + ElMessage({ + type: 'warning', + message: '请按顺序操作,未完成步骤无法查看!' + }); + }, 10) + }else if(val.status === 1){ + setTimeout(() => { + isShowMenu.value = false; + selectedObj.value = val; + }, 10) + }else if(val.status === 2){ + setTimeout(() => { + isShowMenu.value = false; + if((val !== selectedObj.value && nextObj.value === selectedObj.value) || nextObj.value === selectedObj.value){ + nextObj.value = selectedObj.value; + } + if(nextObj.value !== val) { + selectedObj.value.status = 2 + } + + selectedObj.value = val; + selectedObj.value.status = 3; + nextObj.value.status = 4; + console.log('selectedObj.valu',selectedObj.value) + //跳转 + }, 10) + if(flag){ + setTimeout(() => { + goRouter(selectedObj.value.id) + }, 10) + } + + }else if (val.status === 3) { + setTimeout(() => { + isShowMenu.value = false; + selectedObj.value = val; + //跳转 + }, 10) + }else if (val.status === 4) { + setTimeout(() => { + isShowMenu.value = false; + nextObj.value = selectedObj.value; + selectedObj.value = val; + selectedObj.value.status = 1; + nextObj.value.status = 2; + nextObj.value = val; + //跳转 + }, 10) + } +} +const next = () => { + if(selectedObj.value.status === 3){ + if(selectedObj.value.id + 1 <= 4){ + nextMenu(0) + }else if (selectedObj.value.id+ 1 >4 && selectedObj.value.id+ 1 <= 6){ + nextMenu(1) + }else if (selectedObj.value.id+ 1 >6 && selectedObj.value.id+ 1 <= 9){ + nextMenu(2) + }else if (selectedObj.value.id+ 1 >9 && selectedObj.value.id+ 1 <= 11){ + nextMenu(3) + }else if (selectedObj.value.id+ 1 >11){ + nextMenu(4) + } + } else { + //下一步——保存 + goRouter(selectedObj.value.id,'add') + // switch (selectedObj.value.id){ + // case 1: + // riskRef.value.riskOpen('add',''); + // break; + // case 2: + // + // break; + // case 3: + // + // break; + // case 4: + // + // break; + // case 5: + // + // break; + // case 6: + // + // break; + // case 7: + // + // break; + // case 8: + // + // break; + // case 9: + // + // break; + // case 10: + // + // break; + // case 11: + // + // break; + // case 12: + // + // break; + // case 13: + // + // break; + // case 14: + // + // break; + // } + } + setTimeout( () => { + goRouter(selectedObj.value.id) + }, 100) +} + +const nextMenu = (id) => { + menuList.value[id].subMenus.forEach( item => { + if(item.id === selectedObj.value.id + 1){ + chooseSubMenu(item,false); + } + }) +} + +const backMenu = (id) => { + menuList.value[id].subMenus.forEach(async item => { + if(item.id === selectedObj.value.id - 1){ + await chooseSubMenu(item,false); + console.log("ssssss",selectedObj.value) + } + }) +} + +//上一步——回退(查看详情) +const back = () => { + if(selectedObj.value.id-1 <= 4){ + backMenu(0) + }else if (selectedObj.value.id-1 >4 && selectedObj.value.id-1 <= 6){ + backMenu(1) + }else if (selectedObj.value.id-1 >6 && selectedObj.value.id-1 <= 9){ + backMenu(2) + }else if (selectedObj.value.id-1 >9 && selectedObj.value.id-1 <= 11){ + backMenu(3) + }else if (selectedObj.value.id-1 >11){ + backMenu(4) + } + setTimeout( () => { + goRouter(selectedObj.value.id) + // switch (selectedObj.value.id){ + // case 1: + // if(projectStatus.value === 'view'){ + // riskRef.value.riskOpen('detail',projectId.value); + // }else { + // riskRef.value.riskOpen('edit',projectId.value); + // } + // break; + // case 2: + // + // break; + // case 3: + // + // break; + // case 4: + // + // break; + // case 5: + // + // break; + // case 6: + // + // break; + // case 7: + // + // break; + // case 8: + // + // break; + // case 9: + // + // break; + // case 10: + // + // break; + // case 11: + // + // break; + // case 12: + // + // break; + // case 13: + // + // break; + // case 14: + // + // break; + // } + }, 100) + +} +const clickEdit = () => { + goRouter(selectedObj.value.id,'clickEdit') + // switch (selectedObj.value.id){ + // case 1: + // riskRef.value.riskOpen('clickEdit',projectId.value); + // break; + // case 2: + // + // break; + // case 3: + // + // break; + // case 4: + // + // break; + // case 5: + // + // break; + // case 6: + // + // break; + // case 7: + // + // break; + // case 8: + // + // break; + // case 9: + // + // break; + // case 10: + // + // break; + // case 11: + // + // break; + // case 12: + // + // break; + // case 13: + // + // break; + // case 14: + // + // break; + // } +} +const getNextStatus = (val) => { + getStatus(val); +} + +const goRouter = (val,type) => { + switch (val){ + case 1: + if(type === 'add'){ + riskRef.value.riskOpen('add',''); + }else if (type === 'clickEdit'){ + riskRef.value.riskOpen('clickEdit',projectId.value); + }else { + if(projectStatus.value === 'view'){ + riskRef.value.riskOpen('detail',projectId.value); + }else { + riskRef.value.riskOpen('edit',projectId.value); + } + } + console.log("1") + break; + case 2: + console.log("2") + break; + case 3: + console.log("3") + break; + case 4: + console.log("4") + break; + case 5: + console.log("5") + break; + case 6: + console.log("6") + break; + case 7: + console.log("7") + break; + case 8: + console.log("8") + break; + case 9: + console.log("9") + break; + case 10: + console.log("10") + break; + case 11: + console.log("11") + break; + case 12: + console.log("12") + break; + case 13: + console.log("13") + break; + case 14: + console.log("14") + break; + } +} + +</script> + +<style scoped lang="scss"> +.process-container{ + height: 100%; + .flow{ + position: relative; + height: 85px; + background: transparent; + .content{ + z-index: 10; + top: 0; + left: 0; + right: 0; + min-height: 85px; + max-height: 85px; + cursor: pointer; + background-color: hsla(0,0%,100%,.97); + background-image: url(../../../assets/images/bg.png); + background-repeat: no-repeat; + background-size: 100% 84px; + overflow: hidden; + transition: max-height .3s linear; + position: absolute; + .content-bottom{ + position: relative; + height: 18px; + text-align: center; + background-color: transparent; + pointer-events: none; + } + .content-bottom:after { + content: " "; + cursor: pointer; + position: absolute; + top: 0; + bottom: 0; + left: 50%; + pointer-events: auto; + transform: translateX(-50%); + width: 118px; + background-image: url(../../../assets/images/back.png); + } + .choose .header-item{ + color: #fff; + background-image: url(../../../assets/images/choosed.png); + } + } + .show{ + cursor: default; + max-height: 600px; + } + .content-middle{ + padding-left: 37px; + padding-right: 27px; + display: flex; + flex-direction: row; + box-shadow: 0 0 18px 3px rgba(145,146,171,.2); + } + .header-item{ + position: relative; + height: 83px; + color: #5d6c8e; + flex: 1; + margin-left: -2%; + display: flex; + align-items: center; + justify-content: center; + background-repeat: no-repeat; + background-size: 100% 100%; + background-image: url(../../../assets/images/header.png); + .item-content{ + width: 80%; + padding: 0 20px; + display: flex; + align-items: center; + margin-left: 8%; + margin-bottom: 2px; + font-size: 20px; + font-weight: 400; + } + } + .down-item{ + height: 47px; + margin: 30px auto 10px auto; + font-size: 19px; + color: #626c8b; + border: 1px solid #fff; + background: rgba(198,230,255,.2); + border-radius: 8px; + display: flex; + align-items: center; + width: 70%; + padding-left: 20px; + padding-right: 15px; + .item-icon-status0{ + width: 21px; + height: 21px; + color: #fff; + font-size: 16px; + line-height: 20px; + text-align: center; + border-radius: 50%; + background: #b9c2d5; + margin-right: 10px; + } + .item-icon-status1{ + color: #3b75ff; + background: #fff; + } + .item-icon-status2{ + background: transparent; + } + .item-icon-status4{ + background: #0cca8f; + } + } + .itemActive{ + cursor: pointer; + color: #fff; + border: 1px solid #c6e6ff; + background: linear-gradient(90deg,#57b6ff,#3b75ff); + } + .itemPrev{ + cursor: pointer; + color: #3b75ff; + border: 1px solid #c6e6ff; + background: rgba(198,230,255,.4); + } + .itemPrev:hover{ + transform: scale(1.1); + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -o-transform: scale(1.1); + -ms-transform: scale(1.1); + } + } + .middle{ + padding: 10px; + .card-header{ + height: 50px; + line-height: 26px; + font-weight: 700; + padding: 12px 56px; + font-size: 18px; + color: #fff; + background-image: url(../../../assets/images/cardHeader.png); + background-size: 100% 100%; + background-repeat: no-repeat; + margin: -20px; + } + .card-content{ + padding: 20px 60px; + margin-top: 20px; + } + ::-webkit-scrollbar { + display: none; + } + } + .bottom{ + height: 60px; + background: rgb(255, 255, 255); + padding: 9px 10px; + } + .text-eclipse{ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + +} + +</style> -- Gitblit v1.9.2