dedsudiyu пре 2 дана
родитељ
комит
5008645791

+ 129 - 0
src/api/safetyEducationExaminationNew/index.js

@@ -715,3 +715,132 @@ export function examUserCoursewareLearningKnowledgePoints(query) {
   })
 }
 
+//查询学年进度列表(从入学年到当前学年,每学年返回状态及通过分数)
+export function examElLabEduRelationAcademicYearProgress(query) {
+  return request({
+    url: '/exam/elLabEduRelation/academicYearProgress',
+    method: 'get',
+    params: query
+  })
+}
+//查询当前登录用户的实验室绑定状态(仅新入学硕士研究生有数据,其他用户返回 null)
+export function examElLabEduRelationBindStatus(query) {
+  return request({
+    url: '/exam/elLabEduRelation/bindStatus',
+    method: 'get',
+    params: query
+  })
+}
+//实验室搜索
+export function examElLabEduRelationLabLists(query) {
+  return request({
+    url: '/exam/elLabEduRelation/labLists',
+    method: 'get',
+    params: query
+  })
+}
+//实验室详情
+export function examElLabEduRelationLabDetail(query) {
+  return request({
+    url: '/exam/elLabEduRelation/labDetail',
+    method: 'get',
+    params: query
+  })
+}
+//查询实验室负责人 待审批的列表
+export function examElLabEduRelationAuditList(data) {
+  return request({
+    url: '/exam/elLabEduRelation/auditList',
+    method: 'post',
+    data: data
+  })
+}
+//实验室负责人审批
+export function examElLabEduRelationAudit(data) {
+  return request({
+    url: '/exam/elLabEduRelation/audit',
+    method: 'post',
+    data: data
+  })
+}
+//实验室申请绑定
+export function examElLabEduRelationBind(data) {
+  return request({
+    url: '/exam/elLabEduRelation/bind',
+    method: 'post',
+    data: data
+  })
+}
+//实验室管理规定查询
+export function laboratoryLabSafeManagerRegulationsDetail(query) {
+  return request({
+    url: '/laboratory/labSafeManagerRegulations/detail',
+    method: 'get',
+    params: query
+  })
+}
+//查询各级别研究生初始最低学时配置
+export function examElConfigMasterInitMinHour(query) {
+  return request({
+    url: '/exam/elConfig/masterInitMinHour',
+    method: 'get',
+    params: query
+  })
+}
+//****************************** 统计相关 ******************************/
+//考试统计页面学年-学年列表
+export function examElStatisticsGetAcademicYear(data) {
+  return request({
+    url: '/exam/elStatistics/getAcademicYear',
+    method: 'post',
+    data: data
+  })
+}
+//考试统计列表
+export function examElStatisticsExamList(data) {
+  return request({
+    url: '/exam/elStatistics/exam/list',
+    method: 'post',
+    data: data
+  })
+}
+//考试统计图表
+export function examElStatisticsExamChart(data) {
+  return request({
+    url: '/exam/elStatistics/exam/chart',
+    method: 'post',
+    data: data
+  })
+}
+//实验室与人员匹配统计图表数据
+export function examElStatisticsLabChart(query) {
+  return request({
+    url: '/exam/elStatistics/lab/chart',
+    method: 'get',
+    params: query
+  })
+}
+//实验室与人员匹配统计列表
+export function examElStatisticsLabList(data) {
+  return request({
+    url: '/exam/elStatistics/lab/list',
+    method: 'post',
+    data: data
+  })
+}
+//学时统计图表
+export function examElStatisticsCreditHourChart(data) {
+  return request({
+    url: '/exam/elStatistics/creditHour/chart',
+    method: 'post',
+    data: data
+  })
+}
+//学时统计列表
+export function examElStatisticsCreditHourList(data) {
+  return request({
+    url: '/exam/elStatistics/creditHour/list',
+    method: 'post',
+    data: data
+  })
+}

+ 50 - 1
src/components/fullScreenFileLook/fullScreenFileLook.vue

@@ -27,6 +27,10 @@
         <p class="position-download-p el-icon-download" @click="downloadOffice" v-if="!officeNullType&&officeType!='text'"></p>
         <p class="title-p">{{lookName}}</p>
         <!--关闭按钮-->
+        <div class="top-button-box" v-if="officeType === 'docx'&&!officeNullType">
+          <p :class="pdfType==1?'check-p':''" @click="buttonCheck(1)">纵向预览</p>
+          <p :class="pdfType==2?'check-p':''" @click="buttonCheck(2)">横向预览</p>
+        </div>
         <p class="position-off-p el-icon-circle-close" @click="outButton"></p>
       </div>
       <div class="max-img-box" ref="viewBox">
@@ -34,6 +38,7 @@
           <vue-office-docx
             v-if="officeType === 'docx'&&!officeNullType"
             :src="lookUrl"
+            :class="pdfType == 1?'':'vertical'"
             @rendered="renderedHandler"
             @error="errorHandler"
           />
@@ -84,7 +89,8 @@
         lookUrl:"",
         currentPage: 1,
         totalPages: 1,
-        currentScale: 1.0 // 初始缩放比例
+        currentScale: 1.0, // 初始缩放比例
+        pdfType:1,
       }
     },
     created () {
@@ -140,10 +146,25 @@
         // 释放 Blob URL
         window.URL.revokeObjectURL(url)
       },
+      //DOCX排版样式切换
+      buttonCheck(val){
+        if(this.pdfType != val){
+          this.$set(this,'pdfType',val);
+        }
+      },
     },
   }
 </script>
 <style scoped lang="scss">
+  //横竖排版
+  .horizontal{
+
+  }
+  .vertical{
+    ::v-deep .docx{
+      padding:0!important;
+    }
+  }
   .fade-enter-active, .fade-leave-active {
     transition: opacity .3s;
   }
@@ -174,6 +195,34 @@
         text-align: center;
         line-height:50px;
       }
+      .top-button-box{
+        display: flex;
+        color:#fff;
+        line-height:30px;
+        text-align: center;
+        font-size:14px;
+        cursor: pointer;
+        z-index: 999;
+        margin:10px 40px 0 0;
+        border-radius: 4px;
+        overflow: hidden;
+        height:30px;
+        p{
+          width:100px;
+          height:30px;
+          background-color: #999;
+          color:#fff;
+        }
+        p:nth-child(1){
+
+        }
+        p:nth-child(2){
+        }
+        .check-p{
+          background-color: #0183fa;
+          color:#fff;
+        }
+      }
       .position-off-p{
         color:#fff;
         width:30px;

+ 0 - 38
src/views/basicsModules/login.vue

@@ -305,25 +305,6 @@ export default {
               this.getExploitConfig();
               this.getHomeConfig();
               // localStorage.setItem('initPage',res.data.skipHomePage);
-              if(res.data.isInitPasswd){
-                this.$confirm('账号当前登录密码为默认密码,是否现在修改?', "提示", {
-                  confirmButtonText: "确定",
-                  cancelButtonText: "取消",
-                  type: "warning"
-                }).then(function() {
-                  let routeData = JSON.parse(localStorage.getItem("routeData"))
-                  for(let i=0;i<routeData.length;i++){
-                    if(!routeData[i].hidden&&routeData[i].name != 'Https://www.sxitdlc.com'){
-                      store.dispatch('settings/setPageName', routeData[i].meta.title)
-                      self.$store.commit("SET_SIDEBAR_ROUTERS", routeData[i].children);
-                      self.$router.push({ path: '/integratedManagement/individualCenter' });
-                      break
-                    }
-                  }
-                }).then(() => {
-                }).catch(() => {
-                });
-              }
               //身份字段规则转义 原定11老师 22学生  新版本后0系统1老师2学生,这里身份字段转义为旧版本规则
               localStorage.setItem('userType',res.data.userType==0||res.data.userType==1?'1':'2')
               //新的身份字段 用于区分系统身份(旧的未废弃)
@@ -375,25 +356,6 @@ export default {
                 this.getExploitConfig();
                 this.getHomeConfig();
                 // localStorage.setItem('initPage',res.data.skipHomePage);
-                if(res.data.isInitPasswd){
-                  this.$confirm('账号当前登录密码为默认密码,是否现在修改?', "提示", {
-                    confirmButtonText: "确定",
-                    cancelButtonText: "取消",
-                    type: "warning"
-                  }).then(function() {
-                    let routeData = JSON.parse(localStorage.getItem("routeData"))
-                    for(let i=0;i<routeData.length;i++){
-                      if(!routeData[i].hidden&&routeData[i].name != 'Https://www.sxitdlc.com'){
-                        store.dispatch('settings/setPageName', routeData[i].meta.title)
-                        self.$store.commit("SET_SIDEBAR_ROUTERS", routeData[i].children);
-                        self.$router.push({ path: '/integratedManagement/individualCenter' });
-                        break
-                      }
-                    }
-                  }).then(() => {
-                  }).catch(() => {
-                  });
-                }
                 //身份字段规则转义 原定11老师 22学生  新版本后0系统1老师2学生,这里身份字段转义为旧版本规则
                 localStorage.setItem('userType',res.data.userType==0||res.data.userType==1?'1':'2')
                 //新的身份字段 用于区分系统身份(旧的未废弃)

+ 1 - 1
src/views/safetyEducationExaminationNew/courseManagement/courseManagement/basicSettings.vue

@@ -146,7 +146,7 @@
             { required: true, message: "请上传课程封面", validator: this.spaceJudgment, trigger: "blur" }
           ],
           popupVerifyEnabled: [
-            { required: true, message: '请输入图形弹窗校验间隔', trigger: 'change' },
+            { required: true, message: '请输入图形弹窗校验间隔', trigger: 'change' },
             { validator: validateData5, trigger: 'blur' }
           ],
         },

+ 235 - 17
src/views/safetyEducationExaminationNew/examManagement/examinationArrange/addPage.vue

@@ -35,7 +35,7 @@
                     </el-select>
                   </el-form-item>
                   <el-form-item label="风险等级" prop="riskLevelId" v-if="placementTest">
-                    <el-select :disabled="showType" v-model="newData.riskLevelId" placeholder="I级" style="width:200px;">
+                    <el-select :disabled="showType" v-model="newData.riskLevelId" placeholder="请选择" style="width:200px;">
                       <el-option v-for="item in optionRiskLevel" :key="item.levelId" :label="item.titleName"
                                  :value="item.levelId"
                       />
@@ -239,7 +239,7 @@
                     <el-switch :disabled="showType" v-model="newData.studyHoursRequirementEnabled" :active-value="1" :inactive-value="0"
                                active-color="#0183FB" inactive-color="#AAAAAA"
                     />
-                    <div class="number-stepper" v-if="newData.studyHoursRequirementEnabled == 1" style="margin-left:12px;height:40px;">
+                    <div class="number-stepper" v-if="!yzyhTest&&newData.studyHoursRequirementEnabled == 1" style="margin-left:12px;height:40px;">
                       <el-input-number
                         :disabled="showType"
                         class="form-input-number"
@@ -251,12 +251,12 @@
                       >
                       </el-input-number>
                     </div>
-                    <span v-if="newData.studyHoursRequirementEnabled == 1" class="unit-text" style="margin-top:2px;"
+                    <span v-if="!yzyhTest&&newData.studyHoursRequirementEnabled == 1" class="unit-text" style="margin-top:2px;"
                     >学时</span>
                     <span class="tip-text" style="margin-left:12px;">开启后,需完成相应学时才能参加考试</span>
                     <el-cascader
                       :disabled="showType"
-                      v-if="newData.studyHoursRequirementEnabled == 1"
+                      v-if="!yzyhTest&&newData.studyHoursRequirementEnabled == 1"
                       style="width: 600px;margin-left:20px;"
                       v-model="newData.knowledgePointIds"
                       :options="optionListC"
@@ -273,6 +273,90 @@
                       placeholder="知识点"
                     ></el-cascader>
                   </div>
+                  <!--应知应会 学时要求-->
+                  <div class="yzyh-max-big-box" v-if="yzyhTest&&newData.studyHoursRequirementEnabled == 1">
+                    <div class="yzyh-title-box">
+                      <p>用户类别</p>
+                      <p>学时要求配置</p>
+                    </div>
+                    <div class="yzyh-input-max-box">
+                      <p class="yzyh-title-p">研究生</p>
+                      <div class="yzyh-text-box">
+                        <p>新生(无需进入实验室的)</p>
+                        <p>非新生</p>
+                      </div>
+                      <div class="yzyh-input-big-box">
+                        <div class="yzyh-input-box">
+                          <el-input-number
+                            :disabled="showType"
+                            class="form-input-number"
+                            style="width:200px;margin-top:20px;"
+                            :min="0"
+                            :max="9999"
+                            :step="1" :precision="0" step-strictly
+                            v-model="newData.mandatoryStudyHoursConfig.freshmanNoLabHours"
+                          >
+                          </el-input-number>
+                        </div>
+                        <div class="yzyh-input-box">
+                          <p class="yzyh-text-p">Ⅰ级{{hourData.level1Hour}}学时 / Ⅱ级{{hourData.level2Hour}}学时 / Ⅲ级{{hourData.level3Hour}}学时 / Ⅳ级{{hourData.level4Hour}}学时</p>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="yzyh-input-max-box">
+                      <p class="yzyh-title-p">本科生</p>
+                      <div class="yzyh-text-box">
+                        <p>非新生</p>
+                      </div>
+                      <div class="yzyh-input-big-box">
+                        <div class="yzyh-input-box">
+                          <el-input-number
+                            :disabled="showType"
+                            class="form-input-number"
+                            style="width:200px;margin-top:20px;"
+                            :min="0"
+                            :max="9999"
+                            :step="1" :precision="0" step-strictly
+                            v-model="newData.mandatoryStudyHoursConfig.undergraduateHours"
+                          >
+                          </el-input-number>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="yzyh-input-max-box">
+                      <p class="yzyh-title-p">教职工</p>
+                      <div class="yzyh-text-box">
+                        <p>新入职</p>
+                        <p>非新入职</p>
+                      </div>
+                      <div class="yzyh-input-big-box">
+                        <div class="yzyh-input-box">
+                          <el-input-number
+                            :disabled="showType"
+                            class="form-input-number"
+                            style="width:200px;margin-top:20px;"
+                            :min="0"
+                            :max="9999"
+                            :step="1" :precision="0" step-strictly
+                            v-model="newData.mandatoryStudyHoursConfig.staffNewHours"
+                          >
+                          </el-input-number>
+                        </div>
+                        <div class="yzyh-input-box">
+                          <el-input-number
+                            :disabled="showType"
+                            class="form-input-number"
+                            style="width:200px;margin-top:20px;"
+                            :min="0"
+                            :max="9999"
+                            :step="1" :precision="0" step-strictly
+                            v-model="newData.mandatoryStudyHoursConfig.staffNonNewHours"
+                          >
+                          </el-input-number>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
                 </el-form-item>
                 <el-form-item label="考前学习" prop="preCourseEnabled">
                   <div class="switch-row">
@@ -498,7 +582,7 @@
   import selectCertificate from './selectCertificate.vue'
   import {
     examElExamAdd,examElExamEdit,examElExamDetail,examElKnowledgePointTreeList,
-    examElExamTypeList, laboratoryLevelConfigGetLevelTitleList
+    examElExamTypeList, laboratoryLevelConfigGetLevelTitleList,examElConfigMasterInitMinHour
   } from '@/api/safetyEducationExaminationNew/index'
 
   export default {
@@ -532,11 +616,24 @@
         }
       }
       const validateData1 = (rule, value, callback) => {
-        if (this.newData.studyHoursRequirementEnabled == 1 && this.newData.studyHoursRequirement == 0) {
+        if (this.newData.studyHoursRequirementEnabled == 1 && !this.yzyhTest && this.newData.studyHoursRequirement == 0) {
           callback(new Error('学时要求不能为0'))
-        } else {
+        }else if (this.newData.studyHoursRequirementEnabled == 1 && this.yzyhTest && this.newData.mandatoryStudyHoursConfig.freshmanNoLabHours == 0) {
+          callback(new Error('研究生-新生的学时要求不能为0'))
+        }else if (this.newData.studyHoursRequirementEnabled == 1 && this.yzyhTest && this.newData.mandatoryStudyHoursConfig.undergraduateHours == 0) {
+          callback(new Error('本科生的学时要求不能为0'))
+        }else if (this.newData.studyHoursRequirementEnabled == 1 && this.yzyhTest && this.newData.mandatoryStudyHoursConfig.staffNewHours == 0) {
+          callback(new Error('教职工-新入职的学时要求不能为0'))
+        }else if (this.newData.studyHoursRequirementEnabled == 1 && this.yzyhTest && this.newData.mandatoryStudyHoursConfig.staffNonNewHours == 0) {
+          callback(new Error('教职工-非新入职的学时要求不能为0'))
+        }else {
           callback()
         }
+        // if (this.newData.studyHoursRequirementEnabled == 1 && this.newData.studyHoursRequirement == 0) {
+        //   callback(new Error('学时要求不能为0'))
+        // } else {
+        //   callback()
+        // }
       }
       const validateData2 = (rule, value, callback) => {
         if (this.newData.preCourseEnabled == 1 && !this.newData.preCourseId) {
@@ -617,8 +714,17 @@
         userList: [],
         //是否是分级考试
         placementTest: false,
+        //是否是应知应会考试
+        yzyhTest:false,
         //答卷次数限制
         answerUnlimited: false,
+        //应知应会学时要求数据
+        hourData:{
+          level1Hour:0,
+          level2Hour:0,
+          level3Hour:0,
+          level4Hour:0,
+        },
         newData: {
           examTypeId: null,
           riskLevelId: null,
@@ -657,7 +763,13 @@
           certificateTemplateName: '',
           certificatePrintEnabled: 0,
           certificateValidMonths: 0,
-          certificateReviewMonths: 0
+          certificateReviewMonths: 0,
+          mandatoryStudyHoursConfig:{
+            freshmanNoLabHours:0,
+            undergraduateHours:0,
+            staffNewHours:0,
+            staffNonNewHours:0,
+          }
         },
         rules: {
           examTypeId: [
@@ -706,7 +818,7 @@
             { validator: validateData4, trigger: 'blur' }
           ],
           // popupVerifyEnabled: [
-          //   { required: true, message: '请输入图形弹窗校验间隔', trigger: 'change' },
+          //   { required: true, message: '请输入图形弹窗校验间隔', trigger: 'change' },
           //   { validator: validateData5, trigger: 'blur' }
           // ],
           certificateEnabled: [
@@ -746,10 +858,11 @@
       }
     },
     mounted() {
-      this.initialize()
-      this.examElExamTypeList()
-      this.laboratoryLevelConfigGetLevelTitleList()
-      this.examElKnowledgePointTreeList()
+      this.initialize();
+      this.examElExamTypeList();
+      this.laboratoryLevelConfigGetLevelTitleList();
+      this.examElConfigMasterInitMinHour();
+      this.examElKnowledgePointTreeList();
     },
     methods: {
       initialize() {
@@ -800,7 +913,8 @@
               certificateTemplateName: response.data.certificateEnabled==1?response.data.certificateTemplateName:'',
               certificatePrintEnabled: response.data.certificateEnabled==1?response.data.certificatePrintEnabled:0,
               certificateValidMonths: response.data.certificateEnabled==1?response.data.certificateValidMonths:0,
-              certificateReviewMonths: response.data.certificateEnabled==1?response.data.certificateReviewMonths:0
+              certificateReviewMonths: response.data.certificateEnabled==1?response.data.certificateReviewMonths:0,
+              mandatoryStudyHoursConfig: response.data.mandatoryStudyHoursConfig,
             };
             this.$set(this,'scopeList',response.data.ruleScopeItems?response.data.ruleScopeItems:[]);
             this.$set(this,'userList',response.data.designatedCandidates?response.data.designatedCandidates:[]);
@@ -858,7 +972,13 @@
           ruleScopeItems:this.newData.scopeType==2?this.scopeList:[],
           designatedCandidates:this.newData.scopeType==3?this.userList:[],
           studyHoursRequirementEnabled:this.newData.studyHoursRequirementEnabled,
-          studyHoursRequirement:this.newData.studyHoursRequirementEnabled == 1?this.newData.studyHoursRequirement:0,
+          studyHoursRequirement:this.newData.studyHoursRequirementEnabled == 1&&!this.yzyhTest?this.newData.studyHoursRequirement:0,
+          mandatoryStudyHoursConfig:this.newData.studyHoursRequirementEnabled == 1&&this.yzyhTest?this.newData.mandatoryStudyHoursConfig:{
+            freshmanNoLabHours:0,
+            undergraduateHours:0,
+            staffNewHours:0,
+            staffNonNewHours:0,
+          },
           knowledgePointIds:this.newData.studyHoursRequirement == 1?this.newData.knowledgePointIds:[],
           preCourseEnabled:this.newData.preCourseEnabled,
           preCourseId:this.newData.preCourseEnabled == 1?this.newData.preCourseId:'',
@@ -1000,13 +1120,36 @@
             } else {
               self.$set(this, 'placementTest', false)
             }
+            if (self.optionExamType[i].examTypeCode == 'yzyh') {
+              self.$set(this, 'yzyhTest', true)
+            } else {
+              self.$set(this, 'yzyhTest', false)
+            }
           }
         }
       },
       //查询全部考试类型
       examElExamTypeList() {
         examElExamTypeList({}).then(response => {
-          this.$set(this, 'optionExamType', response.data)
+          let userInfo = JSON.parse(localStorage.getItem('user'));
+          let schoolId = process.env.VUE_APP_SCHOOL_ID;
+          if(userInfo.position == schoolId ){
+            this.$set(this, 'optionExamType', response.data)
+          } else {
+            let list = [];
+            for(let i=0;i<response.data.length;i++){
+              if(response.data[i].examTypeCode != 'CLASSIFIED' && response.data[i].examTypeCode != 'yzyh'){
+                list.push(response.data[i])
+              }
+            }
+            this.$set(this, 'optionExamType', list)
+          }
+        })
+      },
+      //查询全部考试类型
+      examElConfigMasterInitMinHour() {
+        examElConfigMasterInitMinHour({}).then(response => {
+          this.$set(this, 'hourData', response.data)
         })
       },
       //查询全部考试类型
