多体系建设信息化条统-前端
祖安之光
2025-11-26 50ded4c9a21fb294892bfee8cad62f1a56f96ca8
修改新增
25 files modified
3 files added
1287 ■■■■ changed files
public/summaryInfo.docx patch | view | raw | blame | history
src/api/staffManage/staff.js 8 ●●●●● patch | view | raw | blame | history
src/views/build/conpanyFunctionConsult/companyInfo/overview/components/overviewDialog.vue 522 ●●●●● patch | view | raw | blame | history
src/views/build/conpanyFunctionConsult/companyInfo/qualifications/components/qualificationsDialog.vue 31 ●●●●● patch | view | raw | blame | history
src/views/build/conpanyFunctionConsult/orgStructure/departManage/components/departDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/build/conpanyFunctionConsult/orgStructure/departManage/components/dutyDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/assetMng/toolsMonitorMeasure/annualVerificationPlan/components/editDialog.vue 14 ●●●● patch | view | raw | blame | history
src/views/work/assetMng/toolsMonitorMeasure/annualVerificationPlan/index.vue 34 ●●●●● patch | view | raw | blame | history
src/views/work/assetMng/toolsMonitorMeasure/equipCalibrateConfirm/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/assetMng/workEnvironmentControl/6sInspectChecklist/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/documentManage/docBorrowCopy/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/documentManage/docChangeInvalidate/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/documentManage/docDestruction/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/documentManage/docDistributeRetrieve/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/marketingManagement/contractManagement/contractLedger/components/editDialog.vue 9 ●●●●● patch | view | raw | blame | history
src/views/work/marketingManagement/contractManagement/contractLedger/index.vue 5 ●●●●● patch | view | raw | blame | history
src/views/work/qualityInfo/infrastructureMng/ledger/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/infrastructureMng/maintainPlan/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/infrastructureMng/maintainRecord/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/infrastructureMng/repairRecord/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/infrastructureMng/reviewRecordStatistics/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/inventoryRecord/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/qualityInfo/supplierQuality/satisfiedEvaluste/report/components/editDialog.vue 38 ●●●● patch | view | raw | blame | history
src/views/work/qualityInfo/supplierQuality/satisfiedEvaluste/report/index.vue 16 ●●●● patch | view | raw | blame | history
src/views/work/selfProblems/mngAudit/mngAuditPlan/components/editDialog.vue 2 ●●● patch | view | raw | blame | history
src/views/work/situationSum/components/exportDoc.js 360 ●●●●● patch | view | raw | blame | history
src/views/work/situationSum/index.vue 218 ●●●●● patch | view | raw | blame | history
public/summaryInfo.docx
Binary files differ
src/api/staffManage/staff.js
@@ -167,3 +167,11 @@
        params: params
    })
}
export function getSituationSumDetail(params) {
    return request({
        url: '/system/statistics/getCompanyStatistics',
        method: 'get',
        params: params
    })
}
src/views/build/conpanyFunctionConsult/companyInfo/overview/components/overviewDialog.vue
@@ -1,62 +1,96 @@
<template>
    <div class="notice">
        <el-dialog
            v-model="dialogVisible"
            :title="title"
            width="800px"
            :before-close="handleClose"
        >
            <el-form :model="state.noticeForm" size="default" ref="noticeRef" :rules="title === '新增' || title === '编辑' ? state.formRules : {}" label-width="110px" >
              <el-form-item v-if="state.isAdmin" label="单位:" prop="companyId">
                <el-select v-model="state.noticeForm.companyId" placeholder="请选择" clearable filterable style="width: 100%" :disabled="title == '查看' || title == '编辑' || !state.isAdmin">
                  <el-option
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        width="800px"
        :before-close="handleClose"
    >
      <el-form :model="state.noticeForm" size="default" ref="noticeRef"
               :rules="title === '新增' || title === '编辑' ? state.formRules : {}" label-width="110px">
        <el-form-item v-if="state.isAdmin" label="单位:" prop="companyId">
          <el-select v-model="state.noticeForm.companyId" placeholder="请选择" clearable filterable style="width: 100%"
                     :disabled="title == '查看' || title == '编辑' || !state.isAdmin">
            <el-option
                      v-for="item in state.companyList"
                      :key="item.id"
                      :label="item.name"
                      :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
                <el-form-item label="单位概况:" v-if="showEditor"  required>
                    <t-editor style="width: 800px" ref="myEditor" :toolbar="toolbar" :value="state.noticeForm.companySummary" ></t-editor>
                </el-form-item>
                <el-form-item label="单位概况:" v-else>
                    <div class="ql-container ql-snow" style="height: 500px;width: 100%;margin-top: 10px;" >
                        <div class="ql-editor">
                            <div class="reviewTable" v-html="state.noticeForm.companySummary"  @click="showFile($event)"></div>
                        </div>
                    </div>
                </el-form-item>
                <el-form-item label="营业执照:" prop="filePath" v-if="showEditor">
                  <el-upload accept=".jpg,.jpeg,.png,.pdf" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
                    <el-button type="primary">点击上传</el-button>
                    <template #tip>
                      <div class="el-upload__tip">尺寸小于5M,最多可上传1张</div>
                    </template>
                  </el-upload>
                </el-form-item>
              <el-form-item label="营业执照:" prop="filePath" v-else>
                <div v-if="state.fileType === 'pdf'" class="pdf-preview">
                  <iframe
                      :src="state.fileUrl"
                      width="650px"
                      height="800px"
                  ></iframe>
                </div>
                <div v-else class="image-preview">
                  <img :src="state.fileUrl" style="width:650px" alt="预览图片" class="preview-image" />
                </div>
              </el-form-item>
            </el-form>
            <template #footer v-if="!isReview">
                v-for="item in state.companyList"
                :key="item.id"
                :label="item.name"
                :value="item.id">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="单位类型:" prop="companyType">
          <el-select v-model="state.noticeForm.companyType" placeholder="请选择" clearable filterable
                     style="width: 100%" :disabled="title == '查看'">
            <el-option :key="1" label="民营" :value="1"></el-option>
            <el-option :key="2" label="私营" :value="2"></el-option>
            <el-option :key="3" label="企事业单位" :value="3"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="行业:" prop="industry">
          <el-input v-model.trim="state.noticeForm.industry" :disabled="title === '查看'"/>
        </el-form-item>
        <el-form-item label="证书编号:" prop="legalPersonCode">
          <el-input v-model.trim="state.noticeForm.legalPersonCode" :disabled="title === '查看'"/>
        </el-form-item>
        <el-form-item label="法人代表:" prop="legalPerson">
          <el-input v-model.trim="state.noticeForm.legalPerson" :disabled="title === '查看'"/>
        </el-form-item>
        <el-form-item label="注册资本:" prop="registeredCapital">
          <el-input v-model.trim="state.noticeForm.registeredCapital" :disabled="title === '查看'"/>
        </el-form-item>
        <el-form-item label="总办公地址:" prop="officeAddress">
          <el-input v-model.trim="state.noticeForm.officeAddress" :rows="2" type="textarea"
                    :disabled="title === '查看'"/>
        </el-form-item>
        <el-form-item label="其他分场所地址:" prop="otherAddress">
          <el-input v-model.trim="state.noticeForm.otherAddress" :rows="3" type="textarea"
                    :disabled="title === '查看'"/>
        </el-form-item>
        <el-form-item label="单位概况:" v-if="showEditor" required>
          <t-editor style="width: 800px" ref="myEditor" :toolbar="toolbar"
                    :value="state.noticeForm.companySummary"></t-editor>
        </el-form-item>
        <el-form-item label="单位概况:" v-else>
          <div class="ql-container ql-snow" style="height: 500px;width: 100%;margin-top: 10px;">
            <div class="ql-editor">
              <div class="reviewTable" v-html="state.noticeForm.companySummary" @click="showFile($event)"></div>
            </div>
          </div>
        </el-form-item>
        <el-form-item label="营业执照:" prop="filePath" v-if="showEditor">
          <el-upload accept=".jpg,.jpeg,.png,.pdf" :action="state.uploadUrl" :headers="state.header" method="post"
                     :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip"
                     :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize"
                     :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)">
            <el-button type="primary">点击上传</el-button>
            <template #tip>
              <div class="el-upload__tip">尺寸小于5M,最多可上传1张</div>
            </template>
          </el-upload>
        </el-form-item>
        <el-form-item label="营业执照:" prop="filePath" v-else>
          <div v-if="state.fileType === 'pdf'" class="pdf-preview">
            <iframe
                :src="state.fileUrl"
                width="650px"
                height="800px"
            ></iframe>
          </div>
          <div v-else class="image-preview">
            <img :src="state.fileUrl" style="width:650px" alt="预览图片" class="preview-image"/>
          </div>
        </el-form-item>
      </el-form>
      <template #footer v-if="!isReview">
                    <span class="dialog-footer">
                        <el-button @click="handleClose" size="default">取 消</el-button>
                        <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
                        <el-button type="primary" @click="onSubmit" size="default" v-preReClick>确认</el-button>
                    </span>
            </template>
        </el-dialog>
    </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {nextTick, onMounted, reactive, ref, toRefs, watch} from 'vue'
