zhouwx
2024-11-20 8e5874a30ae9b194968393b2399bbba193eaa313
项目管理
已修改8个文件
已添加2个文件
921 ■■■■■ 文件已修改
src/api/projectManage.js 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/basicInfo.vue 113 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/chooseExpert.vue 185 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/expertsList.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/exportWord.js 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/projectApproval.vue 175 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/components/projectArchive.vue 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/index.vue 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safetyReview/projectManage/process.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
    })
}
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)
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({
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
}
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({
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
            }
        });
}
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
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()
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();
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
    }
  })
}