zhouwenxuan
2024-01-16 52f0aa42410a2102a8fa1badfc8e467cae7d6a7c
项目管理
已修改2个文件
已添加5个文件
1710 ■■■■■ 文件已修改
src/api/projectManage/project.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/projectManage/riskAnalysis.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Sidebar/menu.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/riskAnalysis.vue 456 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/index.vue 318 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/process.vue 846 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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'
    })
}
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
    })
}
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'},
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',
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>
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>
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>