@@ -85,19 +119,32 @@
const isReview = ref(false);
const showEditor = ref(true);
const state = reactive({
    noticeForm: {
        id: '',
        companySummary: '',
        companyId:null,
        companyName: '',
        filePath: '',
        fileName: ''
    },
    formRules:{
      companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
      companySummary: [{ required: true, message: '企业概况', trigger: 'blur' }],
      filePath: [{ required: true, message: '请上传营业执照', trigger: 'blur' }],
    },
  noticeForm: {
    id: '',
    companySummary: '',
    companyId: null,
    companyName: '',
    filePath: '',
    fileName: '',
    companyType: null,
    industry: '',
    legalPersonCode: '',
    legalPerson: '',
    registeredCapital: '',
    officeAddress: '',
    otherAddress: ''
  },
  formRules: {
    companyId: [{required: true, message: '请选择单位', trigger: 'blur'}],
    companySummary: [{required: true, message: '单位概况', trigger: 'blur'}],
    filePath: [{required: true, message: '请上传营业执照', trigger: 'blur'}],
    companyType: [{required: true, message: '单位类型', trigger: 'blur'}],
    industry: [{required: true, message: '行业', trigger: 'blur'}],
    legalPersonCode: [{required: true, message: '证书编号', trigger: 'blur'}],
    legalPerson: [{required: true, message: '法人代表', trigger: 'blur'}],
    registeredCapital: [{required: true, message: '注册资本', trigger: 'blur'}],
    officeAddress: [{required: true, message: '总办公地址', trigger: 'blur'}]
  },
  isAdmin: false,
  companyList: [],
  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
@@ -113,181 +160,188 @@
onMounted(() => {
});
const openDialog = async (type, value,companyList) => {
const openDialog = async (type, value, companyList) => {
    const userInfo = JSON.parse(Cookies.get('userInfo'))
    state.isAdmin = userInfo.userType === 0;
  if(state.isAdmin){
  const userInfo = JSON.parse(Cookies.get('userInfo'))
  state.isAdmin = userInfo.userType === 0;
  if (state.isAdmin) {
    state.companyList = companyList
  }
    isReview.value = false;
    showEditor.value = false
    title.value = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看' ;
    if(type === 'edit' || type === 'review') {
      Object.keys(state.noticeForm).forEach(key => {
        if (key in value) {
          state.noticeForm[key] = value[key]
        }
      })
      state.noticeForm.companySummary = value.companySummary
      state.noticeForm.id = value.id
      if(state.isAdmin){
        state.noticeForm.companyId = value.companyId
        state.noticeForm.companyName = value.companyName
  isReview.value = false;
  showEditor.value = false
  title.value = type === 'add' ? '新增' : type === 'edit' ? '编辑' : '查看';
  if (type === 'edit' || type === 'review') {
    Object.keys(state.noticeForm).forEach(key => {
      if (key in value) {
        state.noticeForm[key] = value[key]
      }
    })
    state.noticeForm.companySummary = value.companySummary
    state.noticeForm.id = value.id
    if (state.isAdmin) {
      state.noticeForm.companyId = value.companyId
      state.noticeForm.companyName = value.companyName
    }
      if(value.filePath) {
        const obj = {
          url: value.filePath,
          name: value.fileName
        }
        state.fileType = value.fileName.split('.')[1]
        state.fileUrl = import.meta.env.VITE_APP_BASE_API + '/' + value.filePath
        state.fileList = [obj]
    if (value.filePath) {
      const obj = {
        url: value.filePath,
        name: value.fileName
      }
      state.fileType = value.fileName.split('.')[1]
      state.fileUrl = import.meta.env.VITE_APP_BASE_API + '/' + value.filePath
      state.fileList = [obj]
    }
    if(type === 'review') {
        showEditor.value = false
        isReview.value = true;
    }
    if(type === 'edit' || type === 'add') {
        showEditor.value = true;
        isReview.value = false;
    }
    if(type === 'add'){
        reset()
    }
  }
  if (type === 'review') {
    showEditor.value = false
    isReview.value = true;
  }
  if (type === 'edit' || type === 'add') {
    showEditor.value = true;
    isReview.value = false;
  }
  if (type === 'add') {
    reset()
  }
  dialogVisible.value = true;
}
const getEditorData = (val) =>{
    state.noticeForm.companySummary = val;
const getEditorData = (val) => {
  state.noticeForm.companySummary = val;
}
const showFile = (e) => {
    if(e.target.nodeName === 'A'){
        console.log("e",e)
        e.preventDefault();
        const file = {
            fileUrl: e.target.href,
            fileName: e.target.innerHTML
        }
        axios.get( file.fileUrl,{
                headers:
                    {
                        'Content-Type': 'application/json',
                        'Authorization':getToken(),
                    },
                responseType: 'blob'
            }
        ).then(res=>{
            if (res) {
                const link = document.createElement('a')
                let blob = new Blob([res.data],{type: res.data.type})
                link.style.display = "none";
                link.href = URL.createObjectURL(blob); // 创建URL
                link.setAttribute("download", file.fileName);
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            } else {
                this.$message.error('获取文件失败')
            }
            // handleClose();
        })
  if (e.target.nodeName === 'A') {
    console.log("e", e)
    e.preventDefault();
    const file = {
      fileUrl: e.target.href,
      fileName: e.target.innerHTML
    }
    axios.get(file.fileUrl, {
          headers:
              {
                'Content-Type': 'application/json',
                'Authorization': getToken(),
              },
          responseType: 'blob'
        }
    ).then(res => {
      if (res) {
        const link = document.createElement('a')
        let blob = new Blob([res.data], {type: res.data.type})
        link.style.display = "none";
        link.href = URL.createObjectURL(blob); // 创建URL
        link.setAttribute("download", file.fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      } else {
        this.$message.error('获取文件失败')
      }
      // handleClose();
    })
  }
}
const onSubmit = async () => {
    state.noticeForm.companySummary = tinyMCE.activeEditor.getContent();
    if(!state.isAdmin){
      const userInfo = JSON.parse(Cookies.get('userInfo'))
      state.noticeForm.companyId = userInfo.companyId
      state.noticeForm.companyName = userInfo.companyName
  state.noticeForm.companySummary = tinyMCE.activeEditor.getContent();
  if (!state.isAdmin) {
    const userInfo = JSON.parse(Cookies.get('userInfo'))
    state.noticeForm.companyId = userInfo.companyId
    state.noticeForm.companyName = userInfo.companyName
  }
  // // myEditor.value.submit();
  const valid = await noticeRef.value.validate();
  if (valid) {
    if (state.noticeForm.companySummary == "") {
      ElMessage({
        type: 'warning',
        message: '请输入单位概况'
      });
      return;
    }
    // // myEditor.value.submit();
    const valid = await noticeRef.value.validate();
    if(valid){
        if(state.noticeForm.companySummary == "") {
            ElMessage({
                type: 'warning',
                message: '请输入企业概况'
            });
            return;
        }
        if(title.value === '新增'){
          const {id,...data} = JSON.parse(JSON.stringify(state.noticeForm))
            const res = await addCom(data)
            if(res.code === 200){
                ElMessage({
                    type: 'success',
                    message: '新增成功'
                });
            }else{
                ElMessage.warning(res.message)
            }
            emit("getList")
            reset();
            showEditor.value=false
            myEditor.value.clear();
            noticeRef.value.clearValidate();
            dialogVisible.value = false;
        }else if(title.value === '编辑') {
          const {...data} = JSON.parse(JSON.stringify(state.noticeForm))
            const res = await editCom(data)
            if(res.code === 200){
                ElMessage({
                    type: 'success',
                    message: '编辑成功'
                });
            }else{
                ElMessage.warning(res.message)
            }
            emit("getList")
            reset();
            showEditor.value=false
            myEditor.value.clear();
            noticeRef.value.clearValidate();
            dialogVisible.value = false;
        }
    if (title.value === '新增') {
      const {id, ...data} = JSON.parse(JSON.stringify(state.noticeForm))
      const res = await addCom(data)
      if (res.code === 200) {
        ElMessage({
          type: 'success',
          message: '新增成功'
        });
      } else {
        ElMessage.warning(res.message)
      }
      emit("getList")
      reset();
      showEditor.value = false
      myEditor.value.clear();
      noticeRef.value.clearValidate();
      dialogVisible.value = false;
    } else if (title.value === '编辑') {
      const {...data} = JSON.parse(JSON.stringify(state.noticeForm))
      const res = await editCom(data)
      if (res.code === 200) {
        ElMessage({
          type: 'success',
          message: '编辑成功'
        });
      } else {
        ElMessage.warning(res.message)
      }
      emit("getList")
      reset();
      showEditor.value = false
      myEditor.value.clear();
      noticeRef.value.clearValidate();
      dialogVisible.value = false;
    }
  }
}
const selectValue = (val) => {
  state.companyList.forEach(item => {
    if(item.name === val){
    if (item.name === val) {
      state.noticeForm.companyId = item.id
    }
  })
}
const handleClose = () => {
    if(title.value ==="新增"|| title.value ==='编辑'){
        myEditor.value.clear();
        showEditor.value=false
    }
  if (title.value === "新增" || title.value === '编辑') {
    myEditor.value.clear();
    showEditor.value = false
  }
    // reset()
  // reset()
  state.fileList = []
    state.companyList = []
    noticeRef.value.clearValidate();
    dialogVisible.value = false;
  state.companyList = []
  noticeRef.value.clearValidate();
  dialogVisible.value = false;
}
const reset = () => {
    state.noticeForm = {
      id: '',
      companySummary: '',
      companyId:null,
      companyName: '',
      filePath: '',
      fileName: ''
    }
    state.fileList = []
  state.noticeForm = {
    id: '',
    companySummary: '',
    companyId: null,
    companyName: '',
    filePath: '',
    fileName: '',
    companyType: null,
    industry: '',
    legalPersonCode: '',
    legalPerson: '',
    registeredCapital: '',
    officeAddress: '',
    otherAddress: ''
  }
  state.fileList = []
}
const handleAvatarSuccess = (res, uploadFile) => {
  if(res.code == 200){
  if (res.code == 200) {
    state.noticeForm.fileName = res.data.originName
    state.noticeForm.filePath = res.data.path
  }else{
  } else {
    state.fileList = []
    ElMessage({
      type: 'warning',
@@ -296,14 +350,14 @@
  }
}
const showTip =()=>{
const showTip = () => {
  ElMessage({
    type: 'warning',
    message: '超出文件上传数量'
  });
}
const picSize = async (rawFile) => {
  if(rawFile.size / 1024 / 1024 > 5){
  if (rawFile.size / 1024 / 1024 > 5) {
    ElMessage({
      type: 'warning',
      message: '文件大小不能超过5M'
@@ -315,14 +369,14 @@
  let path = state.noticeForm.filePath;
  await delPic({path: path}).then(res => {
    if(res.code == 200){
    if (res.code == 200) {
      // ElMessage({
      //   type: 'success',
      //   message: '文件已删除'
      // })
      state.noticeForm.filePath = ''
      state.noticeForm.fileName = ''
    }else{
    } else {
      ElMessage({
        type: 'warning',
        message: res.message
@@ -335,35 +389,39 @@
defineExpose({
    openDialog
  openDialog
});
</script>
<style scoped lang="scss">
.notice{
    :deep(.el-form .el-form-item__label) {
        font-size: 15px;
    }
    .file {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
    }
.notice {
  :deep(.el-form .el-form-item__label) {
    font-size: 15px;
  }
  .file {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}
.reviewTable {
    :deep(table){
        border: 1px solid #ccc;
        text-align: center;
    }
    :deep(table td){
        border: 1px solid #ccc;
        text-align: center;
        padding: 0 5px;
    }
    :deep(table th){
        border: 1px solid #ccc;
    }
  :deep(table) {
    border: 1px solid #ccc;
    text-align: center;
  }
  :deep(table td) {
    border: 1px solid #ccc;
    text-align: center;
    padding: 0 5px;
  }
  :deep(table th) {
    border: 1px solid #ccc;
  }
}
src/views/build/conpanyFunctionConsult/companyInfo/qualifications/components/qualificationsDialog.vue
@@ -25,6 +25,16 @@
        <el-form-item label="证书编号:" prop="certificateNum" >
          <el-input v-model="state.form.certificateNum" :disabled="title === '查看'" placeholder="请输入证书编号"/>
        </el-form-item>
        <el-form-item label="获取时间:" prop="getTime" >
          <el-date-picker
              :disabled="state.title =='查看'"
              v-model="state.form.getTime"
              type="date"
              placeholder="请选择获取日期"
              style="width: 100%"
              value-format="YYYY-MM-DD"
          />
        </el-form-item>
        <el-form-item label="有效期:" prop="effectiveTime" >
          <el-date-picker
              :disabled="title === '查看'"
@@ -84,12 +94,29 @@
const busRef = ref();
const length = ref()
const emit = defineEmits(["getList"]);
const checkEffectiveTime = (rule, value, callback) => {
  if (!value) {
    return callback(new Error('请选择有效期'));
  }
  const getTime = state.form.getTime;
  if (!getTime) {
    return callback(new Error('请先选择获取时间'));
  }
  const effectiveDate = new Date(value);
  const achieveDate = new Date(getTime);
  if (effectiveDate <= achieveDate) {
    callback(new Error('有效期必须在获取时间之后'));
  } else {
    callback();
  }
}
const state = reactive({
  form: {
    id: '',
    companyId: '',
    certificateName: '',
    certificateNum: '',
    getTime: '',
    effectiveTime: '',
    filePath: '',
    fileName: ''
@@ -98,7 +125,8 @@
    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
    certificateName:[{ required: true, message: '请输入取得资质证书名称', trigger: 'blur' }],
    certificateNum:[{ required: true, message: '请输入证书编号', trigger: 'blur' }],
    effectiveTime:[{ required: true, message: '请选择截止日期', trigger: 'blur' }],
    getTime:[{ required: true, message: '请输入获取时间', trigger: 'blur' }],
    effectiveTime:[{ required: true, validator: checkEffectiveTime, trigger: 'blur' }],
    filePath:[{ required: true, message: '请上传证书', trigger: 'blur' }],
  },
  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
@@ -230,6 +258,7 @@
    companyId: '',
    certificateName: '',
    certificateNum: '',
    getTime: '',
    effectiveTime: '',
    filePath: '',
    fileName: ''
src/views/build/conpanyFunctionConsult/orgStructure/departManage/components/departDialog.vue
@@ -205,7 +205,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/build/conpanyFunctionConsult/orgStructure/departManage/components/dutyDialog.vue
@@ -236,7 +236,7 @@
const getUserList = async ()=> {
  const res = await getEmployeeRecords()
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/assetMng/toolsMonitorMeasure/annualVerificationPlan/components/editDialog.vue
@@ -76,6 +76,15 @@
              </el-input>
            </template>
          </el-table-column>
          <el-table-column label="设备类型" prop="planType" align="center">
            <template #default="scope">
              <el-radio-group v-model="scope.row.planType" :disabled="state.title =='查看'">
                <el-radio :label="1">软件</el-radio>
                <el-radio :label="2">硬件</el-radio>
              </el-radio-group>
            </template>
          </el-table-column>
          <el-table-column label="规格型号" prop="model" align="center">
            <template #default="scope">
              <el-input
@@ -366,6 +375,7 @@
    annualVerificationId: null,
    deviceNumber: '',
    deviceName: '',
    planType: null,
    model: '',
    calibrationCycle: '',
    nextCalibrationTime: '',
@@ -380,7 +390,6 @@
}
const handleDelete = (i) =>{
  console.log(i,state.fileList,'list')
  state.form.annualVerificationDevices = state.form.annualVerificationDevices.filter((item,index) => index != i)
  state.fileList = state.fileList.filter((item,index) => index != i)
}
@@ -431,6 +440,7 @@
        annualVerificationId: item.annualVerificationId,
        deviceNumber: item.deviceNumber,
        deviceName: item.deviceName,
        planType: item.planType,
        model: item.model,
        calibrationCycle: item.calibrationCycle,
        nextCalibrationTime: item.nextCalibrationTime,
@@ -473,7 +483,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/assetMng/toolsMonitorMeasure/annualVerificationPlan/index.vue
@@ -12,7 +12,7 @@
          >新增</el-button>
        </el-form-item>
        <el-form-item v-if="isAdmin" label="单位:" >
          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
          <el-select v-model="queryParams.companyId" placeholder="请选择" clearable>
            <el-option
                v-for="item in companyList"
                :key="item.id"
@@ -23,7 +23,7 @@
        </el-form-item>
        <el-form-item v-if="isAdmin" label="年份:">
          <el-date-picker
              v-model="data.queryParams.year"
              v-model="queryParams.year"
              type="year"
              value-format="YYYY"
              placeholder="请选择年份"
@@ -32,17 +32,17 @@
        <el-form-item >
          <el-button v-if="isAdmin" type="primary" @click="getList">查询</el-button>
          <el-button v-if="isAdmin" type="primary" plain @click="reset">重置</el-button>
          <el-button type="primary" plain @click="openDialog('edit',data.dataList)" v-if="data.hasInfo" v-hasPermi="['annualVerificationPlan:list:edit']">编辑</el-button>
          <el-button type="primary" plain @click="openDialog('edit',dataList)" v-if="data.hasInfo" v-hasPermi="['annualVerificationPlan:list:edit']">编辑</el-button>
          <el-button type="primary" plain @click="downloadFile()" v-if="data.hasInfo">导出</el-button>
<!--          <el-button type="primary">导出</el-button>-->
        </el-form-item>
      </el-form>
    </div>
    <div style="background: #fff;padding: 20px;border-radius: 0.6rem">
      <el-form :model="data.dataList" label-position="left" size="default" label-width="120px" v-if="data.hasInfo">
      <el-form :model="dataList" label-position="left" size="default" label-width="120px" v-if="data.hasInfo">
        <el-form-item v-if="isAdmin" label="单位:" prop="companyId">
          <el-input
              v-model.trim="data.dataList.companyName"
              v-model.trim="dataList.companyName"
              style="width: 100%;"
              readonly
          >
@@ -50,7 +50,7 @@
        </el-form-item>
        <el-form-item label="计划名称:" prop="name">
          <el-input
              v-model.trim="data.dataList.name"
              v-model.trim="dataList.name"
              style="width: 100%;"
              readonly
          >
@@ -58,7 +58,7 @@
        </el-form-item>
        <el-form-item label="年份:" prop="year">
          <el-input
              v-model.trim="data.dataList.year"
              v-model.trim="dataList.year"
              style="width: 100%;"
              readonly
          >
@@ -67,21 +67,26 @@
        <el-form-item label="设备列表:" prop="annualVerificationDevices">
        </el-form-item>
        <el-table v-if="data.dataList.annualVerificationDevices && data.dataList.annualVerificationDevices.length>0" :data="data.dataList.annualVerificationDevices" style="margin-bottom: 20px" :border="true">
        <el-table v-if="dataList.annualVerificationDevices && dataList.annualVerificationDevices.length>0" :data="dataList.annualVerificationDevices" style="margin-bottom: 20px" :border="true">
          <el-table-column label="序号" type="index" width="80" align="center">
          </el-table-column>
          <el-table-column label="设备编号" prop="deviceNumber" align="center"/>
          <el-table-column label="设备名称" prop="deviceName" align="center"/>
          <el-table-column label="设备类型" prop="planType" align="center">
            <template #default="scope">
              {{scope.row.planType == 1?'软件':'硬件'}}
            </template>
          </el-table-column>
          <el-table-column label="规格型号" prop="model" align="center"/>
          <el-table-column label="校准周期" prop="calibrationCycle" align="center"/>
          <el-table-column label="下次校准时间" prop="nextCalibrationTime" align="center">
            <template #default="scope">
              {{scope.row.nextCalibrationTime.substring(0,10)}}
              {{scope.row.nextCalibrationTime?.substring(0,10)}}
            </template>
          </el-table-column>
          <el-table-column label="实际检定时间" prop="actCalibrationTime" align="center">
            <template #default="scope">
              {{scope.row.actCalibrationTime.substring(0,10)}}
              {{scope.row.actCalibrationTime?.substring(0,10)}}
            </template>
          </el-table-column>
          <el-table-column label="校准人" prop="calibrationUser" align="center"/>
@@ -126,7 +131,7 @@
          <el-col :span="12">
            <el-form-item label="编制:" prop="establishmentName">
              <el-input
                  v-model.trim="data.dataList.establishmentName"
                  v-model.trim="dataList.establishmentName"
                  style="width: 100%;"
                  readonly
              >
@@ -136,7 +141,7 @@
          <el-col :span="12">
            <el-form-item label="日期:" prop="establishmentTime" >
              <el-input
                  v-model.trim="data.dataList.establishmentTime"
                  v-model.trim="dataList.establishmentTime"
                  style="width: 100%;"
                  readonly
              >
@@ -148,7 +153,7 @@
          <el-col :span="12">
            <el-form-item label="审核:" prop="processName">
              <el-input
                  v-model.trim="data.dataList.processName"
                  v-model.trim="dataList.processName"
                  style="width: 100%;"
                  readonly
              >
@@ -158,7 +163,7 @@
          <el-col :span="12">
            <el-form-item label="日期:" prop="processTime" >
              <el-input
                  v-model.trim="data.dataList.processTime"
                  v-model.trim="dataList.processTime"
                  style="width: 100%;"
                  readonly
              >
@@ -271,6 +276,7 @@
          data.dataList.companyName = data.companyList.find(i=>i.id == data.dataList.companyId)?.name
          data.dataList.establishmentTime = data.dataList.establishmentTime.substring(0,10)
          data.dataList.processTime = data.dataList.processTime.substring(0,10)
          console.log(data.dataList,'data')
        }
      }else{
        data.hasInfo = false
src/views/work/assetMng/toolsMonitorMeasure/equipCalibrateConfirm/components/editDialog.vue
@@ -277,7 +277,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/assetMng/workEnvironmentControl/6sInspectChecklist/components/editDialog.vue
@@ -458,7 +458,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/documentManage/docBorrowCopy/components/editDialog.vue
@@ -286,7 +286,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/documentManage/docChangeInvalidate/components/editDialog.vue
@@ -515,7 +515,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/documentManage/docDestruction/components/editDialog.vue
@@ -307,7 +307,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/documentManage/docDistributeRetrieve/components/editDialog.vue
@@ -467,7 +467,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/marketingManagement/contractManagement/contractLedger/components/editDialog.vue
@@ -35,6 +35,12 @@
              style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="是否交付:" prop="deliver" >
          <el-radio-group v-model="state.form.deliver" :disabled="title =='查看'">
            <el-radio :label="1">是</el-radio>
            <el-radio :label="0">否</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer v-if="title !== '查看'">
        <span class="dialog-footer">
@@ -70,12 +76,14 @@
    number: '',
    contractName: '',
    signDate: '',
    deliver: null
  },
  formRules:{
    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
    number:[{ required: true, message: '请输入编号', trigger: 'blur' }],
    contractName:[{ required: true, message: '请输入合同名称', trigger: 'blur' }],
    signDate:[{ required: true, message: '请选择签订日期', trigger: 'blur' }],
    deliver:[{ required: true, message: '请选择是否交付', trigger: 'blur' }]
  },
  companyList: [],
  isAdmin: false
@@ -155,6 +163,7 @@
    number: '',
    contractName: '',
    signDate: '',
    deliver: null
  }
  state.companyList = []
}
src/views/work/marketingManagement/contractManagement/contractLedger/index.vue
@@ -34,6 +34,11 @@
      <el-table-column label="合同编号" prop="number" align="center"/>
      <el-table-column label="合同名称" prop="contractName" align="center"  width="130"/>
      <el-table-column label="签订日期" prop="signDate" align="center"  />
      <el-table-column label="是否交付" prop="deliver" align="center">
        <template #default="scope">
          {{scope.row.deliver == 1?'是':'否'}}
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width"  width="160">
        <template #default="scope">
          <el-button link type="primary"  @click="openDialog('review',scope.row)" >查看</el-button>
src/views/work/qualityInfo/infrastructureMng/ledger/components/editDialog.vue
@@ -224,7 +224,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/qualityInfo/infrastructureMng/maintainPlan/components/editDialog.vue
@@ -469,7 +469,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
    if(state.form.deptId){
      state.interUserList = state.userList.filter(i=>i.deptId !== state.form.deptId)
    }else{
src/views/work/qualityInfo/infrastructureMng/maintainRecord/components/editDialog.vue
@@ -509,7 +509,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/qualityInfo/infrastructureMng/repairRecord/components/editDialog.vue
@@ -426,7 +426,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/qualityInfo/infrastructureMng/reviewRecordStatistics/components/editDialog.vue
@@ -393,7 +393,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/qualityInfo/inventoryRecord/components/editDialog.vue
@@ -391,7 +391,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/qualityInfo/outsourcingCooperate/outsourcedProcessReview/components/editDialog.vue
@@ -518,7 +518,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/qualityInfo/supplierQuality/satisfiedEvaluste/report/components/editDialog.vue
@@ -10,8 +10,8 @@
    >
      <el-form :model="state.form" size="default" ref="busRef" :rules="state.rules" label-position="left" label-width="135">
        <el-row :gutter="24">
          <el-col :span="24">
            <el-form-item label="单位名称:" prop="companyId" v-if="state.isAdmin">
          <el-col :span="12" v-if="state.isAdmin">
            <el-form-item label="单位名称:" prop="companyId">
              <el-select v-model="state.form.companyId" placeholder="请选择" filterable clearable style="width: 100%" :disabled="title == '查看' || title == '编辑' || !state.isAdmin" @change="selectValueCom">
                <el-option
                    v-for="item in state.companyList"
@@ -20,6 +20,17 @@
                    :value="item.id">
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="年份:" prop="year">
              <el-date-picker
                  v-model="state.form.year"
                  type="year"
                  value-format="YYYY"
                  placeholder="请选择年份"
                  :disabled="title =='查看'"
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -193,6 +204,7 @@
    companyId: null,
    deptId: null,
    reportName: '',
    year: '',
    number: null,
    endTime: null,
    grantAmount: null,
@@ -211,6 +223,7 @@
  rules: {
    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
    reportName: [{ required: true, message: '请输入报告名称', trigger: 'blur' }],
    year: [{ required: true, message: '请选择年份', trigger: 'blur' }],
    number: [{ required: true, message: '请输入编号', trigger: 'blur' }],
    deptId: [{ required: true, message: '请选择实施部门', trigger: 'blur' }],
    endTime: [{ required: true, message: '请选择调查结束日期', trigger: 'blur' }],
@@ -312,13 +325,22 @@
    id: '',
    companyId: null,
    deptId: null,
    reportName: '',
    year: '',
    fictionId: null,
    checkId: null,
    ratifyId: null,
    fictionTime: null,
    frequency: '',
    expectContents:[],
    number: null,
    endTime: null,
    grantAmount: null,
    recycleAmount: null,
    recycleRate: '',
    checkAmount:'',
    yearRate:'',
    ancientlyRate:'',
    researchSatisficing:'',
    deliverySatisficing:'',
    sumSatisficing:'',
    suggest:'',
    agentId:'',
    deptUser:'',
  }
  state.checkProductTypes = []
  state.companyList = []
src/views/work/qualityInfo/supplierQuality/satisfiedEvaluste/report/index.vue
@@ -21,6 +21,14 @@
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="年份:" style="margin-left: 20px">
          <el-date-picker
              v-model="data.queryParams.year"
              type="year"
              value-format="YYYY"
              placeholder="请选择年份"
          />
        </el-form-item>
        <el-form-item v-if="data.isAdmin">
          <el-button type="primary" style="margin-left: 30px" @click="searchClick">查询</el-button>
          <el-button plain @click="reset">重置</el-button>
@@ -97,7 +105,7 @@
onMounted(() => {
  const userInfo = JSON.parse(Cookies.get('userInfo'))
  console.log("userInfo",userInfo)
  data.queryParams.year = new Date().getFullYear().toString()
  data.isAdmin = userInfo.userType === 0;
  if(data.isAdmin){
    data.queryParams.companyId = null
@@ -167,10 +175,10 @@
function reset() {
  if(data.isAdmin){
    data.queryParams = {
      companyId: '',
      pageNum: 1,
      pageSize: 10,
      year: '',
      companyId: null,
      year: new Date().getFullYear().toString(),
      type: ''
    }
    choosedData.value = []
@@ -181,7 +189,7 @@
      companyId: data.queryParams.companyId,
      pageNum: 1,
      pageSize: 10,
      year: '',
      year: new Date().getFullYear().toString(),
      type: ''
    }
  }
src/views/work/selfProblems/mngAudit/mngAuditPlan/components/editDialog.vue
@@ -256,7 +256,7 @@
const getUserList = async (companyId)=> {
  const res = await getEmployeeRecords({companyId: companyId})
  if(res.code == 200){
    state.userList = res.data.list?res.data.list:[]
    state.userList = res.data ? res.data :[]
  }else{
    ElMessage.warning(res.message)
  }
src/views/work/situationSum/components/exportDoc.js
New file
@@ -0,0 +1,360 @@
// 引入工具
import PizZip from 'pizzip';
import Docxtemplater from 'docxtemplater';
import JSZipUtils from 'jszip-utils';
import { saveAs } from 'file-saver';
import ImageModule from 'docxtemplater-image-module-free'
// 加载 .docx 模板文件
function loadFile(url, callback) {
    JSZipUtils.getBinaryContent(url, callback);
}
// 下载生成的文档
export function download(file, name) {
    saveAs(file, name);
}
// 处理富文本,提取段落和缩进信息
function processRichText(html) {
    if (!html) return '';
    // 将HTML字符串转换为DOM对象
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    let result = [];
    // 处理普通段落
    const paragraphs = doc.querySelectorAll('p');
    paragraphs.forEach(p => {
        const style = p.getAttribute('style') || '';
        const indentMatch = style.match(/text-indent:\s*(\d+)pt/);
        const indent = indentMatch ? parseInt(indentMatch[1]) / 24 : 0;
        const text = p.textContent.trim();
        if (text) {
            const indentStr = indent > 0 ? '    '.repeat(indent) : '';
            result.push(indentStr + text);
        }
    });
    // 处理列表(ul/li)
    const lists = doc.querySelectorAll('ul');
    lists.forEach(ul => {
        const lis = ul.querySelectorAll('li');
        lis.forEach(li => {
            // 计算缩进层级
            let parent = li.parentElement;
            let indentLevel = 0;
            while (parent && parent !== ul) {
                if (parent.tagName === 'UL') indentLevel++;
                parent = parent.parentElement;
            }
            const text = li.textContent.trim();
            if (text) {
                // 使用不同符号表示不同层级
                const bullets = ['▪', '•', '▫', '◦'];
                const bullet = bullets[Math.min(indentLevel, bullets.length - 1)];
                const indentStr = '    '.repeat(indentLevel);
                result.push(indentStr + bullet + ' ' + text);
            }
        });
    });
    return result.join('\n'); // 用两个换行符分隔段落
}
function convertTreeToHtml(data) {
    let html = '';
    function buildList(items) {
        let listHtml = '<ul style="font-family: 宋体; font-size: 12pt; line-height: 1.5;">';
        items.forEach(item => {
            listHtml += `<li style="margin-bottom: 6pt;">${item.deptName}`;
            if (item.children && item.children.length > 0) {
                listHtml += buildList(item.children);
            }
            listHtml += '</li>';
        });
        listHtml += '</ul>';
        return listHtml;
    }
    html = buildList(data);
    return html;
}
function generateTableXML(clauses, deptList) {
    const allDeptNames = [...new Set(deptList.map(item => item.deptName))];
    // 构建数据映射
    const dataMap = {};
    deptList.forEach(item => {
        if (!dataMap[item.clauseNum]) dataMap[item.clauseNum] = {};
        dataMap[item.clauseNum][item.deptName] = item.chooseLab;
    });
    return `
    <w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
      <w:tblPr>
        <w:tblW w:w="10000" w:type="pct"/>
        <!-- 边框设置 -->
        <w:tblBorders>
          <w:top w:val="single" w:sz="4" w:space="0" w:color="000000"/>
          <w:left w:val="single" w:sz="4" w:space="0" w:color="000000"/>
          <w:bottom w:val="single" w:sz="4" w:space="0" w:color="000000"/>
          <w:right w:val="single" w:sz="4" w:space="0" w:color="000000"/>
          <w:insideH w:val="single" w:sz="4" w:space="0" w:color="000000"/>
          <w:insideV w:val="single" w:sz="4" w:space="0" w:color="000000"/>
        </w:tblBorders>
        <w:tblLook w:val="04A0"/>
      </w:tblPr>
      <!-- 列宽定义 -->
      <w:tblGrid>
        <w:gridCol w:w="1500"/> <!-- 条款号列 -->
        <w:gridCol w:w="3500"/> <!-- 内容列 -->
        ${allDeptNames.map(() => '<w:gridCol w:w="2000"/>').join('')}
      </w:tblGrid>
      <!-- 表头 -->
      <w:tr>
        <w:tc>
          <w:tcPr><w:tcW w:w="1500" w:type="dxa"/></w:tcPr>
          <w:p><w:r><w:rPr><w:b/></w:rPr><w:t>条款号</w:t></w:r></w:p>
        </w:tc>
        <w:tc>
          <w:tcPr><w:tcW w:w="3500" w:type="dxa"/></w:tcPr>
          <w:p><w:r><w:rPr><w:b/></w:rPr><w:t>内容描述</w:t></w:r></w:p>
        </w:tc>
        ${allDeptNames.map(dept => `
          <w:tc>
            <w:tcPr><w:tcW w:w="2000" w:type="dxa"/></w:tcPr>
            <w:p><w:r><w:rPr><w:b/></w:rPr><w:t>${dept}</w:t></w:r></w:p>
          </w:tc>
        `).join('')}
      </w:tr>
      <!-- 数据行 -->
      ${clauses.map(clause => `
        <w:tr>
          <w:tc><w:p><w:r><w:t>${clause.clauseNum}</w:t></w:r></w:p></w:tc>
          <w:tc><w:p><w:r><w:t>${clause.content}</w:t></w:r></w:p></w:tc>
          ${allDeptNames.map(dept => `
            <w:tc>
              <w:p><w:r><w:t>${dataMap[clause.clauseNum]?.[dept] ?? 0}</w:t></w:r></w:p>
            </w:tc>
          `).join('')}
        </w:tr>
      `).join('')}
    </w:tbl>
  `;
}
function processTableHtml(html) {
    if (!html) return '';
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    const table = doc.querySelector('table');
    if (!table) return '';
    // 提取表格结构
    const rows = table.querySelectorAll('tr');
    const result = [];
    // 处理表头
    const headers = Array.from(rows[0].querySelectorAll('th'))
        .map(th => `${th.textContent.trim()}`)
        .join('\t');
    result.push(headers);
    // 处理数据行
    for (let i = 1; i < rows.length; i++) {
        const cells = rows[i].querySelectorAll('td');
        const rowData = Array.from(cells).map(cell => {
            const indent = cell.style.paddingLeft ? ' '.repeat(parseInt(cell.style.paddingLeft)/4) : '';
            const content = cell.textContent.trim();
            return indent + content;
        }).join('\t');
        result.push(rowData);
    }
    return result.join('\n');
}
function generateTableHtml(clauses, deptList) {
    const allDeptNames = [...new Set(deptList.map(item => item.deptName))];
    const dataMap = {};
    // 构建数据映射
    deptList.forEach(item => {
        if (!dataMap[item.clauseNum]) dataMap[item.clauseNum] = {};
        dataMap[item.clauseNum][item.deptName] = item.chooseLab? (item.chooseLab==1?'●':'○'):'○'
    });
    return `
    <table style="width: 100%;border-collapse: collapse;font-family: 'Microsoft YaHei', sans-serif;font-size: 10.5pt;margin-bottom: 12pt;border: 1px solid #ccc">
      <thead>
        <tr style="background-color: #f5f5f5;width: 100%">
          <th style="padding: 6pt 8pt;border: 1px solid #ccc;text-align: center;font-weight: bold;min-width: 60pt">条款号</th>
          <th style="padding: 6pt 8pt;border: 1px solid #cccccc;text-align: left;font-weight: bold">内容描述</th>
          ${allDeptNames.map(dept => `
            <th style="padding: 6pt 8pt;border: 1pt solid #cccccc;text-align: center;font-weight: bold;min-width: 50pt">${dept}</th>
          `).join('')}
        </tr>
      </thead>
      <tbody>
        ${clauses.map(clause => `
          <tr>
            <td style="padding: 5pt 8pt;
              border: 1pt solid #e0e0e0;
              text-align: center;
              vertical-align: top">${clause.clauseNum}</td>
            <td style="
              padding: 5pt 8pt;
              border: 1pt solid #e0e0e0;
              text-align: left;
              vertical-align: top">${clause.content}</td>
            ${allDeptNames.map(dept => `
              <td style="
                padding: 5pt 8pt;
                border: 1pt solid #e0e0e0;
                text-align: center;
                vertical-align: top">
                ${dataMap[clause.clauseNum]?.[dept] ?? '○'}
              </td>
            `).join('')}
          </tr>
        `).join('')}
      </tbody>
    </table>
  `;
}
const base64Regex =
    /^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(tagValue) {
    if (
        typeof tagValue !== "string" ||
        !base64Regex.test(tagValue)
    ) {
        return false;
    }
    const stringBase64 = tagValue.replace(base64Regex, "");
    if (!validBase64.test(stringBase64)) {
        throw new Error(
            "Error parsing base64 data, your data contains invalid characters"
        );
    }
    // For nodejs, return a Buffer
    if (typeof Buffer !== "undefined" && Buffer.from) {
        return Buffer.from(stringBase64, "base64");
    }
    // For browsers, return a string (of binary content) :
    const binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        const ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}
const imageOptions = {
    getImage(tagValue) {
        return base64Parser(tagValue);
    },
    getSize(img, tagValue, tagName, context) {
        return [600, 600];
    },
};
const base64DataURLToArrayBuffer = (dataURL) => {
    // 返回包含 ArrayBuffer 和原始 base64 字符串的对象
    const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
    if (!base64Regex.test(dataURL)) {
        return { buffer: null, base64: dataURL };
    }
    const stringBase64 = dataURL.replace(base64Regex, "");
    let binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    return {
        buffer: bytes.buffer,  // 图片模块需要的 ArrayBuffer
        base64: stringBase64   // 保留原始 base64 字符串(不带前缀)
    };
};
// 生成并下载 Word 文档
export function generateWordDocument(templatePath, data, name) {
    // 处理部门表格数据
    // if (data.clauses && data.duties) {
    //     const tableHtml = generateTableHtml(data.clauses, data.duties);
    //     data.departmentsTable = processTableHtml(tableHtml);
    // }
    // 生成表格XML
    // if (data.clauses && data.duties) {
    //     data.tableXML = generateTableXML(data.clauses, data.duties);
    // }
    // 处理富文本字段(如果有)
    if (data.companyQualityPolicy && typeof data.companyQualityPolicy === 'string') {
        data.companyQualityPolicy = processRichText(data.companyQualityPolicy);
    }
    loadFile(templatePath, function (error, content) {
        if (error) {
            throw error;
        }
        try {
            // 加载模板文件内容到 PizZip
            const zip = new PizZip(content);
            const imageModule = new ImageModule(imageOptions);
            const doc = new Docxtemplater(zip, {
                paragraphLoop: true,
                linebreaks: true,
                modules: [imageModule]
            });
            // 设置模板中的占位符数据
            doc.setData(data);
            // 渲染文档
            doc.render();
            // 替换占位符
            // let xml = zip.files['word/document.xml'].asText();
            // xml = xml.replace('<!-- TABLE_PLACEHOLDER -->', data.tableXML);
            // zip.file('word/document.xml', xml);
            // 生成最终的文档 Blob
            const fileWord = doc.getZip().generate({
                type: 'blob',
                mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            });
            saveAs(fileWord, name);
        } catch (error) {
            console.error('Error rendering document:', error);
            throw error;
        }
    });
}
src/views/work/situationSum/index.vue
New file
@@ -0,0 +1,218 @@
<template>
  <div class="app-container">
    <div style="display: flex;justify-content: space-between">
      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
<!--        <el-form-item>-->
<!--          <el-button-->
<!--              type="primary"-->
<!--              plain-->
<!--              icon="Plus"-->
<!--              @click="openDialog('add',{})"-->
<!--          >新增</el-button>-->
<!--        </el-form-item>-->
        <el-form-item v-if="isAdmin" label="单位:" >
          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable @change="getList">
            <el-option
                v-for="item in companyList"
                :key="item.id"
                :label="item.name"
                :value="item.id">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="年份:">
          <el-date-picker
              v-model="data.queryParams.year"
              type="year"
              value-format="YYYY"
              placeholder="请选择年份"
          />
        </el-form-item>
        <el-form-item >
          <el-button type="primary" @click="getList">查询</el-button>
          <el-button type="primary" plain @click="reset">重置</el-button>
          <el-button type="primary" plain @click="downloadFile">导出</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div v-if="dataList">
    <div class="info-card">
      <span>{{dataList.companyName}}</span>为<span>{{dataList.companyType == 1?'民营':dataList.companyType == 2?'私营':dataList.companyType == 3?'企事业单位':'未知'}}</span>,属于<span>{{dataList.industry || '未知'}}</span>,法人证书编号:<span>{{dataList.legalPersonCode || '未知'}}</span>,法人代表:<span>{{dataList.legalPerson || '未知'}}</span>,公司现有员工:<span>{{dataList.totalEmployee || '未知'}}</span>人,其中专业技术人员<span>{{dataList.professionalEmployee || '未知'}}</span>人,注册资本<span>{{dataList.registeredCapital || '未知'}}</span>元,总办公地址:<span>{{dataList.officeAddress || '未知'}}</span>,其他分场所地址:<span>{{dataList.otherAddress || '暂无'}}</span>。
      <br>
      <br>
      <div v-if="Array.isArray(dataList.companyCertificateVOList) && dataList.companyCertificateVOList.length>0">
        <div v-for="cert in dataList.companyCertificateVOList"><span>{{cert.getTime?cert.getTime.substring(0,10) +',' : ''}}</span>取得了<span>{{cert.certificateName}}</span>,证书编号:<span>{{cert.certificateNum }}</span>,有效期至:<span>{{cert.effectiveTime?.substring(0,10)}}</span>;</div>
      </div>
    </div>
    <div class="info-card">
      公司目前有计算机总数:<span>{{dataList.computerTotal || 0}}</span>台;其他办公设备:<span>{{dataList.otherOfficeEquipment || 0}}</span>台(传真机、打印机、复印机等)
      <br>
      生产设备总台数:<span>{{dataList.productionEquipment || 0}}</span>台
      <br>
      检测设备总计:<span>{{dataList.detectionEquipment || 0}}</span>台。
    </div>
    <div class="info-card">
      最高管理者重视质量管理体系的建设,通过参加内审、主持管理评审,利用各种例会、质量分析会等方式对员工进行质量意识教育和装备质量法规标准教育,履行了管理承诺,明确和落实了质量职责。对按GJB9001C-2017标准制定的体系文件开展了全员培训或宣贯,提高员工质量意识。
      <br>
      公司坚持用匠心集成系统、以技术创造环境的宗旨,努力建设高效团队,尤其是研发团队的建设,提高科研和生产能力,保持JP投入和增长,开发新的增长点。 公司立足于船用环境净化控制技术系统集成,努力实现产品创一流、服务创顶尖、技术创最优、企业创先进的发展战略,争取成为国内一流的船用净化设备供应商,达到最高的客户满意度。
      <br>
      <br>
      企业质量方针是:<br>
      <span v-html="dataList.companyQualityPolicy"></span>
      <br>
      <span>{{dataList.year || '暂无'}}</span>年度公司年度质量目标:<br>
      <span>{{dataList.companyQualityTarget?.join('、')}}</span>
    </div>
    <div class="info-card">
      自上次审核以来共签订了<span>{{dataList.contractLedger || 0}}</span>份合同,已交付<span>{{dataList.deliveredContract || 0}}</span>个合同。
    </div>
    <div class="info-card">
      <span>{{dataList.year || '暂无'}}</span>年客户满意度为<span>{{dataList.sumSatisficing + '%' || '暂无'}}</span>,<span>{{dataList.year || '暂无'}}</span>年取得<span>{{dataList.patent || 0}}</span>个专利(发明/实用新型/软著)。
    </div>
    <div class="info-card" v-if="dataList.trainPlanName && dataList.trainPlanName !== ''">
      <span>{{dataList.year || '暂无'}}</span>年度安排了<span>{{dataList.trainPlanName}}</span>。对质量方面的提升也起到了至关重要的作用。
    </div>
    <div class="info-card">
      <span>{{dataList.year || '暂无'}}</span>进行了本年度内审,本次内审发现<span>{{dataList.internalAuditCheckResult || 0}}</span>个不符合项,各部门对出现的问题及时做出了纠正措施,并对问题进行了整改,不符合项已关闭。
      <br>
      <span>{{dataList.year || '暂无'}}</span>进行了本年度管理评审,提出了<span>{{dataList.conStatisticNum || 0}}</span>个改进项,目前按计划进行中。
    </div>
    <div class="info-card" v-if="dataList.outsourcedProduct && dataList.outsourcedProduct !== ''">
      外包过程包含<span>{{dataList.outsourcedProduct}}</span>,从供应商选择开始,就要进行审核、评价,确认选择合适的供应商进行外包。从首件、样件、产品都会进行检验确认,符合要求的产品才能入厂进行生产。定期对供应商进行评价,持续确保供应商符合我们产品的要求。
    </div>
    <div class="info-card">
      自上次审核以来,人员增加了<span>{{dataList.totalEmployeeAdd || 0}}</span>人,其中技术人员<span>{{dataList.professionalEmployeeAdd || 0}}</span>人,检测设备增加<span>{{dataList.detectionEquipmentAdd || 0}}</span>台。<span>{{dataList.year || '暂无'}}</span>年培训实施<span>{{dataList.trainPlanCompleted || 0}}</span>次。
    </div>
    </div>
    <div v-else>
      暂无该企业信息
    </div>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/onlineEducation/company";
import Cookies from "js-cookie";
import useUserStore from "@/store/modules/user";
import {getDepEmpsDetail, getSituationSumDetail} from "@/api/staffManage/staff";
import * as echarts from 'echarts';
import {getSixInspectionDetail} from "@/api/assetManage/assetMng";
import {generateWordDocument} from './components/exportDoc.js'
const userStore = useUserStore()
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const data = reactive({
  queryParams: {
    companyId: null,
    year: ''
  },
  total: 0,
  dataList: {},
  companyList: [],
  isAdmin: false
});
const { queryParams, total, dataList,companyList, isAdmin } = toRefs(data);
const userInfo = ref()
onMounted(async ()=>{
  data.queryParams.year = new Date().getFullYear().toString()
  userInfo.value = JSON.parse(Cookies.get('userInfo'))
  if(userStore.roles.includes('admin')){
    data.isAdmin = true
    await getCompanyList()
  }else{
    data.isAdmin = false
    data.queryParams.companyId = userStore.companyId
  }
  await getList()
})
onUnmounted(()=>{
})
const getList = async () => {
  loading.value = true
  const res = await getSituationSumDetail(data.queryParams)
  if(res.code == 200){
    data.dataList = res.data
  }else{
    ElMessage.warning(res.message)
  }
  loading.value = false
}
const getCompanyList = async ()=>{
  const queryParams = {
    pageNum: 1,
    pageSize: 999
  }
  const res = await getCompany(queryParams)
  if (res.code == 200) {
    data.companyList = res.data.list?res.data.list:[]
    data.queryParams.companyId = data.companyList[0].id
  } else {
    ElMessage.warning(res.message)
  }
}
const downloadFile = async ()=>{
    if(data.dataList){
      const tableData = {
        ...data.dataList,
        companyTypeName: data.dataList.companyType== 1?'民营':data.dataList.companyType == 2?'私营':data.dataList.companyType == 3?'企事业单位':'未知',
        certList: data.dataList.companyCertificateVOList ? data.dataList.companyCertificateVOList.map((item,i)=>{
          return {
            ...item,
            index: i + 1,
            getTime: item.getTime ? item.getTime.substring(0,10): '',
            effectiveTime: item.effectiveTime ? item.effectiveTime.substring(0,10): ''
          }
        }): [],
        qualityTarget: data.dataList.companyQualityTarget ? data.dataList.companyQualityTarget.join('、'): '',
        otherAddress: data.dataList.otherAddress? data.dataList.otherAddress : '暂无',
        trainPlanName: data.dataList.trainPlanName? `安排了${data.dataList.trainPlanName},对质量方面的提升也起到了至关重要的作用` : '无培训计划',
        outsourcedProduct: data.dataList.outsourcedProduct? `外包过程包含${data.dataList.outsourcedProduct},从供应商选择开始,就要进行审核、评价,确认选择合适的供应商进行外包。从首件、样件、产品都会进行检验确认,符合要求的产品才能入厂进行生产。定期对供应商进行评价,持续确保供应商符合我们产品的要求。` : '暂无外包过程。',
      }
      try {
        generateWordDocument('/summaryInfo.docx', tableData, tableData.companyName + '情况汇总.docx');
      } catch (error){
        ElMessage({
          type: 'warning',
          message: '导出失败'
        });
      }
    }else{
      ElMessage.warning('暂无数据')
    }
}
/** 重置新增的表单以及其他数据  */
const reset= async()=> {
  data.queryParams = {
    pageNum: 1,
    pageSize: 10,
    companyId: null,
    year: new Date().getFullYear().toString()
  }
  await getCompanyList()
  await getList()
}
</script>
<style lang="scss" scoped>
  .info-card{
    background:#fff;
    padding: 20px;
    border-radius: 0.6rem;
    border: 1px solid #f0f0f0;
    margin-bottom: 10px;
    span{
      color: #2563EB;
    }
  }
</style>