@@ -1078,7 +1221,82 @@
     .section-block {
       padding-bottom: 8px;
     }
-
+    .yzyh-max-big-box{
+      width:720px;
+      margin-top:20px;
+      .yzyh-title-box{
+        display: flex;
+        background-color: #F2F2F2;
+        border:1px solid #D7D7D7;
+        p{
+          line-height:40px;
+          padding:0 20px;
+        }
+        p:nth-child(1){
+          width:500px;
+        }
+        p:nth-child(2){
+          width:220px;
+        }
+      }
+      .yzyh-input-max-box:nth-child(2){
+        .yzyh-title-p{
+          line-height:160px;
+        }
+      }
+      .yzyh-input-max-box:nth-child(3){
+        .yzyh-title-p{
+          line-height:80px;
+        }
+      }
+      .yzyh-input-max-box:nth-child(4){
+        .yzyh-title-p{
+          line-height:160px;
+        }
+      }
+      .yzyh-input-max-box{
+        display: flex;
+        border-bottom:1px solid #D7D7D7;
+        .yzyh-title-p{
+          width:200px;
+          padding:0 20px;
+          border-left:1px solid #D7D7D7;
+        }
+        .yzyh-text-box{
+          width:300px;
+          p{
+            padding:0 20px;
+            line-height:80px;
+            border-left:1px solid #D7D7D7;
+          }
+          p:nth-child(1){
+          }
+          p:nth-child(2){
+            border-top:1px solid #D7D7D7;
+          }
+        }
+        .yzyh-input-big-box{
+          width:220px;
+          border-right:1px solid #D7D7D7;
+          .yzyh-input-box{
+            height:80px;
+            .yzyh-text-p{
+              color:#666;
+              line-height:40px;
+              background-color:#F4F6FA;
+              margin:20px 20px 20px 0;
+              padding:0 10px;
+              border-radius:4px;
+              margin-left:-180px;
+              text-align: center;
+            }
+          }
+          .yzyh-input-box:nth-child(2){
+            border-top:1px solid #D7D7D7;
+          }
+        }
+      }
+    }
     .section-divider {
       height: 1px;
       background: #e8e8e8;

+ 0 - 3
src/views/safetyEducationExaminationNew/publicConfiguration/certificateTemplate/index.vue

@@ -248,9 +248,6 @@
 </script>
 <style scoped lang="scss">
   .certificateTemplate{
-    ::v-deep .docx{
-      padding:0!important;
-    }
     .certificateTemplatePage{
       .table-top-button-box{
         display: inline-block;

+ 2 - 2
src/views/safetyEducationExaminationNew/safetyTest/mockExam/index.vue

@@ -14,8 +14,8 @@
           <div class="table-top-button-box">
             <div>
               <p :class="examStatus === ''?'check-table-button':''" @click="tableTypeButton('')">全部</p>
-              <p :class="examStatus === 0?'check-table-button':''" @click="tableTypeButton(0)">未开始</p>
-              <p :class="examStatus === 1?'check-table-button':''" @click="tableTypeButton(1)">进行中</p>
+              <p :class="examStatus === 1?'check-table-button':''" @click="tableTypeButton(1)">未开始</p>
+              <p :class="examStatus === 2?'check-table-button':''" @click="tableTypeButton(2)">进行中</p>
               <!--<p :class="examStatus === 2?'check-table-button':''" @click="tableTypeButton(2)">已完成</p>-->
             </div>
           </div>

+ 334 - 290
src/views/safetyEducationExaminationNew/workbench/ExamSituation.vue

@@ -4,23 +4,24 @@
     <div class="card-header">
       <span class="card-title">考试情况统计</span>
       <div class="header-right">
-        <el-select v-model="filterData.year" size="small" style="width:140px">
-          <el-option label="2025-2026 学年" value="2025-2026" />
-          <el-option label="2024-2025 学年" value="2024-2025" />
+        <el-select v-model="valueYear" @change="topChange()" size="small" style="width:220px">
+          <el-option
+            v-for="dict in optionList"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
         </el-select>
         <!-- 分组按钮 -->
         <div class="table-top-button-box">
           <div>
-            <p
-              v-for="(btn, i) in filterData.typeBtns"
-              :key="btn.value"
-              :class="filterData.activeType === btn.value ? 'check-table-button' : ''"
-              @click="filterData.activeType = btn.value"
-            >{{ btn.label }}</p>
+            <p @click="checkButton(2)" :class="checkIndex == 2?'check-table-button':''">研究生</p>
+            <p @click="checkButton(3)" :class="checkIndex == 3?'check-table-button':''">本科生</p>
+            <p @click="checkButton(1)" :class="checkIndex == 1?'check-table-button':''">教职工</p>
           </div>
         </div>
         <!-- 导出按钮 -->
-        <p class="export-btn" @click="() => {}">
+        <p class="export-btn" @click="exportButton()">
           <i class="el-icon-download"></i> 导出数据
         </p>
       </div>
@@ -29,139 +30,70 @@
     <!-- 图表区 -->
     <div class="chart-area">
       <!-- 堆叠柱状图 -->
-      <div class="bar-chart-wrap">
-        <div class="chart-legend">
-          <span class="leg-item"><i class="dot green"></i>通过</span>
-          <span class="leg-item"><i class="dot red"></i>未通过</span>
-          <span class="leg-item"><i class="dot gray"></i>未考</span>
-        </div>
-        <div class="bar-chart">
-          <div class="y-axis">
-            <span v-for="y in yLabels" :key="y">{{ y }}</span>
-          </div>
-          <div class="bars-container">
-            <div v-for="(g, i) in chartData.barGroups" :key="i" class="bar-group">
-              <div class="bar-stack">
-                <div class="seg gray"  :style="{height: scaleH(g.notExam) + 'px'}">
-                  <span v-if="g.notExam > 0" class="seg-label">{{ g.notExam }}</span>
-                </div>
-                <div class="seg red"   :style="{height: scaleH(g.fail) + 'px'}">
-                  <span v-if="g.fail > 0" class="seg-label">{{ g.fail }}</span>
-                </div>
-                <div class="seg green" :style="{height: scaleH(g.pass) + 'px'}">
-                  <span v-if="g.pass > 0" class="seg-label">{{ g.pass }}</span>
-                </div>
-              </div>
-              <div class="bar-label">{{ g.label }}</div>
-            </div>
-          </div>
-        </div>
-      </div>
-
+      <p class="bar-chart-wrap" v-if="!chartData.deptStats[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
+      <div class="bar-chart-wrap" v-if="chartData.deptStats[0]" id="bar-chart-wrap" ref="barChart"></div>
       <!-- 环形图 -->
-      <div class="pie-chart-wrap">
-        <div class="donut-wrap">
-          <svg viewBox="0 0 120 120" width="160" height="160">
-            <!-- 通过 大部分 ~85% -->
-            <circle cx="60" cy="60" r="45" fill="none" stroke="#67c23a"
-              stroke-width="22"
-              stroke-dasharray="238 282"
-              stroke-dashoffset="0"
-              transform="rotate(-90 60 60)" />
-            <!-- 未通过 ~7% -->
-            <circle cx="60" cy="60" r="45" fill="none" stroke="#f56c6c"
-              stroke-width="22"
-              stroke-dasharray="20 282"
-              stroke-dashoffset="-238"
-              transform="rotate(-90 60 60)" />
-            <!-- 未考 ~8% -->
-            <circle cx="60" cy="60" r="45" fill="none" stroke="#c0c4cc"
-              stroke-width="22"
-              stroke-dasharray="24 282"
-              stroke-dashoffset="-258"
-              transform="rotate(-90 60 60)" />
-            <circle cx="60" cy="60" r="28" fill="#fff" />
-          </svg>
-          <!-- 标注线 -->
-          <div class="pie-annotation pie-ann-top">未考</div>
-          <div class="pie-annotation pie-ann-right">未通过</div>
-          <div class="pie-annotation pie-ann-bottom">通过</div>
-        </div>
-        <div class="pie-legend">
-          <div class="p-item"><i class="dot green"></i><span>通过</span></div>
-          <div class="p-item"><i class="dot red"></i><span>未通过</span></div>
-          <div class="p-item"><i class="dot gray"></i><span>未考</span></div>
-        </div>
-      </div>
+      <p class="pie-chart-wrap" v-if="!chartData.deptStats[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
+      <div class="pie-chart-wrap" v-if="chartData.deptStats[0]" id="pie-chart-wrap" ref="pieChart"></div>
     </div>
 
     <!-- 筛选 -->
     <div class="filter-row">
-      <el-select v-model="filterData.college" placeholder="筛选学院" size="small" clearable style="width:130px">
-        <el-option v-for="c in filterData.colleges" :key="c" :label="c" :value="c" />
-      </el-select>
-      <el-select v-model="filterData.examType" placeholder="考试类型" size="small" clearable style="width:120px">
-        <el-option label="新生分级考试" value="新生分级考试" />
-        <el-option label="应知应会考试" value="应知应会考试" />
+      <el-cascader
+        style="width: 180px"
+        clearable
+        v-model="queryParams.deptId"
+        :options="optionListA"
+        :props="{
+                    emitPath:false,
+                    checkStrictly: true,
+                    value: 'deptId',
+                    label: 'deptName',
+                    children: 'child',
+                  }"
+        :show-all-levels="false"
+        filterable
+        placeholder="筛选学院"
+        @change="queryParamsChange"
+      ></el-cascader>
+      <el-select
+        @change="queryParamsChange"
+        v-model="queryParams.examTypeId" clearable placeholder="考试类型" style="width: 160px">
+        <el-option
+          v-for="dict in optionListB"
+          :key="dict.id"
+          :label="dict.examTypeName"
+          :value="dict.id"
+        />
       </el-select>
     </div>
 
     <!-- 统计表格 -->
-    <el-table :data="tableData" border size="small" :span-method="spanMethod">
-      <el-table-column prop="college" label="学院" min-width="130" />
-      <el-table-column prop="examType" label="考试类型" min-width="120" />
-      <el-table-column prop="total" label="应考人数" width="90" align="center" />
-      <el-table-column prop="pass" label="通过人数" width="90" align="center" />
-      <el-table-column prop="fail" label="未通过人数" width="100" align="center">
-        <template slot-scope="scope">
-          <span :class="scope.row.examType !== '汇总' ? 'link-blue' : ''">{{ scope.row.fail }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column prop="notExam" label="未考人数" width="90" align="center" />
-      <el-table-column prop="rate" label="通过率" width="80" align="center" />
-    </el-table>
+
+    <div class="page-content-box">
+      <el-table class="table-box" :data="dataList" border :span-method="objectSpanMethod">
+        <el-table-column prop="deptName" label="学院" />
+        <el-table-column prop="examTypeName" label="考试类型" width="200" />
+        <el-table-column prop="examCount" label="应考人数" width="160" align="center" />
+        <el-table-column prop="passedCount" label="通过人数" width="160" align="center" />
+        <el-table-column prop="notPassedCount" label="未通过人数" width="160" align="center"/>
+        <el-table-column prop="absentCount" label="未考人数" width="160" align="center" />
+        <el-table-column prop="passRate" label="通过率" width="160" align="center" />
+      </el-table>
+    </div>
   </div>
 </template>
 
 <script>
-const colleges = [
-  { name: '化学与化工学院', d1: [45, 38, 4, 3, '84%'], d2: [75, 68, 4, 3, '91%'], sum: [120, 106, 8, 6, '88%'] },
-  { name: '生命科学学院',   d1: [35, 30, 3, 2, '86%'], d2: [60, 54, 4, 2, '90%'], sum: [95,  84,  7, 4, '88%'] },
-  { name: '物理学院',       d1: [28, 23, 3, 2, '82%'], d2: [50, 44, 3, 3, '88%'], sum: [78,  67,  6, 5, '86%'] },
-  { name: '电气工程学院',   d1: [40, 35, 3, 2, '88%'], d2: [70, 63, 4, 3, '90%'], sum: [110, 98,  7, 5, '89%'] },
-  { name: '材料科学学院',   d1: [32, 28, 2, 2, '88%'], d2: [56, 50, 3, 3, '89%'], sum: [88,  78,  5, 5, '89%'] },
-  { name: '机械工程学院',   d1: [25, 21, 2, 2, '84%'], d2: [47, 42, 3, 2, '89%'], sum: [72,  63,  5, 4, '88%'] },
-  { name: '自动化学院',     d1: [22, 20, 1, 1, '91%'], d2: [43, 39, 2, 2, '91%'], sum: [65,  59,  3, 3, '91%'] },
-  { name: '资源环境学院',   d1: [20, 17, 2, 1, '85%'], d2: [36, 32, 2, 2, '89%'], sum: [56,  49,  4, 3, '88%'] },
-]
-
-function buildTableData() {
-  const rows = []
-  colleges.forEach(c => {
-    rows.push({ college: c.name, examType: '新生分级考试', total: c.d1[0], pass: c.d1[1], fail: c.d1[2], notExam: c.d1[3], rate: c.d1[4] })
-    rows.push({ college: c.name, examType: '应知应会考试', total: c.d2[0], pass: c.d2[1], fail: c.d2[2], notExam: c.d2[3], rate: c.d2[4] })
-    rows.push({ college: c.name, examType: '汇总',        total: c.sum[0], pass: c.sum[1], fail: c.sum[2], notExam: c.sum[3], rate: c.sum[4] })
-  })
-  return rows
-}
-
+import echarts from 'echarts'
+import {
+  examElStatisticsGetAcademicYear,examElStatisticsExamList,examElStatisticsExamChart,
+  examElExamTypeList,systemDeptCurrentDept,
+} from "@/api/safetyEducationExaminationNew/index";
 export default {
   name: 'ExamSituation',
   data() {
     return {
-      filterData: {
-        year: '2025-2026',
-        activeType: 'graduate',
-        typeBtns: [
-          { label: '研究生', value: 'graduate' },
-          { label: '本科生', value: 'undergrad' },
-          { label: '教职工', value: 'staff' },
-        ],
-        college: '',
-        examType: '',
-        colleges: colleges.map(c => c.name),
-      },
-      yLabels: [120, 100, 80, 60, 40, 20, 0],
       chartData: {
         barGroups: [
           { label: '化学与化工', pass: 106, fail: 8, notExam: 6 },
@@ -174,40 +106,277 @@ export default {
           { label: '资源环境',   pass: 49,  fail: 4, notExam: 3 },
         ]
       },
-      tableData: buildTableData(),
-      spanMap: {}
+      chartList:[],
+      spanMap: {},
+      optionList:[],
+      optionListA:[],
+      optionListB:[],
+      valueYear:'',
+      checkIndex:2,
+      queryParams:{
+        deptId:'',
+        examTypeId:'',
+      },
+      dataList:[
+        {
+          deptName:'学院1',
+          examType:'类型1',
+          total:'1',
+          pass:'2',
+          fail:'3',
+          notExam:'4',
+          rate:'5',
+        },
+        {
+          deptName:'学院12',
+          examType:'类型1',
+          total:'1',
+          pass:'2',
+          fail:'3',
+          notExam:'4',
+          rate:'5',
+        },
+        {
+          deptName:'学院1',
+          examType:'类型2',
+          total:'1',
+          pass:'2',
+          fail:'3',
+          notExam:'4',
+          rate:'5',
+        },
+      ],
     }
   },
   created() {
-    this.buildSpanMap()
+
+  },
+  mounted() {
+    this.examElExamTypeList();
+    this.systemDeptCurrentDept();
+    this.examElStatisticsGetAcademicYear();
+  },
+  beforeDestroy() {
+    if (this._barChart) this._barChart.dispose()
+    if (this._pieChart) this._pieChart.dispose()
   },
   methods: {
-    scaleH(val) {
-      return Math.round(val / 120 * 180)
+    //导出
+    exportButton(){
+      let self = this;
+      self.$confirm(`确认导出数据?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(async () => {
+        let obj = {
+          academicYear:this.valueYear,
+          personType:this.checkIndex,
+          deptId:this.queryParams.deptId,
+          examTypeId:this.queryParams.examTypeId,
+        };
+        self.download('/exam/elStatistics/exam/export', {...obj}, '考试情况统计.xlsx')
+      }).catch(() => {})
+    },
+    //图表
+    initBarChart() {
+      const groups = this.chartData.deptStats
+      const chart = echarts.init(this.$refs.barChart)
+      this._barChart = chart
+      chart.setOption({
+        legend: {
+          data: ['通过', '未通过', '未考'],
+          top: 10,
+          itemWidth: 14,
+          itemHeight: 14,
+          textStyle: { fontSize: 12, color: '#555' },
+        },
+        grid: { top: 60, bottom: 60, left: 40, right: 10, containLabel: false },
+        xAxis: {
+          type: 'category',
+          data: groups.map(g => g.deptName),
+          axisLine: { lineStyle: { color: '#ddd' } },
+          axisLabel: { fontSize: 11, color: '#666', rotate: 25 },
+          axisTick: { show: false },
+        },
+        yAxis: {
+          type: 'value',
+          splitLine: { lineStyle: { color: '#f0f0f0' } },
+          axisLabel: { fontSize: 11, color: '#aaa' },
+          axisLine: { show: false },
+          axisTick: { show: false },
+        },
+        series: [
+          {
+            name: '通过',
+            type: 'bar',
+            stack: 'total',
+            data: groups.map(g => g.passedCount),
+            itemStyle: { color: '#4fbe7e' },
+            label: { show: true, position: 'inside', fontSize: 11, color: '#fff',
+              formatter: (p) => p.value > 0 ? p.value : '' },
+          },
+          {
+            name: '未通过',
+            type: 'bar',
+            stack: 'total',
+            data: groups.map(g => g.notPassedCount),
+            itemStyle: { color: '#f56c6c' },
+            label: { show: true, position: 'inside', fontSize: 10, color: '#fff',
+              formatter: (p) => p.value > 0 ? p.value : '' },
+          },
+          {
+            name: '未考',
+            type: 'bar',
+            stack: 'total',
+            data: groups.map(g => g.absentCount),
+            itemStyle: { color: '#c0c4cc' },
+            label: { show: true, position: 'inside', fontSize: 10, color: '#fff',
+              formatter: (p) => p.value > 0 ? p.value : '' },
+          },
+        ],
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { type: 'shadow' },
+        },
+      })
+    },
+    //图表
+    initPieChart() {
+      const pass    = this.chartData.totalPassed
+      const fail    = this.chartData.totalNotPassed
+      const notExam = this.chartData.totalAbsent
+      const chart = echarts.init(this.$refs.pieChart)
+      this._pieChart = chart
+      chart.setOption({
+        legend: {
+          orient: 'horizontal',
+          bottom: 0,
+          data: ['通过', '未通过', '未考'],
+          itemWidth: 12,
+          itemHeight: 12,
+          textStyle: { fontSize: 12, color: '#555' },
+        },
+        series: [
+          {
+            type: 'pie',
+            radius: ['45%', '68%'],
+            center: ['50%', '45%'],
+            label: {
+              formatter: '{b}',
+              fontSize: 12,
+              color: '#555',
+            },
+            labelLine: { length: 12, length2: 8 },
+            data: [
+              { value: pass,    name: '通过',  itemStyle: { color: '#4fbe7e' } },
+              { value: fail,    name: '未通过', itemStyle: { color: '#f56c6c' } },
+              { value: notExam, name: '未考',   itemStyle: { color: '#c0c4cc' } },
+            ],
+            emphasis: {
+              itemStyle: { shadowBlur: 6, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.2)' }
+            },
+          },
+        ],
+        tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
+      })
+    },
+    //顶部学年切换
+    topChange(){
+      this.examElStatisticsExamChart();
+      this.examElStatisticsExamList();
+    },
+    //身份切换
+    checkButton(val){
+      if(this.checkIndex!=val){
+        this.$set(this,'checkIndex',val);
+        this.examElStatisticsExamChart();
+        this.examElStatisticsExamList();
+      }
     },
-    buildSpanMap() {
-      const map = {}
-      this.tableData.forEach((row, i) => {
-        if (!map[row.college]) {
-          // find count
-          let count = 0
-          for (let j = i; j < this.tableData.length; j++) {
-            if (this.tableData[j].college === row.college) count++
-            else break
+    //条件变更
+    queryParamsChange(){
+      this.examElStatisticsExamList();
+    },
+    //数据合并
+    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
+      // 只对第一列(学院列,columnIndex === 0)进行合并
+      if (columnIndex === 0) {
+        const list = this.dataList;
+
+        // 如果是第一行,或者当前行的学院与上一行的学院不同,则开始合并计算
+        if (rowIndex === 0 || row.deptName !== list[rowIndex - 1].deptName) {
+          let rowspan = 1;
+          // 向下遍历,统计有多少行是相同的学院
+          for (let i = rowIndex + 1; i < list.length; i++) {
+            if (list[i].deptName === row.deptName) {
+              rowspan++;
+            } else {
+              break; // 遇到不同的学院,停止计算
+            }
           }
-          map[i] = count
+          return { rowspan, colspan: 1 };
         } else {
-          map[i] = 0
+          // 如果当前行的学院与上一行相同,则隐藏当前单元格(被合并了)
+          return { rowspan: 0, colspan: 0 };
         }
-      })
-      this.spanMap = map
+      }
+      // 其他列正常显示
+      return { rowspan: 1, colspan: 1 };
     },
-    spanMethod({ row, column, rowIndex, columnIndex }) {
-      if (columnIndex === 0) {
-        const span = this.spanMap[rowIndex]
-        if (span !== undefined) return { rowspan: span || 0, colspan: span ? 1 : 0 }
+    //考试统计图表
+    examElStatisticsExamChart(){
+      let obj = {
+        academicYear:this.valueYear,
+        personType:this.checkIndex,
       }
-    }
+      examElStatisticsExamChart(obj).then(response => {
+        this.$set(this,'chartData',response.data);
+        this.$nextTick(() => {
+          if(response.data.deptStats[0]){
+            this.$nextTick(() => {
+              this.initBarChart()
+              this.initPieChart()
+            })
+          }
+        })
+      });
+    },
+    //考试统计列表
+    examElStatisticsExamList(){
+      let obj = {
+        academicYear:this.valueYear,
+        personType:this.checkIndex,
+        deptId:this.queryParams.deptId,
+        examTypeId:this.queryParams.examTypeId,
+      }
+      examElStatisticsExamList(obj).then(response => {
+        this.$set(this,'dataList',response.data);
+      });
+    },
+    //查询全部考试类型
+    examElExamTypeList(){
+      examElExamTypeList({}).then(response => {
+        this.$set(this,'optionListB',response.data);
+      });
+    },
+    //学院列表
+    systemDeptCurrentDept(){
+      systemDeptCurrentDept({}).then(response => {
+        const list = response.data || [];
+        this.formatTreeData(list);
+        this.$set(this,'optionListA',list);
+      });
+    },
+    //学年列表
+    examElStatisticsGetAcademicYear(){
+      examElStatisticsGetAcademicYear({}).then(response => {
+        this.$set(this,'optionList',response.data);
+        this.$set(this,'valueYear',response.data[0].value);
+        this.examElStatisticsExamList();
+        this.examElStatisticsExamChart();
+      });
+    },
   }
 }
 </script>
@@ -226,7 +395,7 @@ export default {
     align-items: center;
     margin-bottom: 16px;
     .card-title { font-size: 16px; font-weight: 700; color: #222; }
-    .header-right { display: flex; align-items: center; gap: 20px; }
+    .header-right { width:610px;display: flex; align-items: center; gap: 20px; }
   }
 
   /* 分组筛选按钮 */
@@ -246,11 +415,11 @@ export default {
         &:nth-child(1) { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
         &:nth-child(2) { border-left: none; border-right: none; }
         &:nth-child(3) { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
-        &.check-table-button {
-          border: 1px solid #0183fa;
-          background-color: #0183fa;
-          color: #fff;
-        }
+      }
+      .check-table-button{
+        border: 1px solid #0183fa!important;
+        background-color: #0183fa!important;;
+        color:#fff!important;;
       }
     }
   }
@@ -279,134 +448,13 @@ export default {
 
     .bar-chart-wrap {
       flex: 1;
-      padding: 16px;
+      height: 280px;
       border-right: 1px solid #eee;
-
-      .chart-legend {
-        display: flex;
-        gap: 20px;
-        margin-bottom: 10px;
-        font-size: 12px;
-        color: #555;
-        justify-content: center;
-        .leg-item { display: flex; align-items: center; gap: 4px; }
-      }
-
-      .bar-chart {
-        display: flex;
-        gap: 4px;
-        align-items: flex-end;
-        height: 220px;
-
-        .y-axis {
-          display: flex;
-          flex-direction: column;
-          justify-content: space-between;
-          font-size: 11px;
-          color: #aaa;
-          height: 200px;
-          min-width: 28px;
-          text-align: right;
-        }
-
-        .bars-container {
-          flex: 1;
-          display: flex;
-          align-items: flex-end;
-          justify-content: space-around;
-          height: 200px;
-          border-left: 1px solid #e8e8e8;
-          border-bottom: 1px solid #e8e8e8;
-          padding: 0 8px 20px;
-          position: relative;
-
-          // 横向网格线
-          &::before {
-            content: '';
-            position: absolute;
-            left: 0; right: 0; top: 0; bottom: 20px;
-            background: repeating-linear-gradient(to bottom, #f5f5f5 0, #f5f5f5 1px, transparent 1px, transparent calc(100% / 6));
-            pointer-events: none;
-          }
-
-          .bar-group {
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            position: relative;
-            z-index: 1;
-
-            .bar-stack {
-              display: flex;
-              flex-direction: column;
-              width: 28px;
-              gap: 0;
-              position: relative;
-
-              .seg {
-                width: 100%;
-                position: relative;
-                &.green { background: #4fbe7e; }
-                &.red   { background: #f56c6c; }
-                &.gray  { background: #c0c4cc; }
-
-                .seg-label {
-                  position: absolute;
-                  top: -16px;
-                  left: 50%;
-                  transform: translateX(-50%);
-                  font-size: 10px;
-                  color: #333;
-                  white-space: nowrap;
-                }
-              }
-            }
-
-            .bar-label {
-              position: absolute;
-              bottom: -18px;
-              left: 50%;
-              transform: translateX(-50%);
-              font-size: 10px;
-              color: #555;
-              white-space: nowrap;
-            }
-          }
-        }
-      }
     }
 
     .pie-chart-wrap {
-      width: 300px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      padding: 20px;
-      gap: 16px;
-
-      .donut-wrap {
-        position: relative;
-
-        .pie-annotation {
-          position: absolute;
-          font-size: 11px;
-          color: #555;
-          white-space: nowrap;
-
-          &.pie-ann-top    { top: 10px; left: 50%; transform: translateX(-50%); }
-          &.pie-ann-right  { top: 50%; right: -8px; transform: translateY(-50%); }
-          &.pie-ann-bottom { bottom: 10px; left: 50%; transform: translateX(-50%); color: #4fbe7e; }
-        }
-      }
-
-      .pie-legend {
-        display: flex;
-        gap: 16px;
-        font-size: 12px;
-        color: #555;
-        .p-item { display: flex; align-items: center; gap: 5px; }
-      }
+      width: 320px;
+      height: 280px;
     }
   }
 
@@ -415,18 +463,14 @@ export default {
     gap: 20px;
     margin-bottom: 12px;
   }
+  .page-content-box{
+    height:500px;
+    padding:0;
+    ::v-deep .el-table--medium td:nth-child(1){
+      border-left:1px solid #dfe6ec!important;
+    }
+  }
 
   .link-blue { color: #409eff; }
-
-  .dot {
-    display: inline-block;
-    width: 10px;
-    height: 10px;
-    border-radius: 50%;
-    flex-shrink: 0;
-    &.green { background: #4fbe7e; }
-    &.red   { background: #f56c6c; }
-    &.gray  { background: #c0c4cc; }
-  }
 }
 </style>

+ 199 - 102
src/views/safetyEducationExaminationNew/workbench/LabBindingApply.vue

@@ -1,6 +1,6 @@
 <!-- 实验室绑定申请 -->
 <template>
-  <div class="lab-binding-card">
+  <div class="lab-binding-card" v-show="dataList[0]||showType">
     <div class="card-header">
       <span class="card-title">实验室绑定申请</span>
       <div class="header-right">
@@ -10,95 +10,201 @@
             <p
               v-for="(btn, i) in filterData.statusBtns" :key="btn.value"
               :class="filterData.activeStatus === btn.value ? 'check-table-button' : ''"
-              @click="filterData.activeStatus = btn.value"
+              @click="checkButton(btn.value)"
             >{{ btn.label }}</p>
           </div>
         </div>
       </div>
     </div>
-
-    <el-table :data="filteredData" border size="small" :row-class-name="rowClass">
-      <el-table-column prop="applicant" label="申请人" width="80" />
-      <el-table-column prop="studentId" label="学号" width="110" />
-      <el-table-column prop="userType" label="用户类型" width="100" />
-      <el-table-column prop="college" label="所属学院" min-width="120" show-overflow-tooltip />
-      <el-table-column prop="lab" label="申请实验室" min-width="160" show-overflow-tooltip />
-      <el-table-column prop="level" label="分级" width="70" align="center">
-        <template slot-scope="scope">
-          <span class="level-tag" :style="{background: getLevelBg(scope.row.level), color: getLevelColor(scope.row.level)}">
-            {{ scope.row.level }}
-          </span>
-        </template>
-      </el-table-column>
-      <el-table-column prop="category" label="分类" width="80" />
-      <el-table-column prop="reason" label="进入原因" min-width="120" show-overflow-tooltip />
-      <el-table-column prop="submitTime" label="提交时间" width="140" />
-      <el-table-column prop="passTime" label="通过时间" width="140" />
-      <el-table-column prop="status" label="状态" width="80" align="center">
-        <template slot-scope="scope">
-          <span :class="'status-txt status-' + scope.row.statusCode">{{ scope.row.status }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" min-width="180" align="left">
-        <template slot-scope="scope">
-          <template v-if="scope.row.statusCode === 'reject'">
-            <span class="reject-msg">{{ scope.row.rejectMsg }}</span>
+    <div class="page-content-box">
+      <el-table class="table-box" :data="dataList" border>
+        <el-table-column prop="userName" label="申请人" width="80" show-overflow-tooltip/>
+        <el-table-column prop="userAccount" label="学号" width="110" show-overflow-tooltip/>
+        <el-table-column prop="educationName" label="用户类型" width="100" show-overflow-tooltip/>
+        <el-table-column prop="deptName" label="所属学院" min-width="120" show-overflow-tooltip />
+        <el-table-column prop="subName" label="申请实验室" min-width="160" show-overflow-tooltip />
+        <el-table-column prop="levelName" label="分级" width="70" align="center" show-overflow-tooltip>
+          <template slot-scope="scope">
+            <span :style="'color:'+scope.row.levelColor+';'">{{ scope.row.levelName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="typeName" label="分类" width="120" />
+        <el-table-column prop="reason" label="进入原因" min-width="120" show-overflow-tooltip />
+        <el-table-column prop="submitTime" label="提交时间" width="150" show-overflow-tooltip>
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.submitTime,"{y}-{m}-{d} {h}:{i}") }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="approveTime" label="通过时间" width="150" show-overflow-tooltip>
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.approveTime,"{y}-{m}-{d} {h}:{i}") }}</span>
           </template>
-          <template v-else-if="scope.row.statusCode === 'pass'">
+        </el-table-column>
+        <el-table-column prop="statusCode" label="状态" width="80" align="center" show-overflow-tooltip>
+          <template slot-scope="scope">
+            <span :class="scope.row.statusCode==0?'status-txt-a':(scope.row.statusCode==1?'status-txt-b':(scope.row.statusCode==2?'status-txt-c':''))">
+              {{ scope.row.statusCode==0?'待审批':(scope.row.statusCode==1?'已通过':(scope.row.statusCode==2?'已驳回':'')) }}
+            </span>
           </template>
-          <template v-else>
-            <p class="action-btn btn-pass" @click="() => {}">通过</p>
-            <p class="action-btn btn-reject" @click="() => {}">驳回</p>
+        </el-table-column>
+        <el-table-column label="操作" min-width="130" align="left">
+          <template slot-scope="scope">
+            <p class="button-color-a" v-if="scope.row.statusCode==0" @click="tableButton(1,scope.row)">通过</p>
+            <p class="button-color-b" v-if="scope.row.statusCode==0" @click="tableButton(2,scope.row)">驳回</p>
           </template>
-        </template>
-      </el-table-column>
-    </el-table>
+        </el-table-column>
+      </el-table>
+      <pagination :page-sizes="[20, 30, 40, 50]"
+                  v-show="total>0"
+                  :total="total"
+                  :page.sync="queryParams.page"
+                  :limit.sync="queryParams.pageSize"
+                  @pagination="getList"
+      />
+    </div>
+    <el-dialog class="slab-binding-card-dialog"
+               :modal-append-to-body="false"
+               :close-on-click-modal="false" :close-on-press-escape="false"
+               title="驳回申请"
+               :visible.sync="dialogType"
+               v-if="dialogType"
+               width="560px"
+               append-to-body>
+      <div style="min-height:60px;overflow: hidden">
+        <div>
+          <el-form :model="dialogData" ref="dialogForm" :rules="dialogRules">
+            <el-form-item label="" prop="reason" label-width="0">
+              <el-input
+                type="textarea"
+                :autosize="{ minRows: 4, maxRows: 4}"
+                placeholder="请输入原因"
+                maxLength="100"
+                resize="none"
+                style="width: 520px"
+                v-model="dialogData.reason">
+              </el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer dialog-footer-box">
+        <p class="dialog-footer-button-null"></p>
+        <p class="dialog-footer-button-info" @click="dialogCancel">取消</p>
+        <p class="dialog-footer-button-primary" @click="dialogSubmit">提交</p>
+        <p class="dialog-footer-button-null"></p>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
+  import {
+    examElLabEduRelationAuditList,examElLabEduRelationAudit,
+  } from "@/api/safetyEducationExaminationNew/index";
 export default {
   name: 'LabBindingApply',
   data() {
     return {
       filterData: {
-        activeStatus: 'all',
+        activeStatus: '',
         statusBtns: [
-          { label: '全部',   value: 'all' },
-          { label: '待审批', value: 'pending' },
-          { label: '已通过', value: 'pass' },
-          { label: '已驳回', value: 'reject' },
+          { label: '全部',   value: '' },
+          { label: '待审批', value: '0' },
+          { label: '已通过', value: '1' },
+          { label: '已驳回', value: '2' },
         ]
       },
-      tableData: [
-        { applicant: '张明远', studentId: '2025100001', userType: '硕士研究生', college: '化学与化工学院', lab: '有机化学实验室(A301)', level: 'Ⅱ', category: '化学类', reason: '', submitTime: '2025-09-02 14:30', passTime: '2025-09-09 14:30', status: '待审批', statusCode: 'pending', rejectMsg: '' },
-        { applicant: '赵雨晴', studentId: '2025100023', userType: '硕士研究生', college: '电气工程学院', lab: '电气工程实验室(C102)', level: 'Ⅲ', category: '机电类', reason: '', submitTime: '2025-09-03 09:15', passTime: '2025-09-10 09:15', status: '待审批', statusCode: 'pending', rejectMsg: '' },
-        { applicant: '刘芳华', studentId: '2025100078', userType: '硕士研究生', college: '化学与化工学院', lab: '高分子化学实验室(A402)', level: 'Ⅱ', category: '化学类', reason: '', submitTime: '2025-09-01 16:45', passTime: '2025-09-08 16:45', status: '已通过', statusCode: 'pass', rejectMsg: '' },
-        { applicant: '王浩然', studentId: '2024100088', userType: '硕士研究生', college: '物理学院', lab: '辐射防护实验室(D401)', level: 'Ⅰ', category: '辐射类', reason: '', submitTime: '2025-08-30 10:20', passTime: '2025-09-06 10:20', status: '已通过', statusCode: 'pass', rejectMsg: '' },
-        { applicant: '陈志强', studentId: '2024100056', userType: '硕士研究生', college: '材料科学学院', lab: '材料力学实验室(E203)', level: 'Ⅳ', category: '机电类', reason: '', submitTime: '2025-09-04 11:00', passTime: '2025-09-11 11:00', status: '已驳回', statusCode: 'reject', rejectMsg: '该实验室已满员,请联系系统管理员更换实验室' },
-        { applicant: '李佳欣', studentId: '2025200312', userType: '本科生', college: '生命科学学院', lab: '分子生物学实验室(B205)', level: 'Ⅰ', category: '生物类', reason: '毕业论文需要进入该实验...', submitTime: '2025-09-05 08:30', passTime: '2025-09-12 08:30', status: '待审批', statusCode: 'pending', rejectMsg: '' },
-        { applicant: '周文博', studentId: '2025100034', userType: '硕士研究生', college: '自动化学院', lab: '智能控制实验室(C305)', level: 'Ⅲ', category: '机电类', reason: '', submitTime: '2025-09-05 15:10', passTime: '2025-09-12 15:10', status: '待审批', statusCode: 'pending', rejectMsg: '' },
-        { applicant: '孙晓明', studentId: '2023100091', userType: '硕士研究生', college: '生命科学学院', lab: '微生物学实验室(B308)', level: 'Ⅱ', category: '生物类', reason: '', submitTime: '2025-08-28 13:40', passTime: '2025-09-04 13:40', status: '已通过', statusCode: 'pass', rejectMsg: '' },
-      ]
+      queryParams: {
+        page: 1,
+        pageSize: 20,
+      },
+      dataList: [],
+      //数据数量
+      total: 0,
+      showType:false,
+      dialogType:false,
+      dialogData:{
+        reason:'',
+      },
+      dialogRules:{
+        reason: [
+          { required: true, message: "请输入驳回原因", trigger: "blur" }
+        ],
+      },
+      checkId:null,
     }
   },
-  computed: {
-    filteredData() {
-      if (this.filterData.activeStatus === 'all') return this.tableData
-      return this.tableData.filter(r => r.statusCode === this.filterData.activeStatus)
-    }
+  mounted(){
+    this.getList();
   },
   methods: {
-    getLevelBg(level) {
-      return { 'Ⅰ': '#fff0f0', 'Ⅱ': '#fff7e6', 'Ⅲ': '#e8f4ff', 'Ⅳ': '#eef0ff' }[level] || '#f5f5f5'
+    checkButton(val){
+      if(this.filterData.activeStatus != val){
+        this.$set(this.filterData,'activeStatus',val);
+        this.handleQuery();
+      }
     },
-    getLevelColor(level) {
-      return { 'Ⅰ': '#E86452', 'Ⅱ': '#F59A48', 'Ⅲ': '#0183fa', 'Ⅳ': '#6ea8f7' }[level] || '#666'
+    tableButton(val,row){
+      let self = this;
+      this.$set(this,'checkId',row.id);
+      this.$set(this.dialogData,'reason','');
+      if (val == 1){
+        //通过
+        this.$confirm('确认审核通过?', "警告", {
+          confirmButtonText: "确认",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+        }).then(() => {
+          self.examElLabEduRelationAudit(1)
+        }).catch(() => {});
+      } else {
+        //驳回
+        this.$set(this,'dialogType',true);
+      }
     },
-    rowClass({ row }) {
-      return row.statusCode === 'reject' ? 'row-reject' : ''
-    }
-  }
+    //提交按钮
+    dialogSubmit(){
+      let self = this;
+      this.$refs["dialogForm"].validate(valid => {
+        if (valid) {
+          self.examElLabEduRelationAudit(2)
+        }
+      })
+    },
+    examElLabEduRelationAudit(statusCode){
+      let obj = {
+        id:this.checkId,
+        statusCode:statusCode,
+        reason:this.dialogData.reason,
+      }
+      examElLabEduRelationAudit(obj).then(response => {
+        this.msgSuccess(response.message)
+        this.getList();
+      })
+    },
+    //关闭按钮
+    dialogCancel(){
+      this.$set(this,'dialogType',false);
+    },
+    //查询按钮
+    handleQuery() {
+      this.$set(this.queryParams, 'page', 1)
+      this.getList()
+    },
+    //获取数据列表
+    getList() {
+      let obj = JSON.parse(JSON.stringify(this.queryParams))
+      obj.statusCode = this.filterData.activeStatus;
+      examElLabEduRelationAuditList(obj).then(response => {
+        if(response.data.records[0]){
+          this.$set(this,'showType',true);
+        }
+        this.$set(this, 'dataList', response.data.records)
+        this.$set(this, 'total', response.data.total)
+      })
+    },
+  },
 }
 </script>
 
@@ -128,6 +234,10 @@ export default {
       gap: 20px;
     }
   }
+  .page-content-box{
+    height:500px;
+    padding:0;
+  }
 
   /* 分组筛选按钮 — 与其他页面保持一致 */
   .table-top-button-box {
@@ -165,50 +275,37 @@ export default {
       }
     }
   }
-
-  /* 表格内操作按钮 */
-  .action-btn {
-    display: inline-block;
-    padding: 0 14px;
-    height: 28px;
-    line-height: 28px;
-    border-radius: 4px;
-    font-size: 13px;
+  .button-color-a{
+    width:60px;
     cursor: pointer;
-    margin-right: 6px;
-
-    &.btn-pass {
-      border: 1px solid #00B176;
-      color: #fff;
-      background-color: #00B176;
-    }
-
-    &.btn-reject {
-      border: 1px solid #E86452;
-      color: #fff;
-      background-color: #E86452;
-    }
+    margin-right:10px;
+    display: inline-block;
+    text-align: center;
+    line-height:24px;
+    border-radius:4px;
+    border: 1px solid #00B176;
+    color: #fff;
+    background-color: #00B176;
   }
-
-  .level-tag {
+  .button-color-b{
+    width:60px;
+    cursor: pointer;
     display: inline-block;
-    padding: 0 8px;
-    border-radius: 4px;
-    font-size: 13px;
-    line-height: 24px;
+    text-align: center;
+    line-height:24px;
+    border-radius:4px;
+    border: 1px solid #E86452;
+    color: #fff;
+    background-color: #E86452;
   }
-
-  .status-txt {
-    font-size: 13px;
-    &.status-pending { color: #F59A48; }
-    &.status-pass    { color: #00B176; }
-    &.status-reject  { color: #E86452; }
+  .status-txt-a{
+    color: #F59A48;
   }
-
-  .reject-msg {
-    font-size: 12px;
+  .status-txt-b{
+    color: #00B176;
+  }
+  .status-txt-c{
     color: #E86452;
-    line-height: 1.5;
   }
 
   ::v-deep .row-reject td {

+ 178 - 122
src/views/safetyEducationExaminationNew/workbench/LabPersonMatch.vue

@@ -4,10 +4,10 @@
     <div class="card-header">
       <span class="card-title">实验室与人员匹配</span>
       <div class="header-right">
-        <p class="export-btn export-btn--blue" @click="() => {}">
+        <p class="export-btn export-btn--blue" @click="exportButton1()">
           <i class="el-icon-download"></i> 导出各学院数据
         </p>
-        <p class="export-btn export-btn--green" @click="() => {}">
+        <p class="export-btn export-btn--green" @click="exportButton2()">
           <i class="el-icon-download"></i> 导出实验室人员明细
         </p>
       </div>
@@ -15,82 +15,197 @@
 
     <!-- 柱状图 -->
     <div class="chart-wrap">
-      <div class="bar-chart">
-        <div class="y-axis">
-          <span v-for="y in yLabels" :key="y">{{ y }}</span>
-        </div>
-        <div class="bars-container">
-          <div v-for="(g, i) in chartData" :key="i" class="bar-group">
-            <div class="bar-value">{{ g.value }}</div>
-            <div class="bar-body" :style="{height: scaleH(g.value) + 'px', background: g.color}"></div>
-            <div class="bar-label">{{ g.label }}</div>
-          </div>
-        </div>
-      </div>
+      <p class="bar-chart" v-if="!chartData[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
+      <div class="bar-chart" v-if="chartData[0]" id="bar-chart" ref="barChart"></div>
     </div>
 
     <!-- 筛选 -->
     <div class="filter-row">
-      <el-select v-model="filterData.college" placeholder="筛选学院" size="small" clearable style="width:130px">
-        <el-option v-for="c in filterData.colleges" :key="c" :label="c" :value="c" />
-      </el-select>
-      <el-select v-model="filterData.level" placeholder="实验室等级" size="small" clearable style="width:120px">
-        <el-option label="Ⅰ级" value="1" />
-        <el-option label="Ⅱ级" value="2" />
-        <el-option label="Ⅲ级" value="3" />
-        <el-option label="Ⅳ级" value="4" />
+      <el-cascader
+        style="width: 180px"
+        clearable
+        v-model="queryParams.deptId"
+        :options="optionListA"
+        :props="{
+                    emitPath:false,
+                    checkStrictly: true,
+                    value: 'deptId',
+                    label: 'deptName',
+                    children: 'child',
+                  }"
+        :show-all-levels="false"
+        filterable
+        placeholder="筛选学院"
+        @change="examElStatisticsLabList"
+      ></el-cascader>
+      <el-select v-model="queryParams.levelId" clearable @change="examElStatisticsLabList" placeholder="实验室等级" style="width:200px;">
+        <el-option v-for="item in optionListB" :key="item.levelId" :label="item.titleName"
+                   :value="item.levelId"/>
       </el-select>
     </div>
 
     <!-- 表格 -->
-    <el-table :data="tableData" border size="small">
-      <el-table-column prop="college" label="学院" min-width="140" />
-      <el-table-column prop="l1" label="Ⅰ级人数" width="100" align="center" />
-      <el-table-column prop="l2" label="Ⅱ级人数" width="100" align="center" />
-      <el-table-column prop="l3" label="Ⅲ级人数" width="100" align="center" />
-      <el-table-column prop="l4" label="Ⅳ级人数" width="100" align="center" />
-      <el-table-column prop="total" label="总人数" width="100" align="center">
-        <template slot-scope="scope">
-          <span class="total-link">{{ scope.row.total }}</span>
-        </template>
-      </el-table-column>
-    </el-table>
+    <div class="page-content-box">
+      <el-table class="table-box" :data="tableData" border size="small">
+        <el-table-column prop="deptName" label="学院" min-width="140" />
+        <el-table-column prop="level1Count" label="Ⅰ级人数" width="200" align="center" />
+        <el-table-column prop="level2Count" label="Ⅱ级人数" width="200" align="center" />
+        <el-table-column prop="level3Count" label="Ⅲ级人数" width="200" align="center" />
+        <el-table-column prop="level4Count" label="Ⅳ级人数" width="200" align="center" />
+        <el-table-column prop="totalCount" label="总人数" width="200" align="center"/>
+      </el-table>
+    </div>
   </div>
 </template>
 
 <script>
+import echarts from 'echarts'
+import {
+  examElStatisticsLabChart,examElStatisticsLabList,
+  laboratoryLevelConfigGetLevelTitleList,systemDeptCurrentDept,
+} from "@/api/safetyEducationExaminationNew/index";
+
 export default {
   name: 'LabPersonMatch',
   data() {
     return {
-      filterData: {
-        college: '',
-        level: '',
-        colleges: ['化学与化工学院','生命科学学院','物理学院','电气工程学院','材料科学学院','机械工程学院','自动化学院','资源环境学院'],
+      optionListA:[],
+      optionListB:[],
+      queryParams:{
+        deptId:'',
+        levelId:'',
       },
-      yLabels: [600, 500, 400, 300, 200, 100, 0],
-      chartData: [
-        { label: 'Ⅰ级(重大风险)', value: 156, color: '#f56c6c' },
-        { label: 'Ⅱ级(高风险)',   value: 342, color: '#f59e42' },
-        { label: 'Ⅲ级(中风险)',   value: 528, color: '#f59e42' },
-        { label: 'Ⅳ级(低风险)',   value: 215, color: '#6ea8f7' },
-      ],
-      tableData: [
-        { college: '化学与化工学院', l1: 18, l2: 45, l3: 62, l4: 25, total: 150 },
-        { college: '生命科学学院',   l1: 32, l2: 38, l3: 48, l4: 20, total: 138 },
-        { college: '物理学院',       l1: 25, l2: 30, l3: 42, l4: 18, total: 115 },
-        { college: '电气工程学院',   l1: 15, l2: 42, l3: 58, l4: 30, total: 145 },
-        { college: '材料科学学院',   l1: 20, l2: 35, l3: 50, l4: 22, total: 127 },
-        { college: '机械工程学院',   l1: 12, l2: 38, l3: 55, l4: 28, total: 133 },
-        { college: '自动化学院',     l1: 18, l2: 32, l3: 45, l4: 20, total: 115 },
-        { college: '资源环境学院',   l1: 16, l2: 28, l3: 40, l4: 18, total: 102 },
-      ]
+      chartData: [],
+      tableData: [],
     }
   },
+  mounted() {
+    this.laboratoryLevelConfigGetLevelTitleList();
+    this.systemDeptCurrentDept();
+    this.examElStatisticsLabChart();
+    this.examElStatisticsLabList();
+  },
+  beforeDestroy() {
+    if (this._barChart) this._barChart.dispose()
+  },
   methods: {
-    scaleH(val) {
-      return Math.round(val / 600 * 240)
-    }
+    //导出
+    exportButton1(){
+      let self = this;
+      self.$confirm(`确认导出各学院数据?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(async () => {
+        let obj = {
+          academicYear:this.valueYear,
+          personType:this.checkIndex,
+          deptId:this.queryParams.deptId,
+          examTypeId:this.queryParams.examTypeId,
+        };
+        self.download('/exam/elStatistics/lab/export', {...obj}, '实验室与人员匹配学院数据.xlsx')
+      }).catch(() => {})
+    },
+    exportButton2(){
+      let self = this;
+      self.$confirm(`确认导出实验室人员明细?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(async () => {
+        let obj = {
+          academicYear:this.valueYear,
+          personType:this.checkIndex,
+          deptId:this.queryParams.deptId,
+          examTypeId:this.queryParams.examTypeId,
+        };
+        self.download('/exam/elStatistics/lab/detail-export', {...obj}, '实验室与人员匹配实验室人员明细.xlsx')
+      }).catch(() => {})
+    },
+    initBarChart() {
+      const chart = echarts.init(this.$refs.barChart)
+      this._barChart = chart
+      const data = this.chartData
+      let num = 0;
+      for(let i=0;i<data.length;i++){
+        if(data[i].count>num){
+          num = data[i].count
+        }
+      }
+      chart.setOption({
+        grid: { top: 30, bottom: 20, left: 50, right: 30, containLabel: false },
+        xAxis: {
+          type: 'category',
+          data: data.map(d => d.levelName),
+          axisLine: { lineStyle: { color: '#e0e0e0' } },
+          axisTick: { show: false },
+          axisLabel: { fontSize: 12, color: '#666' },
+        },
+        yAxis: {
+          type: 'value',
+          max: num,
+          interval: 100,
+          splitLine: { lineStyle: { color: '#f0f0f0' } },
+          axisLabel: { fontSize: 11, color: '#aaa' },
+          axisLine: { show: false },
+          axisTick: { show: false },
+        },
+        series: [
+          {
+            type: 'bar',
+            barWidth: 60,
+            data: data.map(d => ({
+              value: d.count,
+              itemStyle: { color: d.levelColor },
+            })),
+            label: {
+              show: true,
+              position: 'top',
+              fontSize: 12,
+              fontWeight: 700,
+              color: '#555',
+              formatter: '{c}',
+            },
+          },
+        ],
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { type: 'shadow' },
+          formatter: (params) => `${params[0].name}: ${params[0].value}`,
+        },
+      })
+    },
+    //图表
+    examElStatisticsLabChart() {
+      examElStatisticsLabChart({}).then(response => {
+        this.$set(this, 'chartData', response.data)
+        if(response.data[0]){
+          this.$nextTick(() => {
+            this.initBarChart()
+          })
+        }
+      })
+    },
+    //列表
+    examElStatisticsLabList() {
+      examElStatisticsLabList(this.queryParams).then(response => {
+        this.$set(this, 'tableData', response.data)
+      })
+    },
+    //查询风险等级
+    laboratoryLevelConfigGetLevelTitleList() {
+      laboratoryLevelConfigGetLevelTitleList({}).then(response => {
+        this.$set(this, 'optionListB', response.data)
+      })
+    },
+    //学院列表
+    systemDeptCurrentDept(){
+      systemDeptCurrentDept({}).then(response => {
+        const list = response.data || [];
+        this.formatTreeData(list);
+        this.$set(this,'optionListA',list);
+      });
+    },
   }
 }
 </script>
@@ -133,70 +248,7 @@ export default {
     margin-bottom: 16px;
 
     .bar-chart {
-      display: flex;
-      gap: 8px;
-      align-items: flex-end;
-      height: 280px;
-
-      .y-axis {
-        display: flex;
-        flex-direction: column;
-        justify-content: space-between;
-        font-size: 11px;
-        color: #aaa;
-        height: 260px;
-        min-width: 32px;
-        text-align: right;
-      }
-
-      .bars-container {
-        flex: 1;
-        display: flex;
-        align-items: flex-end;
-        justify-content: space-around;
-        height: 260px;
-        border-bottom: 1px solid #e8e8e8;
-        padding: 0 40px 24px;
-        position: relative;
-
-        &::before {
-          content: '';
-          position: absolute;
-          left: 0; right: 0; top: 0; bottom: 24px;
-          background: repeating-linear-gradient(to bottom, #f5f5f5 0, #f5f5f5 1px, transparent 1px, transparent calc(100% / 6));
-          pointer-events: none;
-        }
-
-        .bar-group {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          position: relative;
-          z-index: 1;
-
-          .bar-value {
-            font-size: 12px;
-            color: #333;
-            font-weight: 600;
-            margin-bottom: 4px;
-          }
-
-          .bar-body {
-            width: 60px;
-            border-radius: 2px 2px 0 0;
-          }
-
-          .bar-label {
-            position: absolute;
-            bottom: -20px;
-            left: 50%;
-            transform: translateX(-50%);
-            font-size: 11px;
-            color: #555;
-            white-space: nowrap;
-          }
-        }
-      }
+      height: 260px;
     }
   }
 
@@ -206,6 +258,10 @@ export default {
     margin-bottom: 12px;
   }
 
+  .page-content-box{
+    height:500px;
+    padding:0;
+  }
   .total-link { color: #409eff; }
 }
 </style>

+ 1 - 1
src/views/safetyEducationExaminationNew/workbench/ResourceCard.vue

@@ -230,7 +230,7 @@ export default {
     height:60px;
     overflow-x: auto;
     padding-bottom: 4px;
-    margin:20px 0 0 20px;
+    margin:20px 20px 0 20px;
 
     &::-webkit-scrollbar { height: 4px; }
     &::-webkit-scrollbar-thumb { background: #ddd; border-radius: 2px; }

+ 221 - 211
src/views/safetyEducationExaminationNew/workbench/StudyHourStats.vue

@@ -9,97 +9,80 @@
     </div>
 
     <!-- 分组柱状图 -->
-    <div class="chart-wrap">
-      <div class="chart-legend">
-        <span class="leg-item"><i class="dot blue"></i>研究生</span>
-        <span class="leg-item"><i class="dot green"></i>本科生</span>
-        <span class="leg-item"><i class="dot orange"></i>教职工</span>
-      </div>
-      <div class="bar-chart">
-        <div class="y-axis-label">学时</div>
-        <div class="y-axis">
-          <span v-for="y in yLabels" :key="y">{{ y }}</span>
-        </div>
-        <div class="bars-container">
-          <div v-for="(g, i) in chartData" :key="i" class="bar-group">
-            <div class="bars-triple">
-              <div class="single-bar">
-                <div class="bar-val">{{ g.graduate }}</div>
-                <div class="bar blue" :style="{height: scaleH(g.graduate) + 'px'}"></div>
-              </div>
-              <div class="single-bar">
-                <div class="bar-val">{{ g.undergrad }}</div>
-                <div class="bar green" :style="{height: scaleH(g.undergrad) + 'px'}"></div>
-              </div>
-              <div class="single-bar">
-                <div class="bar-val">{{ g.staff }}</div>
-                <div class="bar orange" :style="{height: scaleH(g.staff) + 'px'}"></div>
-              </div>
-            </div>
-            <div class="group-label">{{ g.label }}</div>
-          </div>
-        </div>
-      </div>
-    </div>
+    <p class="chart-wrap" v-if="!chartData[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
+    <div class="chart-wrap" v-if="chartData[0]" id="chart-wrap-bar-chart" ref="barChart"></div>
 
     <!-- 筛选 -->
     <div class="filter-row">
-      <el-select v-model="filterData.userType" placeholder="用户类型" size="small" clearable style="width:110px">
-        <el-option label="研究生" value="graduate" />
-        <el-option label="本科生" value="undergrad" />
-        <el-option label="教职工" value="staff" />
-      </el-select>
-      <el-select v-model="filterData.college" placeholder="学院" size="small" clearable style="width:130px">
-        <el-option v-for="c in filterData.colleges" :key="c" :label="c" :value="c" />
+      <el-select v-model="queryParams.personType" placeholder="用户类型" size="small" clearable style="width:220px">
+        <el-option label="研究生" value="2" />
+        <el-option label="本科生" value="3" />
+        <el-option label="教职工" value="1" />
       </el-select>
-      <el-select v-model="filterData.grade" placeholder="年级" size="small" clearable style="width:100px">
-        <el-option v-for="g in filterData.grades" :key="g" :label="g" :value="g" />
+      <el-cascader
+        style="width: 180px"
+        v-model="queryParams.deptId"
+        :options="optionListA"
+        :props="{
+                  emitPath:false,
+                  checkStrictly: true,
+                  value: 'deptId',
+                  label: 'deptName',
+                  children: 'child',
+                }"
+        :show-all-levels="false"
+        filterable
+        placeholder="学院"
+      ></el-cascader>
+      <el-select v-model="queryParams.gradeNumber" placeholder="学年" style="width: 140px">
+        <el-option
+          v-for="dict in optionListB"
+          :key="dict.gradeNumber"
+          :label="dict.gradeNumber"
+          :value="dict.gradeNumber"
+        />
       </el-select>
-      <el-input v-model="filterData.keyword" placeholder="学号/姓名" size="small" prefix-icon="el-icon-search" style="width:130px" clearable />
+      <el-input v-model="queryParams.searchValue" placeholder="学号/姓名" size="small" prefix-icon="el-icon-search" style="width:210px" clearable />
+      <p class="page-inquire-common-style-button" @click="handleQuery">查询</p>
+      <p class="page-reset-common-style-button" @click="resetQuery">重置</p>
     </div>
 
     <!-- 表格 -->
-    <el-table :data="tableData" border size="small">
-      <el-table-column prop="name" label="姓名" width="80" />
-      <el-table-column prop="studentId" label="学号" width="120" />
-      <el-table-column prop="grade" label="年级" width="80" />
-      <el-table-column prop="college" label="所属学院" min-width="140" show-overflow-tooltip />
-      <el-table-column prop="y1" label="第一年" width="80" align="center" />
-      <el-table-column prop="y2" label="第二年" width="80" align="center">
-        <template slot-scope="scope">
-          <span :class="scope.row.y2IsBlue ? 'link-blue' : ''">{{ scope.row.y2 }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column prop="y3" label="第三年" width="80" align="center">
-        <template slot-scope="scope">
-          <span :class="scope.row.y3IsBlue ? 'link-blue' : ''">{{ scope.row.y3 }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column prop="y4" label="第四年" width="80" align="center" />
-      <el-table-column prop="total" label="总时长" width="80" align="center" />
-    </el-table>
-
-    <!-- 分页 -->
-    <div class="pagination-wrap">
-      <el-pagination
-        :current-page.sync="filterData.page"
-        :page-size="10"
-        :total="50"
-        layout="prev, pager, next"
-        background
-        small
+    <div class="page-content-box">
+      <el-table class="table-box" :data="tableData" border>
+        <el-table-column prop="userName" label="姓名" width="200" />
+        <el-table-column prop="account" label="学工号" width="140" />
+        <el-table-column prop="year" label="学年" width="140" />
+        <el-table-column prop="deptName" label="所属学院" show-overflow-tooltip />
+        <el-table-column prop="year1CreditHours" label="第一年" width="140" align="center" />
+        <el-table-column prop="year2CreditHours" label="第二年" width="140" align="center" />
+        <el-table-column prop="year3CreditHours" label="第三年" width="140" align="center" />
+        <el-table-column prop="year4CreditHours" label="第四年" width="140" align="center" />
+        <el-table-column prop="totalCreditHours" label="总时长" width="140" align="center" />
+      </el-table>
+      <pagination :page-sizes="[20, 30, 40, 50]"
+                  v-show="total>0"
+                  :total="total"
+                  :page.sync="queryParams.page"
+                  :limit.sync="queryParams.pageSize"
+                  @pagination="examElStatisticsCreditHourList"
       />
     </div>
   </div>
 </template>
 
 <script>
+import echarts from 'echarts'
+import {
+  examElStatisticsCreditHourChart,examElStatisticsCreditHourList,
+  systemUserGetAcademicYear,systemDeptCurrentDept,
+} from "@/api/safetyEducationExaminationNew/index";
 export default {
   name: 'StudyHourStats',
   data() {
     return {
       filterData: {
-        userType: '',
+        personType: '',
         college: '',
         grade: '',
         keyword: '',
@@ -107,33 +90,172 @@ export default {
         colleges: ['化学与化工学院','生命科学学院','物理学院','电气工程学院','材料科学学院','机械工程学院','自动化学院','资源环境学院'],
         grades: ['2017级','2018级','2019级','2020级','2021级','2022级'],
       },
-      yLabels: [350, 300, 250, 200, 150, 100, 50, 0],
       chartData: [
-        { label: '化学与化工', graduate: 320, undergrad: 180, staff: 95 },
-        { label: '生命科学',   graduate: 280, undergrad: 160, staff: 72 },
-        { label: '物理',       graduate: 240, undergrad: 145, staff: 68 },
-        { label: '电气工程',   graduate: 300, undergrad: 195, staff: 85 },
-        { label: '材料科学',   graduate: 220, undergrad: 135, staff: 60 },
-        { label: '机械工程',   graduate: 195, undergrad: 170, staff: 55 },
-        { label: '自动化',     graduate: 180, undergrad: 120, staff: 48 },
-        { label: '资源环境',   graduate: 165, undergrad: 110, staff: 52 },
+        // { label: '化学与化工', graduate: 320, undergrad: 180, staff: 95 },
+        // { label: '生命科学',   graduate: 280, undergrad: 160, staff: 72 },
+        // { label: '物理',       graduate: 240, undergrad: 145, staff: 68 },
+        // { label: '电气工程',   graduate: 300, undergrad: 195, staff: 85 },
+        // { label: '材料科学',   graduate: 220, undergrad: 135, staff: 60 },
+        // { label: '机械工程',   graduate: 195, undergrad: 170, staff: 55 },
+        // { label: '自动化',     graduate: 180, undergrad: 120, staff: 48 },
+        // { label: '资源环境',   graduate: 165, undergrad: 110, staff: 52 },
       ],
-      tableData: [
-        { name: '赵丽华', studentId: 'T2018056',   grade: '2018级', college: '化学与化工学院', y1: 4,   y2: 2.5, y2IsBlue: false, y3: 1.8, y3IsBlue: true,  y4: 0,   total: 8.3 },
-        { name: '刘佳琪', studentId: '2022200089',  grade: '2022级', college: '材料科学学院',   y1: 4,   y2: 1.2, y2IsBlue: false, y3: 0.8, y3IsBlue: false, y4: 0,   total: 6 },
-        { name: '王建军', studentId: 'T2020012',   grade: '2020级', college: '物理学院',       y1: 4,   y2: 1.5, y2IsBlue: false, y3: 0,   y3IsBlue: false, y4: 0,   total: 5.5 },
-        { name: '陈小红', studentId: '2021200134',  grade: '2021级', college: '生命科学学院',   y1: 4,   y2: 1.8, y2IsBlue: false, y3: 1.5, y3IsBlue: true,  y4: 0.8, total: 8.1 },
-        { name: '张伟',   studentId: 'T2019033',   grade: '2019级', college: '电气工程学院',   y1: 4,   y2: 2,   y2IsBlue: false, y3: 1.2, y3IsBlue: true,  y4: 0,   total: 7.2 },
-        { name: '李明',   studentId: '2022200201',  grade: '2022级', college: '机械工程学院',   y1: 4,   y2: 0.8, y2IsBlue: false, y3: 0,   y3IsBlue: false, y4: 0,   total: 4.8 },
-        { name: '周芳',   studentId: 'T2017045',   grade: '2017级', college: '化学与化工学院', y1: 4,   y2: 2.2, y2IsBlue: false, y3: 1.8, y3IsBlue: false, y4: 1.5, total: 9.5 },
-        { name: '吴强',   studentId: '2021200089',  grade: '2021级', college: '自动化学院',     y1: 4,   y2: 1.5, y2IsBlue: false, y3: 1.2, y3IsBlue: true,  y4: 0.5, total: 7.2 },
-      ]
+      tableData: [],
+      optionListA:[],
+      optionListB:[],
+      queryParams:{
+        page:1,
+        pageSize:20,
+        personType:'',
+        deptId:'',
+        gradeNumber:'',
+        searchValue:'',
+      },
+      total:0,
     }
   },
+  mounted() {
+    this.systemUserGetAcademicYear();
+    this.systemDeptCurrentDept();
+    this.examElStatisticsCreditHourChart();
+    this.examElStatisticsCreditHourList();
+  },
+  beforeDestroy() {
+    if (this._barChart) this._barChart.dispose()
+  },
   methods: {
-    scaleH(val) {
-      return Math.round(val / 350 * 200)
-    }
+    initBarChart() {
+      const chart = echarts.init(this.$refs.barChart)
+      this._barChart = chart
+      const data = this.chartData
+      chart.setOption({
+        legend: {
+          data: ['研究生', '本科生', '教职工'],
+          top: 10,
+          right: 'center',
+          itemWidth: 14,
+          itemHeight: 14,
+          textStyle: { fontSize: 12, color: '#555' },
+        },
+        grid: { top: 50, bottom: 36, left: 56, right: 20, containLabel: false },
+        xAxis: {
+          type: 'category',
+          data: data.map(d => d.deptName),
+          axisLine: { lineStyle: { color: '#ddd' } },
+          axisTick: { show: false },
+          axisLabel: { fontSize: 12, color: '#666', rotate: 30 },
+        },
+        yAxis: {
+          type: 'value',
+          name: '学时',
+          nameTextStyle: { fontSize: 11, color: '#aaa', padding: [0, 30, 0, 0] },
+          max: 350,
+          interval: 50,
+          splitLine: { lineStyle: { color: '#f0f0f0' } },
+          axisLabel: { fontSize: 11, color: '#aaa' },
+          axisLine: { show: false },
+          axisTick: { show: false },
+        },
+        series: [
+          {
+            name: '研究生',
+            type: 'bar',
+            barGap: '10%',
+            barCategoryGap: '40%',
+            data: data.map(d => d.graduateCreditHours),
+            itemStyle: { color: '#5b9cf6' },
+            label: {
+              show: true,
+              position: 'top',
+              fontSize: 11,
+              color: '#555',
+              formatter: '{c}',
+            },
+          },
+          {
+            name: '本科生',
+            type: 'bar',
+            data: data.map(d => d.undergraduateCreditHours),
+            itemStyle: { color: '#4fbe7e' },
+            label: {
+              show: true,
+              position: 'top',
+              fontSize: 11,
+              color: '#555',
+              formatter: '{c}',
+            },
+          },
+          {
+            name: '教职工',
+            type: 'bar',
+            data: data.map(d => d.staffCreditHours),
+            itemStyle: { color: '#f59e42' },
+            label: {
+              show: true,
+              position: 'top',
+              fontSize: 11,
+              color: '#555',
+              formatter: '{c}',
+            },
+          },
+        ],
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { type: 'shadow' },
+        },
+      })
+    },
+    //学时统计图表
+    examElStatisticsCreditHourChart(){
+      examElStatisticsCreditHourChart({}).then(response => {
+        this.$set(this,'chartData',response.data.deptStats);
+        if(response.data.deptStats[0]){
+          this.$nextTick(() => {
+            this.initBarChart()
+          })
+        }
+      });
+    },
+    //学时统计列表
+    examElStatisticsCreditHourList(){
+      let obj = JSON.parse(JSON.stringify(this.queryParams))
+      examElStatisticsCreditHourList(obj).then(response => {
+        this.$set(this,'tableData',response.data.records);
+        this.$set(this,'total',response.data.total);
+      });
+    },
+    //学年下拉列表
+    systemUserGetAcademicYear(){
+      systemUserGetAcademicYear({}).then(response => {
+        this.$set(this,'optionListB',response.data);
+      });
+    },
+    //学院列表
+    systemDeptCurrentDept(){
+      systemDeptCurrentDept({}).then(response => {
+        const list = response.data || [];
+        this.formatTreeData(list);
+        this.$set(this,'optionListA',list);
+      });
+    },
+    //查询按钮
+    handleQuery(){
+      this.$set(this.queryParams,'page',1);
+      this.examElStatisticsCreditHourList();
+    },
+    //重置按钮
+    resetQuery(){
+      this.$set(this,'dateRange',[])
+      this.$set(this,'queryParams',{
+        page:1,
+        pageSize:20,
+        personType:'',
+        deptId:'',
+        gradeNumber:'',
+        searchValue:'',
+      });
+      this.examElStatisticsCreditHourList();
+    },
   }
 }
 </script>
@@ -170,113 +292,8 @@ export default {
   .chart-wrap {
     border: 1px solid #eee;
     border-radius: 6px;
-    padding: 16px 20px 30px;
     margin-bottom: 16px;
-
-    .chart-legend {
-      display: flex;
-      gap: 20px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: #555;
-      justify-content: center;
-      .leg-item { display: flex; align-items: center; gap: 4px; }
-    }
-
-    .bar-chart {
-      display: flex;
-      gap: 4px;
-      align-items: flex-end;
-      position: relative;
-
-      .y-axis-label {
-        font-size: 11px;
-        color: #aaa;
-        writing-mode: vertical-rl;
-        transform: rotate(180deg);
-        position: absolute;
-        left: -14px;
-        top: 0;
-        height: 220px;
-        display: flex;
-        align-items: center;
-      }
-
-      .y-axis {
-        display: flex;
-        flex-direction: column;
-        justify-content: space-between;
-        font-size: 11px;
-        color: #aaa;
-        height: 220px;
-        min-width: 28px;
-        text-align: right;
-      }
-
-      .bars-container {
-        flex: 1;
-        display: flex;
-        align-items: flex-end;
-        justify-content: space-around;
-        height: 220px;
-        border-bottom: 1px solid #e8e8e8;
-        padding: 0 8px 24px;
-        position: relative;
-
-        &::before {
-          content: '';
-          position: absolute;
-          left: 0; right: 0; top: 0; bottom: 24px;
-          background: repeating-linear-gradient(to bottom, #f5f5f5 0, #f5f5f5 1px, transparent 1px, transparent calc(100% / 7));
-          pointer-events: none;
-        }
-
-        .bar-group {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          position: relative;
-          z-index: 1;
-
-          .bars-triple {
-            display: flex;
-            gap: 2px;
-            align-items: flex-end;
-
-            .single-bar {
-              display: flex;
-              flex-direction: column;
-              align-items: center;
-              gap: 2px;
-
-              .bar-val {
-                font-size: 9px;
-                color: #333;
-                font-weight: 600;
-              }
-
-              .bar {
-                width: 14px;
-                border-radius: 2px 2px 0 0;
-                &.blue   { background: #5b9cf6; }
-                &.green  { background: #4fbe7e; }
-                &.orange { background: #f59e42; }
-              }
-            }
-          }
-
-          .group-label {
-            position: absolute;
-            bottom: -18px;
-            left: 50%;
-            transform: translateX(-50%);
-            font-size: 10px;
-            color: #555;
-            white-space: nowrap;
-          }
-        }
-      }
-    }
+    height: 300px;
   }
 
   .filter-row {
@@ -285,6 +302,10 @@ export default {
     margin-bottom: 12px;
     flex-wrap: wrap;
   }
+  .page-content-box{
+    height:500px;
+    padding:0;
+  }
 
   .link-blue { color: #409eff; }
 
@@ -293,16 +314,5 @@ export default {
     justify-content: center;
     margin-top: 14px;
   }
-
-  .dot {
-    display: inline-block;
-    width: 10px;
-    height: 10px;
-    border-radius: 2px;
-    flex-shrink: 0;
-    &.blue   { background: #5b9cf6; }
-    &.green  { background: #4fbe7e; }
-    &.orange { background: #f59e42; }
-  }
 }
 </style>

+ 29 - 38
src/views/safetyEducationExaminationNew/workbench/TaskCard.vue

@@ -13,8 +13,8 @@
         </div>
         <div class="bottom-content-box scrollbar-box">
           <div class="bottom-big-box-1" v-for="(item,index) in yearMinList">
-            <p>{{item.yearMinListData1}}</p>
-            <p>{{item.yearMinListData2==1?'进行中':(item.yearMinListData2==2?'通过丨'+item.yearMinListData3:(item.yearMinListData2==3?'未通过':''))}}</p>
+            <p>{{item.academicYear}}</p>
+            <p>{{item.statusCode==0?'未开始':(item.statusCode==2?'通过丨'+item.score:(item.statusCode==1?'进行中':'未通过'))}}</p>
           </div>
         </div>
       </div>
@@ -80,7 +80,7 @@
 
 <script>
   import {
-    examUserCoursewareLearningRestart,
+    examUserCoursewareLearningRestart,examElLabEduRelationAcademicYearProgress,
     examUserCourseLearningList,examUserExamMockList,examUserExamList,examUserExamStart,
   } from "@/api/safetyEducationExaminationNew/index";
 export default {
@@ -91,19 +91,19 @@ export default {
       yearType:true,
       yearMinList:[
         {
-          yearMinListData1:'2025-2026',
-          yearMinListData2:'1',
-          yearMinListData3:'99',
+          academicYear:'2025-2026',
+          statusCode:'1',
+          score:'99',
         },
         {
-          yearMinListData1:'2025-2026',
-          yearMinListData2:'2',
-          yearMinListData3:'99',
+          academicYear:'2025-2026',
+          statusCode:'2',
+          score:'99',
         },
         {
-          yearMinListData1:'2025-2026',
-          yearMinListData2:'3',
-          yearMinListData3:'99',
+          academicYear:'2025-2026',
+          statusCode:'3',
+          score:'99',
         },
       ],
       studyMinList:[],
@@ -119,6 +119,7 @@ export default {
   },
   methods: {
     initialization(){
+      this.examElLabEduRelationAcademicYearProgress();
       this.examUserCourseLearningList();
       this.examUserExamMockList();
       this.examUserExamList();
@@ -128,30 +129,10 @@ export default {
       let self = this;
       if(type == 1){
         //刷题
-        if(item.learnStatus == 2){
-          this.$confirm('该课件已学完,是否重置进度重新学习?', "警告", {
-            confirmButtonText: "确认",
-            cancelButtonText: "取消",
-            type: "warning"
-          }).then(function() {
-          }).then(() => {
-            examUserCoursewareLearningRestart({coursewareId:item.coursewareId}).then(response => {
-              self.$router.push({
-                name: 'learningMaterials',
-                params: {
-                  coursewareId: item.coursewareId,
-                }
-              })
-            });
-          }).catch(() => {});
-        }else{
-          this.$router.push({
-            name: 'learningMaterials',
-            params: {
-              coursewareId: item.coursewareId,
-            }
-          })
-        }
+        self.$router.push({
+          name: 'practiceQuestions',
+          params: {}
+        })
       }else if(type == 2){
         //学习课程
         this.$router.push({
@@ -163,12 +144,12 @@ export default {
         })
       }else if(type == 3){
         //模拟考试
-        if(item.attemptId){
+        if(item.mockExamId){
           this.$router.push({
             name: 'mockExam',
             params: {
               examId: item.examId,
-              attemptId: item.attemptId,
+              attemptId: item.mockExamId,
             }
           })
         }else{
@@ -216,6 +197,12 @@ export default {
         }
       }
     },
+    //查询学年进度列表
+    examElLabEduRelationAcademicYearProgress(){
+      examElLabEduRelationAcademicYearProgress({}).then(response => {
+        this.$set(this, 'yearMinList', response.data)
+      })
+    },
     //学习任务
     examUserCourseLearningList(){
       let obj = {
@@ -231,6 +218,7 @@ export default {
       let obj = {
         page:1,
         pageSize:20,
+        examStatus:2,
       }
       examUserExamMockList(obj).then(response => {
         this.$set(this, 'examinationMinList', response.data.records)
@@ -241,6 +229,7 @@ export default {
       let obj = {
         page:1,
         pageSize:5,
+        examStatus:2,
       }
       examUserExamList(obj).then(response => {
         this.$set(this, 'dataList', response.data.records)
@@ -355,6 +344,7 @@ export default {
           padding:12px 14px;
           margin-bottom:12px;
           height:68px;
+          cursor: pointer;
           p{
             font-size:14px;
             line-height:22px;
@@ -370,6 +360,7 @@ export default {
           padding:12px 14px;
           margin-bottom:12px;
           height:68px;
+          cursor: pointer;
           p{
             font-size:14px;
             line-height:22px;

+ 474 - 79
src/views/safetyEducationExaminationNew/workbench/UserInfoCard.vue

@@ -6,158 +6,362 @@
       <p>当前推荐资料类型:{{textName}}</p>
     </div>
     <div class="user-info-max-box">
+      <!--用户信息-->
       <div class="left-max-box">
         <div class="left-min-box">
           <p>姓名</p>
-          <p>{{userInfo.data1}}</p>
+          <p>{{userInfo.userName}}</p>
         </div>
         <div class="left-min-box">
           <p>学工号</p>
-          <p>{{userInfo.data2}}</p>
+          <p>{{userInfo.account}}</p>
         </div>
         <div class="left-min-box">
           <p>类型</p>
-          <p>{{userInfo.data3}}</p>
+          <p>{{userInfo.userType == 2?userInfo.educationName:(userInfo.userType == 1?'教职工':'系统用户')}}</p>
         </div>
         <div class="left-min-box">
           <p>所属单位</p>
-          <p>{{userInfo.data4}}</p>
+          <p>{{userInfo.deptName}}</p>
         </div>
-        <div class="left-min-box">
+        <div class="left-min-box" v-if="userInfo.userType == 2">
           <p>实验室信息</p>
-          <p>{{userInfo.data5?userInfo.data5:'暂无绑定实验室'}}</p>
-          <p @click="bindingSubButton()">去绑定</p>
+          <p>{{userInfo.labName?userInfo.labName:'暂无绑定实验室'}}</p>
+          <p @click="shadeButton(4)" v-show="!userInfo.bound">去绑定</p>
+          <p v-if="userInfo.bound">{{userInfo.bindStatus}}</p>
         </div>
       </div>
+      <!--进度-->
       <div class="center-max-box">
         <div class="center-title-box scrollbar-x-box">
-          <p :class="index == checkUserIndex?'check-title-p':''"
-             @click="checkTitleButton(index)"
-            v-for="(item,index) in userInfo.data6" :key="index">{{item.dataName}}</p>
+          <p class="check-title-p">{{userInfo.gradeRange}}</p>
+          <p class="check-title-p" v-if="userInfo.labName">{{userInfo.labName}}</p>
+          <p class="check-title-p" v-if="userInfo.typeName">{{userInfo.typeName}}</p>
         </div>
         <div class="center-data-box">
-          <div class="progress-max-box" v-if="checkUserInfo.dataMax">
+          <div class="progress-max-box">
             <el-progress
               :stroke-width="12" :format="format"
-              :percentage="checkUserInfo.dataNum">
+              :percentage="userInfo.studyHourRate">
             </el-progress>
             <div class="progress-min-box">
-              <p>{{checkUserInfo.dataText}}</p>
-              <p>{{checkUserInfo.dataMin}} / {{checkUserInfo.dataMax}} 学时</p>
+              <p>当前学年学时完成进度</p>
+              <p>{{userInfo.completedStudyHours}} / {{userInfo.totalStudyHours}} 学时</p>
             </div>
           </div>
           <div class="center-bottom-box">
             <div class="center-bottom-min-box">
-              <p>{{checkUserInfo.dataMax>0?'学时完成度':'学时要求'}}</p>
-              <p>{{checkUserInfo.dataMax>0?checkUserInfo.dataNum+'%':'无'}}</p>
+              <p>{{userInfo.studyHourRate?'学时完成度':'学时要求'}}</p>
+              <p>{{userInfo.studyHourRate?userInfo.studyHourRate+'%':'无'}}</p>
             </div>
             <div class="center-bottom-min-box">
               <p>目标考试</p>
-              <p>{{checkUserInfo.dataTarget}}</p>
+              <p>{{userInfo.targetExamName?userInfo.targetExamName:'无'}}</p>
             </div>
           </div>
         </div>
       </div>
+      <!--任务-->
       <div class="right-max-box">
         <p class="right-title-p">当前学年任务状态</p>
         <div class="right-big-box">
           <p>考试状态</p>
-          <p :class="userInfo.data7==0?'':(userInfo.data7==1?'color-a':(userInfo.data7==2?'color-b':''))">
-            {{userInfo.data7==0?'暂无':(userInfo.data7==1?'已通过':(userInfo.data7==2?'未通过':''))}}
+          <p :class="!userInfo.examPassStatus||userInfo.examPassStatus==1?'':(userInfo.examPassStatus==2?'color-a':(userInfo.examPassStatus==3||userInfo.examPassStatus==4?'color-b':''))">
+            {{!userInfo.examPassStatus?'暂无':(userInfo.examPassStatus==1?'考试中':((userInfo.examPassStatus==2?'已通过':(userInfo.examPassStatus==3?'未通过':(userInfo.examPassStatus==4?'缺考':'')))))}}
           </p>
         </div>
         <div class="right-big-box">
           <p>证书状态</p>
-          <p :class="userInfo.data8==0?'':(userInfo.data8==1?'color-a':(userInfo.data8==2?'color-b':''))">
-            {{userInfo.data8==0?'暂无':(userInfo.data8==1?'已通过':(userInfo.data8==2?'未通过':''))}}
+          <p :class="!userInfo.certificateStatus?'':(userInfo.certificateStatus==1?'color-a':(userInfo.certificateStatus==2||userInfo.certificateStatus==3||userInfo.certificateStatus==4?'color-b':''))">
+            {{!userInfo.certificateStatus?'暂无':(userInfo.certificateStatus==1?'已通过':(userInfo.certificateStatus==2?'失效':(userInfo.certificateStatus==3?'已撤销':(userInfo.certificateStatus==4?'未签发':''))))}}
           </p>
         </div>
-        <div class="right-big-box">
+        <div class="right-big-box" v-if="userInfo.approvalStatus!=3&&userInfo.userType == 2">
           <p>审批状态</p>
-          <p :class="userInfo.data9==0?'':(userInfo.data9==1?'color-a':(userInfo.data9==2?'color-b':''))">
-            {{userInfo.data9==0?'待绑定':(userInfo.data9==1?'已通过':(userInfo.data9==2?'未通过':''))}}
+          <p :class="userInfo.approvalStatus==0?'':(userInfo.approvalStatus==1?'color-a':(userInfo.approvalStatus==2?'color-b':''))">
+            {{userInfo.bound&&userInfo.approvalStatus==0?'待审批':(userInfo.approvalStatus==1?'已通过':(userInfo.approvalStatus==2?'未通过':'待绑定'))}}
           </p>
         </div>
       </div>
     </div>
+    <!--绑定实验室弹窗-->
+    <div class="shade-max-big-box" v-if="shadeType == 1 || shadeType == 2">
+      <div class="shade-big-box" v-if="shadeType == 1">
+        <div class="shade-title-box">
+          <p>绑定实验室</p>
+          <p class="el-icon-close" @click="shadeButton(5)"></p>
+        </div>
+        <div class="shade-content-box">
+          <el-form :model="shadeFormData" class="form-box" ref="shadeForm"
+                   :inline="true" :rules="rules" style="width:100%;">
+            <div>
+              <el-form-item label="" prop="subId" label-width="0">
+                <el-select
+                  style="width:760px;"
+                  v-model="shadeFormData.subId"
+                  filterable
+                  remote
+                  reserve-keyword
+                  @change="subChange"
+                  placeholder="输入实验室名称、房间或楼栋关键词进行搜索"
+                  :remote-method="subSelectList">
+                  <el-option
+                    v-for="item in subOptions"
+                    :key="item.subId"
+                    :label="item.deptName+' - '+item.subName+(item.roomNum?'('+item.roomNum+')':'')+'- '+item.buildName+item.floorName"
+                    :value="item.subId">
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </div>
+            <p class="shade-text-p">此处请选择需要后续进入的科研实验室的名称,输入实验室名称、房间号或楼栋关键词,再从检索结果中选择目标实验室。</p>
+            <!--实验室信息-->
+            <div class="shade-sub-max-box" v-if="shadeFormData.subId&&subData.subName">
+              <div class="shade-sub-big-box">
+                <div class="shade-sub-box">
+                  <p>实验室名称</p>
+                  <p>{{subData.subName}}{{subData.roomNum?'('+subData.roomNum+')':''}}</p>
+                </div>
+                <div class="shade-sub-box-1">
+                  <p>实验室类型</p>
+                  <p>{{subData.moldName}}</p>
+                </div>
+              </div>
+              <div class="shade-sub-big-box">
+                <div class="shade-sub-box">
+                  <p>负责人</p>
+                  <p>{{subData.adminName}}</p>
+                </div>
+                <div class="shade-sub-box-1">
+                  <p>实验室分级</p>
+                  <p :style="'color:'+subData.subData9+';'">{{subData.classLevelName}}</p>
+                </div>
+              </div>
+              <div class="shade-sub-big-box">
+                <div class="shade-sub-box">
+                  <p>所属学院</p>
+                  <p>{{subData.deptName}}</p>
+                </div>
+                <div class="shade-sub-box-1">
+                  <p>实验室分类</p>
+                  <p>{{subData.classTypeNames}}</p>
+                </div>
+              </div>
+              <div class="shade-sub-big-box">
+                <div class="shade-sub-box">
+                  <p>所在位置</p>
+                  <p>{{subData.buildName}}{{subData.floorName}}</p>
+                </div>
+              </div>
+            </div>
+            <!--本科生选项-->
+            <div style="margin-top:20px;" v-if="userInfo.educationType == 2">
+              <el-form-item label="" prop="reason" label-width="0">
+                <el-input
+                  type="textarea"
+                  style="width:760px;"
+                  :autosize="{ minRows: 2, maxRows: 2}"
+                  placeholder="请填写进入实验室原因(必填)"
+                  maxLength="100"
+                  resize="none"
+                  v-model="shadeFormData.reason">
+                </el-input>
+              </el-form-item>
+              <p class="shade-text-p">毕业论文或毕业设计,科创等相关需要在具体的实验室开展业务的情况可申请绑定实验室,绑定完毕后需要完成对应的分级考试。</p>
+            </div>
+            <!--研究生选项-->
+            <div class="checkbox-box" v-if="userInfo.educationType == 1">
+              <el-form-item label="" label-width="0">
+                <el-checkbox v-model="shadeFormData.isEnter">无需进入实验室</el-checkbox>
+                <p class="checkbox-text-p">人文、体育等相关学院无需进入实验室的新研究生可选择此项,选择后学时要求为4小时,需通过应知应会考试。</p>
+              </el-form-item>
+            </div>
+            <div class="shade-check-box">
+              <el-form-item label="" prop="checkType" label-width="0">
+                <el-checkbox v-model="shadeFormData.checkType">我已阅读并同意</el-checkbox>
+                <span style="color:#0183fa;font-size:14px;cursor: pointer"  @click="shadeButton(2)">《新生绑定实验室须知及责任说明》</span>
+              </el-form-item>
+            </div>
+          </el-form>
+        </div>
+        <div class="shade-button-box">
+          <p></p>
+          <p @click="shadeButton(5)">取消</p>
+          <p @click="shadeButton(1)">确认绑定</p>
+        </div>
+      </div>
+      <div class="shade-big-box" v-if="shadeType == 2">
+        <div class="shade-title-box">
+          <p>新生绑定实验室须知及责任说明</p>
+        </div>
+        <div class="shade-content-box-2 scrollbar-box">
+          <p v-html="textDetail"></p>
+        </div>
+        <div class="shade-button-box-2">
+          <p></p>
+          <p></p>
+          <p @click="shadeButton(3)">我已知晓</p>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
+  import {
+    examElLabEduRelationBindStatus,examElLabEduRelationLabLists,examElLabEduRelationBind,
+    examElLabEduRelationLabDetail,laboratoryLabSafeManagerRegulationsDetail,
+  } from "@/api/safetyEducationExaminationNew/index";
 export default {
   name: 'UserInfoCard',
   data() {
+    const validateData5 = (rule, value, callback) => {
+      console.log('value',value)
+      if (!value) {
+        callback(new Error('请阅读并确认勾选《新生绑定实验室须知及责任说明》'))
+      } else {
+        callback()
+      }
+    }
     return {
       textName:'应知应会通识',
       userInfo: {
-        data1:'Name',
-        data2:'20261001',
-        data3:'硕士研究生',
-        data4:'学院Name',
-        data5:'',
-        data6:[
-          {
-            dataName:'2025 - 2026',
-            dataText:'当前学年学时完成进度',
-            dataMin:0,
-            dataMax:0,
-            dataNum:75,
-            dataTarget:'分级考试',
-          },
-          {
-            dataName:'| 级实验室',
-            dataText:'当前学年学时完成进度',
-            dataMin:6,
-            dataMax:10,
-            dataNum:75,
-            dataTarget:'应知应会考试',
-          },
-          {
-            dataName:'| 级实验室',
-            dataText:'当前学年学时完成进度',
-            dataMin:6,
-            dataMax:10,
-            dataNum:75,
-            dataTarget:'应知应会考试',
-          },
-        ],
-        data7:0,
-        data8:1,
-        data9:2,
+        userType:'',
+        userName:'',
+        account:'',
+        educationName:'',
+        deptName:'',
+        labName:'',
+        bound:'',
+        approvalStatus:'',
+        certificateStatus:'',
+        examPassStatus:'',
+        gradeRange:'',
+        typeName:'',
+        completedStudyHours:'',
+        totalStudyHours:'',
+        studyHourRate:'',
+        targetExamName:'',
       },
-      checkUserInfo: {
-        dataName:'2025 - 2026',
-        dataText:'当前学年学时完成进度',
-        dataMin:0,
-        dataMax:0,
-        dataNum:75,
-        dataTarget:'分级考试',
+      shadeFormData:{
+        subId:null,
+        reason:'',
+        isEnter:false,
+        checkType:false,
+      },
+      rules:{
+        subId: [
+          { required: true, message: "请选择实验室", trigger: "blur" },
+          { required: true, message: "请选择实验室", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        reason: [
+          { required: true, message: "请填写进入实验室原因", trigger: "blur" },
+          { required: true, message: "请填写进入实验室原因", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        checkType: [
+          { required: true, message: "请阅读并确认勾选《新生绑定实验室须知及责任说明》", trigger: "blur" },
+          { validator: validateData5, trigger: 'blur' }
+        ],
       },
-      checkUserIndex:0,
+      subData:{},
+      subOptions:[],
+      //弹层
+      shadeType:null,
+      textDetail:'',
     }
   },
   created() {
 
   },
   mounted() {
-
+    this.examElLabEduRelationBindStatus();
+    this.laboratoryLabSafeManagerRegulationsDetail();
   },
   methods: {
-    //切换选项卡
-    checkTitleButton(index){
-      if(this.checkUserIndex != index){
-        this.$set(this,'checkUserIndex',index);
-        this.$set(this,'checkUserInfo',JSON.parse(JSON.stringify(this.userInfo.data6[index])));
-      }
-    },
-    //绑定实验室按钮
-    bindingSubButton(){
-
-    },
     //进度条去除描述
     format(percentage) {
       return '';
-    }
+    },
+    //用户信息卡
+    examElLabEduRelationBindStatus(){
+      examElLabEduRelationBindStatus({}).then(response => {
+        this.$set(this, 'userInfo', response.data)
+        if(response.data.userType==2&&!response.data.bound){
+          this.shadeButton(4);
+        }
+      })
+    },
+    //实验室管理规定查询
+    laboratoryLabSafeManagerRegulationsDetail(){
+      laboratoryLabSafeManagerRegulationsDetail({}).then(response => {
+        this.$set(this, 'textDetail', unescape(response.data))
+      })
+    },
+    //实验室懒加载
+    subSelectList(query) {
+      if (query !== '' && query.length>1) {
+        examElLabEduRelationLabLists({searchValue:query}).then(response => {
+          this.subOptions = response.data;
+        });
+      } else {
+        this.subOptions = [];
+      }
+    },
+    //关闭弹层
+    shadeButton(type){
+      let self = this;
+      if(type == 1){
+        //提交
+        this.$refs["shadeForm"].validate(valid => {
+          if (valid) {
+            this.$confirm('确认提交?', "警告", {
+              confirmButtonText: "确认",
+              cancelButtonText: "取消",
+              type: "warning"
+            }).then(function() {
+            }).then(() => {
+              self.examElLabEduRelationBind();
+            }).catch(() => {});
+          }
+        })
+      }else if(type == 2){
+        //查看通知
+        this.$set(this,'shadeType',2);
+      }else if(type == 3){
+        //关闭通知
+        this.$set(this.shadeFormData,'checkType',true);
+        this.$set(this,'shadeType',1);
+      }else if(type == 4){
+        //开启
+        this.$set(this,'shadeFormData',{
+          subId:null,
+          reason:'',
+          isEnter:false,
+          checkType:false,
+        });
+        this.$set(this,'shadeType',1);
+      }else if(type == 5){
+        //关闭
+        this.$set(this,'shadeType',null);
+      }
+    },
+    //选中实验室
+    subChange(val){
+      const selectedItem = this.subOptions.find(option => option.subId === val);
+      this.$set(this,'subData',selectedItem);
+    },
+    examElLabEduRelationBind(){
+      let obj = {
+        subId:this.shadeFormData.subId,
+        reason:this.userInfo.educationType == 2?this.shadeFormData.reason:'',
+        isEnter:this.userInfo.educationType == 1?(this.shadeFormData.isEnter?'1':'0'):'',
+      };
+      examElLabEduRelationBind(obj).then(response => {
+        this.msgSuccess(response.message);
+        this.$set(this,'shadeType',null);
+        this.examElLabEduRelationBindStatus();
+      });
+    },
   },
 }
 </script>
@@ -285,6 +489,12 @@ export default {
             p{
               line-height: 30px;
             }
+            p:nth-child(1){
+              font-size:14px;
+            }
+            p:nth-child(2){
+              font-size:16px;
+            }
           }
         }
       }
@@ -320,6 +530,7 @@ export default {
           line-height:30px;
           margin:5px 20px 0 0;
           text-align: center;
+          font-size:14px;
           border-radius:15px;
           background-color: #EBF0F5;
         }
@@ -334,5 +545,189 @@ export default {
       }
     }
   }
+  .shade-max-big-box{
+    z-index:100;
+    position: fixed;
+    top:0;
+    left:0;
+    width:100%;
+    height:100%;
+    background-color: rgba(0,0,0,0.4);
+    display: flex;             /* 开启 Flex 布局 */
+    justify-content: center;   /* 水平居中 */
+    align-items: center;       /* 垂直居中 */
+    .shade-big-box{
+      width:800px;
+      min-height:400px;
+      border-radius:6px;
+      background-color: #fff;
+      position: relative;
+      .shade-title-box{
+        display: flex;
+        p{
+          line-height: 60px;
+        }
+        p:nth-child(1){
+          flex:1;
+          padding:0 20px;
+        }
+        p:nth-child(2){
+          font-size:24px;
+          color:#666;
+          padding:0 20px;
+          cursor: pointer;
+        }
+        p:nth-child(2):hover{
+          color: red;
+        }
+      }
+      .shade-content-box{
+        padding:20px;
+        .shade-text-p{
+          color:#9BA7BA;
+          font-size:14px;
+          line-height:20px;
+        }
+        .checkbox-box{
+          margin-top:20px;
+          padding:10px 15px;
+          border-radius:6px;
+          background-color:#F8F9FC ;
+          .checkbox-text-p{
+            color:#9BA7BA;
+          }
+        }
+        .shade-check-box{
+          margin-top:15px;
+        }
+        .shade-sub-max-box{
+          margin:20px 0 0;
+          .shade-sub-big-box:nth-child(1){
+            border-top:1px solid #EDEFF5;
+          }
+          .shade-sub-big-box{
+            display: flex;
+            border-bottom:1px solid #EDEFF5;
+            border-right:1px solid #EDEFF5;
+          }
+          .shade-sub-box-1{
+            flex:2;
+            display: flex;
+            p{
+              line-height:40px;
+              font-size:14px;
+            }
+            p:nth-child(1){
+              background-color: #FAFAFA;
+              width:110px;
+              padding:0 15px;
+              border-left:1px solid #EDEFF5;
+              border-right:1px solid #EDEFF5;
+            }
+            p:nth-child(2){
+              flex:1;
+              padding:0 15px;
+              background-color: #fff;
+              /*单行省略号*/
+              display:block;
+              overflow:hidden;
+              text-overflow:ellipsis;
+              white-space:nowrap;
+            }
+          }
+          .shade-sub-box{
+            flex:3;
+            display: flex;
+            p{
+              line-height:40px;
+              font-size:14px;
+            }
+            p:nth-child(1){
+              background-color: #FAFAFA;
+              width:110px;
+              padding:0 15px;
+              border-left:1px solid #EDEFF5;
+              border-right:1px solid #EDEFF5;
+            }
+            p:nth-child(2){
+              flex:1;
+              padding:0 15px;
+              background-color: #fff;
+              /*单行省略号*/
+              display:block;
+              overflow:hidden;
+              text-overflow:ellipsis;
+              white-space:nowrap;
+            }
+          }
+        }
+      }
+      .shade-content-box-2{
+        padding: 0 40px;
+        margin: 20px 0;
+        height: 400px;
+      }
+      .shade-button-box{
+        display: flex;
+        padding:20px;
+        p:nth-child(1){
+          flex:1;
+        }
+        p:nth-child(2){
+          width:70px;
+          line-height:40px;
+          border-radius:4px;
+          text-align: center;
+          border:1px solid #dedede;
+          margin-right:20px;
+          cursor: pointer;
+        }
+        p:nth-child(2):hover{
+          border:1px solid #999;
+          background-color: #999;
+          color:#fff;
+        }
+        p:nth-child(3){
+          background-color: #A0CFFE;
+          color:#fff;
+          width:100px;
+          line-height:40px;
+          border-radius:4px;
+          text-align: center;
+          border:1px solid #A0CFFE;
+          cursor: pointer;
+        }
+        p:nth-child(3):hover{
+          border:1px solid #0183fa;
+          background-color: #0183fa;
+        }
+      }
+      .shade-button-box-2{
+        display: flex;
+        padding:20px;
+        p:nth-child(1){
+          flex:1;
+        }
+        p:nth-child(2){
+
+        }
+        p:nth-child(3){
+          background-color: #A0CFFE;
+          color:#fff;
+          width:100px;
+          line-height:40px;
+          border-radius:4px;
+          text-align: center;
+          border:1px solid #A0CFFE;
+          cursor: pointer;
+        }
+        p:nth-child(3):hover{
+          border:1px solid #0183fa;
+          background-color: #0183fa;
+        }
+      }
+
+    }
+  }
 }
 </style>

+ 13 - 8
src/views/safetyEducationExaminationNew/workbench/index.vue

@@ -3,19 +3,19 @@
   <div class="app-container workbench">
     <div class="page-container workbenchPage scrollbar-box" v-if="pageType === 1">
       <!-- 用户信息卡片 -->
-      <user-info-card />
+      <user-info-card/>
       <!-- 工作台任务卡片 -->
-      <task-card />
+      <task-card/>
       <!-- 实验室安全素养资源卡 -->
-      <resource-card />
+      <resource-card/>
       <!-- 实验室绑定申请 -->
-      <lab-binding-apply />
+      <lab-binding-apply/>
       <!-- 考试情况统计 -->
-      <exam-situation />
+      <exam-situation/>
       <!-- 实验室与人员匹配 -->
-      <lab-person-match />
+      <lab-person-match/>
       <!-- 学时统计 -->
-      <study-hour-stats />
+      <study-hour-stats/>
     </div>
   </div>
 </template>
@@ -59,11 +59,16 @@
 
 <style scoped lang="scss">
   .workbench {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
     .workbenchPage {
       padding: 16px;
       background: #f5f7fa;
-      min-height: 100vh;
       overflow-y: scroll !important;
+      display: flex;
+      flex-direction: column;
     }
   }
 </style>